Skip to content

Commit

Permalink
Merge pull request #278 from RailsEventStore/protobuf_v2_docs
Browse files Browse the repository at this point in the history
Protobuf v2 documentation changes
  • Loading branch information
mostlyobvious authored May 1, 2018
2 parents 20066a0 + 1699efb commit 3d8e8f4
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 53 deletions.
28 changes: 16 additions & 12 deletions railseventstore.org/source/docs/mapping_serialization.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ Mapper needs to implement 3 methods:
* `metadata` (String)
* `event_type` (String)
* `serialized_record_to_event(record)` - which takes `RubyEventStore::SerializedRecord` and converts it to an instance of an event class that was stored.
* `add_metadata(domain_event, key, value)` - which knows how to (if possible) add automatically some metadata such as `request_ip`, `request_id` or `timestamp` to events.

```ruby
require 'msgpack'
Expand All @@ -73,25 +72,26 @@ class MyHashToMessagePackMapper
# of your domain event
SerializedRecord.new(
event_id: domain_event.fetch('event_id'),
metadata: "",
data: domain_event.to_msg_pack,
event_type: domain_event.fetch('event_type')
metadata: domain_event.metadata.to_msg_pack,
data: domain_event.data.to_msg_pack,
event_type: domain_event.type
)
end

# Deserialize proper object based on
# event_type and data+metadata fields
def serialized_record_to_event(record)
MessagePack.unpack(record.data)
Object.const_get(event_type).new(
event_id: record.event_id,
metadata: MessagePack.unpack(record.metadata),
data: MessagePack.unpack(record.data)
)
end

def add_metadata(event, key, value)
event[key.to_s] = value
end
end
```

