diff --git a/aggregate_root/lib/aggregate_root.rb b/aggregate_root/lib/aggregate_root.rb index 87a1bd3bde..8ac8282ff5 100644 --- a/aggregate_root/lib/aggregate_root.rb +++ b/aggregate_root/lib/aggregate_root.rb @@ -2,7 +2,6 @@ require_relative "aggregate_root/version" require_relative "aggregate_root/configuration" -require_relative "aggregate_root/transform" require_relative "aggregate_root/default_apply_strategy" require_relative "aggregate_root/repository" require_relative "aggregate_root/instrumented_repository" @@ -81,7 +80,6 @@ def marshal_load(vars) def self.with_default_apply_strategy Module.new do def self.included(host_class) - host_class.extend OnDSL host_class.include AggregateRoot.with_strategy(-> { DefaultApplyStrategy.new }) end end @@ -90,7 +88,8 @@ def self.included(host_class) def self.with_strategy(strategy) Module.new do def self.included(host_class) - host_class.extend Constructor + host_class.extend OnDSL + host_class.extend Constructor host_class.include AggregateMethods end diff --git a/aggregate_root/lib/aggregate_root/configuration.rb b/aggregate_root/lib/aggregate_root/configuration.rb deleted file mode 100644 index 6935658048..0000000000 --- a/aggregate_root/lib/aggregate_root/configuration.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -module AggregateRoot - class << self - attr_accessor :configuration - end - - def self.configure - self.configuration ||= Configuration.new - yield(configuration) - end - - class Configuration - attr_accessor :default_event_store - end -end diff --git a/aggregate_root/lib/aggregate_root/default_apply_strategy.rb b/aggregate_root/lib/aggregate_root/default_apply_strategy.rb index 3046aa2aeb..ff3ffa147c 100644 --- a/aggregate_root/lib/aggregate_root/default_apply_strategy.rb +++ b/aggregate_root/lib/aggregate_root/default_apply_strategy.rb @@ -2,6 +2,7 @@ module AggregateRoot MissingHandler = Class.new(StandardError) + NullHandler = Proc.new {} class DefaultApplyStrategy def initialize(strict: true) @@ -9,30 +10,24 @@ def initialize(strict: true) end def call(aggregate, event) - name = handler_name(aggregate, event) - if aggregate.respond_to?(name, true) - aggregate.method(name).call(event) - else - raise MissingHandler.new("Missing handler method #{name} on aggregate #{aggregate.class}") if strict - end + on_handler(aggregate, event.event_type)[event] end private - def handler_name(aggregate, event) - on_dsl_handler_name(aggregate, event.event_type) || apply_handler_name(event.event_type) - end - - def on_dsl_handler_name(aggregate, event_type) - aggregate.class.on_methods[event_type] if aggregate.class.respond_to?(:on_methods) + def on_handler(aggregate, event_type) + on_method_name = aggregate.class.on_methods.fetch(event_type) + aggregate.method(on_method_name) + rescue KeyError + missing_handler(aggregate, event_type) end - def apply_handler_name(event_type) - "apply_#{Transform.to_snake_case(event_type(event_type))}" - end - - def event_type(event_type) - event_type.split(/::|\./).last + def missing_handler(aggregate, event_type) + if strict + raise MissingHandler.new("Missing handler method on aggregate #{aggregate.class} for #{event_type}") + else + NullHandler + end end attr_reader :strict, :on_methods diff --git a/aggregate_root/lib/aggregate_root/repository.rb b/aggregate_root/lib/aggregate_root/repository.rb index ad73120d43..09e2ea9575 100644 --- a/aggregate_root/lib/aggregate_root/repository.rb +++ b/aggregate_root/lib/aggregate_root/repository.rb @@ -2,7 +2,7 @@ module AggregateRoot class Repository - def initialize(event_store = default_event_store) + def initialize(event_store) @event_store = event_store end @@ -29,9 +29,5 @@ def with_aggregate(aggregate, stream_name, &block) private attr_reader :event_store - - def default_event_store - AggregateRoot.configuration.default_event_store - end end end diff --git a/aggregate_root/lib/aggregate_root/transform.rb b/aggregate_root/lib/aggregate_root/transform.rb deleted file mode 100644 index 39643b1e2e..0000000000 --- a/aggregate_root/lib/aggregate_root/transform.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -module AggregateRoot - class Transform - def self.to_snake_case(name) - name.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase - end - end -end diff --git a/aggregate_root/spec/aggregate_root_spec.rb b/aggregate_root/spec/aggregate_root_spec.rb index 78f8fba56d..3ccdec835f 100644 --- a/aggregate_root/spec/aggregate_root_spec.rb +++ b/aggregate_root/spec/aggregate_root_spec.rb @@ -31,12 +31,14 @@ def expire private - def apply_order_created(_event) - @status = :created + on Orders::Events::OrderCreated do |event| + @status = :created + @created_at = event.valid_at end - def apply_order_expired(_event) - @status = :expired + on Orders::Events::OrderExpired do |event| + @status = :expired + @expired_at = event.valid_at end end end @@ -44,9 +46,8 @@ def apply_order_expired(_event) it "should have ability to apply event on itself" do order = order_klass.new(uuid) order_created = Orders::Events::OrderCreated.new - - expect(order).to receive(:"apply_order_created").with(order_created).and_call_original order.apply(order_created) + expect(order.status).to eq :created expect(order.unpublished_events.to_a).to eq([order_created]) end @@ -56,6 +57,12 @@ def apply_order_expired(_event) expect(order.unpublished_events.to_a).to be_empty end + it "should raise error for missing apply method based on a default apply strategy" do + order = order_klass.new(uuid) + spanish_inquisition = Orders::Events::SpanishInquisition.new + expect { order.apply(spanish_inquisition) }.to raise_error(AggregateRoot::MissingHandler, "Missing handler method on aggregate #{order_klass} for Orders::Events::SpanishInquisition") + end + it "should receive a method call based on a default apply strategy" do order = order_klass.new(uuid) order_created = Orders::Events::OrderCreated.new @@ -64,15 +71,6 @@ def apply_order_expired(_event) expect(order.status).to eq :created end - it "should raise error for missing apply method based on a default apply strategy" do - order = order_klass.new(uuid) - spanish_inquisition = Orders::Events::SpanishInquisition.new - expect { order.apply(spanish_inquisition) }.to raise_error( - AggregateRoot::MissingHandler, - "Missing handler method apply_spanish_inquisition on aggregate #{order_klass}" - ) - end - it "should ignore missing apply method based on a default non-strict apply strategy" do klass = Class.new { include AggregateRoot.with_strategy(-> { AggregateRoot::DefaultApplyStrategy.new(strict: false) }) } diff --git a/aggregate_root/spec/instrumented_repository_spec.rb b/aggregate_root/spec/instrumented_repository_spec.rb index 344723482f..2a212d89fb 100644 --- a/aggregate_root/spec/instrumented_repository_spec.rb +++ b/aggregate_root/spec/instrumented_repository_spec.rb @@ -27,11 +27,11 @@ def expire private - def apply_order_created(_event) + on Orders::Events::OrderCreated do |_event| @status = :created end - def apply_order_expired(_event) + on Orders::Events::OrderExpired do |_event| @status = :expired end end diff --git a/aggregate_root/spec/repository_spec.rb b/aggregate_root/spec/repository_spec.rb index e2ec51d7a8..c8ca929c5c 100644 --- a/aggregate_root/spec/repository_spec.rb +++ b/aggregate_root/spec/repository_spec.rb @@ -34,51 +34,16 @@ def expire private - def apply_order_created(_event) + on Orders::Events::OrderCreated do |_event| @status = :created end - def apply_order_expired(_event) + on Orders::Events::OrderExpired do |_event| @status = :expired end end end - def with_default_event_store(store) - previous = AggregateRoot.configuration.default_event_store - AggregateRoot.configure { |config| config.default_event_store = store } - yield - AggregateRoot.configure { |config| config.default_event_store = previous } - end - - describe "#initialize" do - it "should use default client if event_store not provided" do - with_default_event_store(event_store) do - repository = AggregateRoot::Repository.new - - order = repository.load(order_klass.new(uuid), stream_name) - order_created = Orders::Events::OrderCreated.new - order.apply(order_created) - repository.store(order, stream_name) - - expect(event_store.read.stream(stream_name).to_a).to eq [order_created] - end - end - - it "should prefer provided event_store client" do - with_default_event_store(double(:event_store)) do - repository = AggregateRoot::Repository.new(event_store) - - order = repository.load(order_klass.new(uuid), stream_name) - order_created = Orders::Events::OrderCreated.new - order.apply(order_created) - repository.store(order, stream_name) - - expect(event_store.read.stream(stream_name).to_a).to eq [order_created] - end - end - end - describe "#load" do specify do event_store.publish(Orders::Events::OrderCreated.new, stream_name: stream_name) diff --git a/aggregate_root/spec/spec_helper.rb b/aggregate_root/spec/spec_helper.rb index 70dab3ce72..22e40dfcf1 100644 --- a/aggregate_root/spec/spec_helper.rb +++ b/aggregate_root/spec/spec_helper.rb @@ -4,8 +4,6 @@ require "ruby_event_store" require_relative "../../support/helpers/rspec_defaults" -RSpec.configure { |spec| spec.before(:each) { AggregateRoot.configure { |config| config.default_event_store = nil } } } - module Orders module Events OrderCreated = Class.new(RubyEventStore::Event) diff --git a/aggregate_root/spec/transform_spec.rb b/aggregate_root/spec/transform_spec.rb deleted file mode 100644 index 61ce216c4a..0000000000 --- a/aggregate_root/spec/transform_spec.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true - -require "spec_helper" - -module AggregateRoot - ::RSpec.describe Transform do - specify { expect(Transform.to_snake_case("OrderSubmitted")).to eq("order_submitted") } - specify { expect(Transform.to_snake_case("SHA1ChecksumComputed")).to eq("sha1_checksum_computed") } - specify { expect(Transform.to_snake_case("OKROfPSAInQ1Reached")).to eq("okr_of_psa_in_q1_reached") } - specify { expect(Transform.to_snake_case("EncryptedWithRot13")).to eq("encrypted_with_rot13") } - end -end diff --git a/rails_event_store/lib/rails_event_store/all.rb b/rails_event_store/lib/rails_event_store/all.rb index b366ee4b29..255dde5995 100644 --- a/rails_event_store/lib/rails_event_store/all.rb +++ b/rails_event_store/lib/rails_event_store/all.rb @@ -10,23 +10,3 @@ require_relative "version" require_relative "railtie" require_relative "browser" - -module RailsEventStore - Event = RubyEventStore::Event - InMemoryRepository = RubyEventStore::InMemoryRepository - Subscriptions = RubyEventStore::Subscriptions - Projection = RubyEventStore::Projection - WrongExpectedEventVersion = RubyEventStore::WrongExpectedEventVersion - InvalidExpectedVersion = RubyEventStore::InvalidExpectedVersion - IncorrectStreamData = RubyEventStore::IncorrectStreamData - EventNotFound = RubyEventStore::EventNotFound - SubscriberNotExist = RubyEventStore::SubscriberNotExist - InvalidHandler = RubyEventStore::InvalidHandler - InvalidPageStart = RubyEventStore::InvalidPageStart - InvalidPageStop = RubyEventStore::InvalidPageStop - InvalidPageSize = RubyEventStore::InvalidPageSize - CorrelatedCommands = RubyEventStore::CorrelatedCommands - GLOBAL_STREAM = RubyEventStore::GLOBAL_STREAM - PAGE_SIZE = RubyEventStore::PAGE_SIZE - ImmediateAsyncDispatcher = RubyEventStore::ImmediateAsyncDispatcher -end diff --git a/rails_event_store/spec/active_job_scheduler_spec.rb b/rails_event_store/spec/active_job_scheduler_spec.rb index 905543fcf6..6e7a11d1e1 100644 --- a/rails_event_store/spec/active_job_scheduler_spec.rb +++ b/rails_event_store/spec/active_job_scheduler_spec.rb @@ -23,9 +23,7 @@ module RailsEventStore it_behaves_like :scheduler, ActiveJobScheduler.new(serializer: RubyEventStore::Serializers::YAML) it_behaves_like :scheduler, ActiveJobScheduler.new(serializer: RubyEventStore::NULL) - let(:event) do - TimeEnrichment.with(Event.new(event_id: "83c3187f-84f6-4da7-8206-73af5aca7cc8"), timestamp: Time.utc(2019, 9, 30)) - end + let(:event) { TimeEnrichment.with(RubyEventStore::Event.new(event_id: "83c3187f-84f6-4da7-8206-73af5aca7cc8"), timestamp: Time.utc(2019, 9, 30)) } let(:record) { RubyEventStore::Mappers::Default.new.event_to_record(event) } describe "#verify" do diff --git a/rails_event_store/spec/after_commit_async_dispatcher_spec.rb b/rails_event_store/spec/after_commit_async_dispatcher_spec.rb index f2d3871ab1..59e799e574 100644 --- a/rails_event_store/spec/after_commit_async_dispatcher_spec.rb +++ b/rails_event_store/spec/after_commit_async_dispatcher_spec.rb @@ -12,7 +12,7 @@ class DummyRecord < ActiveRecord::Base it_behaves_like :dispatcher, AfterCommitAsyncDispatcher.new(scheduler: ActiveJobScheduler.new(serializer: RubyEventStore::Serializers::YAML)) - let(:event) { TimeEnrichment.with(RailsEventStore::Event.new(event_id: "83c3187f-84f6-4da7-8206-73af5aca7cc8")) } + let(:event) { TimeEnrichment.with(RubyEventStore::Event.new(event_id: "83c3187f-84f6-4da7-8206-73af5aca7cc8")) } let(:record) { RubyEventStore::Mappers::Default.new.event_to_record(event) } let(:serialized_record) { record.serialize(RubyEventStore::Serializers::YAML).to_h.transform_keys(&:to_s) } diff --git a/rails_event_store/spec/async_handler_helpers_spec.rb b/rails_event_store/spec/async_handler_helpers_spec.rb index 271bd847cc..b407d25dd8 100644 --- a/rails_event_store/spec/async_handler_helpers_spec.rb +++ b/rails_event_store/spec/async_handler_helpers_spec.rb @@ -78,14 +78,14 @@ def perform(_event) specify "with defaults" do HandlerWithDefaults.prepend RailsEventStore::AsyncHandler event_store.subscribe_to_all_events(HandlerWithDefaults) - event_store.publish(ev = RailsEventStore::Event.new) + event_store.publish(ev = RubyEventStore::Event.new) expect($queue.pop).to eq(ev) end specify "with specified event store" do HandlerWithAnotherEventStore.prepend RailsEventStore::AsyncHandler.with(event_store: another_event_store) event_store.subscribe_to_all_events(HandlerWithAnotherEventStore) - event_store.publish(ev = RailsEventStore::Event.new) + event_store.publish(ev = RubyEventStore::Event.new) expect($queue.pop).to eq(ev) end @@ -95,7 +95,7 @@ def perform(_event) event_store_locator: -> { another_event_store } ) another_event_store.subscribe_to_all_events(HandlerWithEventStoreLocator) - another_event_store.publish(ev = RailsEventStore::Event.new) + another_event_store.publish(ev = RubyEventStore::Event.new) expect($queue.pop).to eq(ev) end @@ -105,20 +105,20 @@ def perform(_event) serializer: JSON ) json_event_store.subscribe_to_all_events(HandlerWithSpecifiedSerializer) - json_event_store.publish(ev = RailsEventStore::Event.new) + json_event_store.publish(ev = RubyEventStore::Event.new) expect($queue.pop).to eq(ev) end specify "default dispatcher can into ActiveJob" do event_store.subscribe_to_all_events(MyLovelyAsyncHandler) - event_store.publish(ev = RailsEventStore::Event.new) + event_store.publish(ev = RubyEventStore::Event.new) expect($queue.pop).to eq(ev) end specify "ActiveJob with AsyncHandler prepended" do HandlerWithHelper.prepend RailsEventStore::AsyncHandler event_store.subscribe_to_all_events(HandlerWithHelper) - event_store.publish(ev = RailsEventStore::Event.new) + event_store.publish(ev = RubyEventStore::Event.new) expect($queue.pop).to eq(ev) end @@ -127,7 +127,7 @@ def perform(_event) HandlerA.prepend RailsEventStore::CorrelatedHandler HandlerA.prepend RailsEventStore::AsyncHandler event_store.subscribe_to_all_events(HandlerA) - event_store.publish(ev = RailsEventStore::Event.new) + event_store.publish(ev = RubyEventStore::Event.new) expect($queue.pop).to eq({ correlation_id: ev.correlation_id, causation_id: ev.event_id }) end @@ -136,13 +136,13 @@ def perform(_event) HandlerB.prepend RailsEventStore::CorrelatedHandler HandlerB.prepend RailsEventStore::AsyncHandler event_store.subscribe_to_all_events(HandlerB) - event_store.publish(ev = RailsEventStore::Event.new(metadata: { correlation_id: "COID", causation_id: "CAID" })) + event_store.publish(ev = RubyEventStore::Event.new(metadata: { correlation_id: "COID", causation_id: "CAID" })) expect($queue.pop).to eq({ correlation_id: "COID", causation_id: ev.event_id }) end specify "ActiveJob with sidekiq adapter that requires serialization", mutant: false do ActiveJob::Base.queue_adapter = :sidekiq - ev = RailsEventStore::Event.new + ev = RubyEventStore::Event.new Sidekiq::Testing.fake! do event_store.subscribe_to_all_events(MyLovelyAsyncHandler) event_store.publish(ev) @@ -155,7 +155,8 @@ def perform(_event) HandlerB = Class.new(MetadataHandler) HandlerB.prepend RailsEventStore::CorrelatedHandler HandlerB.prepend RailsEventStore::AsyncHandler - event_store.append(ev = RailsEventStore::Event.new) + HandlerB.metadata = nil + event_store.append(ev = RubyEventStore::Event.new) ev = event_store.read.event(ev.event_id) HandlerB.perform_now(serialize_without_correlation_id(ev)) expect($queue.pop).to eq({ correlation_id: nil, causation_id: ev.event_id }) diff --git a/rails_event_store/spec/client_spec.rb b/rails_event_store/spec/client_spec.rb index 9dde8727d4..e53e8fc0b2 100644 --- a/rails_event_store/spec/client_spec.rb +++ b/rails_event_store/spec/client_spec.rb @@ -3,7 +3,7 @@ module RailsEventStore ::RSpec.describe Client do - TestEvent = Class.new(RailsEventStore::Event) + TestEvent = Class.new(RubyEventStore::Event) specify "has default request metadata proc if no custom one provided" do client = Client.new @@ -20,7 +20,9 @@ module RailsEventStore end specify "published event metadata will be enriched by metadata provided in request metadata when executed inside a with_request_metadata block" do - client = Client.new(repository: InMemoryRepository.new) + client = Client.new( + repository: RubyEventStore::InMemoryRepository.new, + ) event = TestEvent.new client.with_request_metadata( "action_dispatch.request_id" => "dummy_id", @@ -34,7 +36,7 @@ module RailsEventStore end specify "wraps repository into instrumentation" do - client = Client.new(repository: InMemoryRepository.new) + client = Client.new(repository: RubyEventStore::InMemoryRepository.new) received_notifications = 0 ActiveSupport::Notifications.subscribe("append_to_stream.repository.rails_event_store") do @@ -47,7 +49,10 @@ module RailsEventStore end specify "wraps mapper into instrumentation" do - client = Client.new(repository: InMemoryRepository.new, mapper: RubyEventStore::Mappers::NullMapper.new) + client = Client.new( + repository: RubyEventStore::InMemoryRepository.new, + mapper: RubyEventStore::Mappers::NullMapper.new + ) received_notifications = 0 ActiveSupport::Notifications.subscribe("serialize.mapper.rails_event_store") { received_notifications += 1 } diff --git a/rails_event_store/spec/example_invoicing_app.rb b/rails_event_store/spec/example_invoicing_app.rb index 3d35acf01d..2fb59b6a66 100644 --- a/rails_event_store/spec/example_invoicing_app.rb +++ b/rails_event_store/spec/example_invoicing_app.rb @@ -1,10 +1,10 @@ -class OrderCreated < RailsEventStore::Event +class OrderCreated < RubyEventStore::Event end -class ProductAdded < RailsEventStore::Event +class ProductAdded < RubyEventStore::Event end -class PriceChanged < RailsEventStore::Event +class PriceChanged < RubyEventStore::Event end class InvoiceReadModel diff --git a/rails_event_store/spec/spec_helper.rb b/rails_event_store/spec/spec_helper.rb index 506284e86e..105601e1e7 100644 --- a/rails_event_store/spec/spec_helper.rb +++ b/rails_event_store/spec/spec_helper.rb @@ -36,7 +36,7 @@ ActiveJob::Base.logger = nil unless $verbose ActiveRecord::Schema.verbose = $verbose -DummyEvent = Class.new(RailsEventStore::Event) +DummyEvent = Class.new(RubyEventStore::Event) module GeneratorHelper def dummy_app_name diff --git a/railseventstore.org/config.rb b/railseventstore.org/config.rb index 0af2eb298c..d3abe211ee 100644 --- a/railseventstore.org/config.rb +++ b/railseventstore.org/config.rb @@ -39,4 +39,5 @@ def precompiled_template(locals) page "/" page "/docs/v1/*", locals: { version: "v1" }, layout: "documentation" page "/docs/v2/*", locals: { version: "v2" }, layout: "documentation" +page "/docs/v3/*", locals: { version: "v3" }, layout: "documentation" page "*", locals: { version: "v2" } diff --git a/railseventstore.org/source/docs/v3/serialization.html.md.erb b/railseventstore.org/source/docs/v3/serialization.html.md.erb new file mode 100644 index 0000000000..d90e8edab3 --- /dev/null +++ b/railseventstore.org/source/docs/v3/serialization.html.md.erb @@ -0,0 +1,134 @@ +--- +title: Event serialization formats +--- + +By default RailsEventStore will use `YAML` as a +serialization format. The reason is that `YAML` is available out of box +and can serialize and deserialize data types which are not easily +handled in other formats. + +However, if you don't like `YAML` or you have different needs you can +choose to use different serializers or even replace mappers entirely. + +## Configuring a different serializer + +You can pass a different `serializer` as a dependency when [instantiating +the client](/docs/v2/install). + +Here is an example on how to configure RailsEventStore to serialize +events' `data` and `metadata` using `Marshal`. + +```ruby +# config/environments/*.rb + +Rails.application.configure do + config.to_prepare do + Rails.configuration.event_store = RailsEventStore::Client.new( + repository: RailsEventStoreActiveRecord::EventRepository.new(serializer: Marshal) + ) + end +end +``` + +The provided `serializer` must respond to `load` and `dump`. + +Serialization is needed not only when writing to and reading from storage, but also when scheduling events for background processing by async handlers: + +```ruby +Rails.configuration.event_store = RailsEventStore::Client.new( + dispatcher: RubyEventStore::ComposedDispatcher.new( + RailsEventStore::AfterCommitAsyncDispatcher.new(scheduler: ActiveJobScheduler.new(serializer: Marshal)), + RubyEventStore::Dispatcher.new + ) + ) +``` + +```ruby +class SomeHandler < ActiveJob::Base + include RailsEventStore::AsyncHandler.with(serializer: Marshal) + + def perform(event) + # ... + end +end +``` + +## Configuring for Postgres JSON/B data type + +In Postgres database, you can store your events data and metadata in json or jsonb format. + +To generate migration containing event table schemas run + +```console +$ rails generate rails_event_store_active_record:migration --data-type=jsonb +``` + +Next, in your `RailsEventStore::Client` initialization, set repository serialization to ` RailsEventStoreActiveRecord::EventRepository.new(serializer: JSON)` + +```ruby +# config/environments/*.rb + +Rails.application.configure do + config.to_prepare do + Rails.configuration.event_store = RailsEventStore::Client.new( + repository: RubyEventStore::ActiveRecord::EventRepository.new(serializer: JSON) + ) + end +end +``` + +Using the `JSON` serializer (or any compatible, e.g. `ActiveSupport::JSON`) will serialize the event data and metadata. +It will do otherwise when reading. +ActiveRecord serialization will be skipped. +Database itself expect data to be json already. + +
Bear in mind that JSON
will convert symbols to strings and you have to prepare for that when retrieving events.
+ +
+
+JSON.load(JSON.dump({foo: :bar}))
+=> {"foo"=>"bar"}
+
+
+
+
+class MyEvent < RailsEventStore::Event
+ def data
+ ActiveSupport::HashWithIndifferentAccess.new(super)
+ end
+end
+
+OrderPlaced = Class.new(MyEvent)
+
+
+
+
+event_store.publish(OrderPlaced.new(event_id: 'e34fc19a-a92f-4c21-8932-a10f6fb2602b', data: { foo: :bar }))
+event = event_store.read.event('e34fc19a-a92f-4c21-8932-a10f6fb2602b')
+
+event.data[:foo]
+\# => "bar"
+
+event.data['foo']
+\# => "bar"
+
+
++Another way to achive that could be define your own custom mapper and transformation +
+