Check out the code of our [default mapper](https://github.com/RailsEventStore/rails_event_store/blob/52d5104a8f47dab7f71c555d0185b58bc9c71c5a/ruby_event_store/lib/ruby_event_store/mappers/default.rb) and [protobuf mapper](https://github.com/RailsEventStore/rails_event_store/blob/52d5104a8f47dab7f71c555d0185b58bc9c71c5a/ruby_event_store/lib/ruby_event_store/mappers/protobuf.rb) on github for examples on how to implement mappers.
Check out the code of our [default mapper](https://github.com/RailsEventStore/rails_event_store/blob/master/ruby_event_store/lib/ruby_event_store/mappers/default.rb) and [protobuf mapper](https://github.com/RailsEventStore/rails_event_store/blob/master/ruby_event_store/lib/ruby_event_store/mappers/protobuf.rb) on github for examples on how to implement mappers.


You can pass a different `mapper` as a dependency when [instantiating the client](/docs/install).
Expand All @@ -113,10 +113,14 @@ end
Now you should be able to publish your events:

```ruby
Rails.configuration.event_store.publish_event({
class OrderPlaced < RubyEventStore::Event
end

event_store = Rails.configuration.event_store

event_store.publish_event(OrderPlaced.new(data: {
'event_id' => SecureRandom.uuid,
'order_id' => 1,
'event_type' => 'OrderPlaced',
'order_amount' => BigDecimal.new('120.55'),
}, stream_name: 'Order$1')
}), stream_name: 'Order$1')
```
76 changes: 39 additions & 37 deletions railseventstore.org/source/docs/protobuf.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,15 @@ gem 'protobuf_nested_struct'
gem 'rails_event_store'
```

This edge feature is not yet available in a released gem version.
## Migration

Change `data` and `metadata` columns' type to `binary` to allow storing
non-UTF8 characters which might appear when using protobuf serialization.

```ruby
change_column :event_store_events, :data, :binary, null: true
change_column :event_store_events, :metadata, :binary, null: true
```

## Configure protobuf mapper

Expand All @@ -37,9 +45,8 @@ syntax = "proto3";
package my_app;
message OrderPlaced {
string event_id = 1;
string order_id = 2;
int32 customer_id = 3;
string order_id = 1;
int32 customer_id = 2;
}
```

Expand All @@ -53,50 +60,26 @@ require 'google/protobuf'

Google::Protobuf::DescriptorPool.generated_pool.build do
add_message "my_app.OrderPlaced" do
optional :event_id, :string, 1
optional :order_id, :string, 2
optional :customer_id, :int32, 3
optional :order_id, :string, 1
optional :customer_id, :int32, 2
end
end

module MyApp
OrderPlaced = Google::Protobuf::DescriptorPool.generated_pool.lookup("my_app.OrderPlaced").msgclass
end

```

### Metadata

Rails Event Store can automatically fill out some meta-data for your events such as:

* `timestamp`
* `remote_ip`
* `request_id`

If you want to include them, define those fields in your event schema definition as well.

```
syntax = "proto3";
package my_app;
message OrderPlaced {
string event_id = 1;
string order_id = 2;
int32 customer_id = 3;
string remote_ip = 4;
}
```

## Publishing

```ruby
event_store = Rails.configuration.event_store

event = MyApp::OrderPlaced.new(
event_id: "f90b8848-e478-47fe-9b4a-9f2a1d53622b",
customer_id: 123,
order_id: "K3THNX9",
event = RubyEventStore::Proto.new(
data: MyApp::OrderPlaced.new(
order_id: "K3THNX9",
customer_id: 123,
)
)
event_store.publish_event(event, stream_name: "Order-K3THNX9")
```
Expand All @@ -107,6 +90,25 @@ event_store.publish_event(event, stream_name: "Order-K3THNX9")
event = client.read_stream_events_forward('test').last
```

## Async handlers
## Subscribing

#### Sync handlers

```ruby
event_store.subscribe(->(ev){ }, to: [MyApp::OrderPlaced.descriptor.name])
````

#### Async handlers

```ruby
class SendOrderEmailHandler < ActiveJob::Base
self.queue_adapter = :inline
def perform(event)
event = YAML.load(event)
# do something
end
end
* BUG: [Protobuf cannot be used with async handlers](https://github.com/RailsEventStore/rails_event_store/issues/228)
event_store.subscribe(SendOrderEmailHandler, to: [MyApp::OrderPlaced.descriptor.name])
```
20 changes: 16 additions & 4 deletions railseventstore.org/source/docs/repository.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,28 @@ RubyEventStore comes with `RubyEventStore::InMemoryRepository` that you can use
RSpec.configure do |c|
c.around(:each)
Rails.configuration.event_store = RailsEventStore::Client.new(
repository: RubyEventStore::InMemoryRepository.new()
repository: RubyEventStore::InMemoryRepository.new
)
# add subscribers here
end
end
```

We don't recommend using `InMemoryRepository` in production even if you don't need to persist events because the repository keeps all published events in memory. This is acceptable in testing because you can throw the instance away for every test and garbage collector reclaims the memory. In production, your memory would keep growing until you restart the application server.
If you want even faster tests you can additionally skip event's serialization.

```ruby
RSpec.configure do |c|
c.around(:each)
Rails.configuration.event_store = RailsEventStore::Client.new(
repository: RubyEventStore::InMemoryRepository.new,
mapper: RubyEventStore::Mappers::NullMapper,
)
# add subscribers here
end
end
```

`InMemoryRepository` can take custom `mapper:` as an argument just like `RailsEventStoreActiveRecord::EventRepository`. [Read more on that](/docs/mapping_serialization/)
We don't recommend using `InMemoryRepository` in production even if you don't need to persist events because the repository keeps all published events in memory. This is acceptable in testing because you can throw the instance away for every test and garbage collector reclaims the memory. In production, your memory would keep growing until you restart the application server.

# Using Ruby Object Mapper (ROM) for SQL without ActiveRecord or Rails

Expand Down Expand Up @@ -122,4 +134,4 @@ bundle exec rake db:copy_migrations
bundle exec rake db:migrate
```

You can run `bundle exec rake -T` to get a list of all available tasks. You can also programmatically run migrations (see examples above).
You can run `bundle exec rake -T` to get a list of all available tasks. You can also programmatically run migrations (see examples above).

0 comments on commit 3d8e8f4

Please sign in to comment.