sequent 0.1.10 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/db/sequent_schema.rb +0 -3
  3. data/lib/sequent/configuration.rb +10 -0
  4. data/lib/sequent/core/aggregate_repository.rb +41 -8
  5. data/lib/sequent/core/aggregate_root.rb +0 -24
  6. data/lib/sequent/core/aggregate_snapshotter.rb +2 -2
  7. data/lib/sequent/core/base_command_handler.rb +2 -6
  8. data/lib/sequent/core/base_event_handler.rb +1 -1
  9. data/lib/sequent/core/command.rb +23 -15
  10. data/lib/sequent/core/command_service.rb +1 -1
  11. data/lib/sequent/core/core.rb +1 -1
  12. data/lib/sequent/core/event.rb +0 -21
  13. data/lib/sequent/core/event_record.rb +11 -1
  14. data/lib/sequent/core/event_store.rb +122 -18
  15. data/lib/sequent/core/ext/ext.rb +20 -0
  16. data/lib/sequent/core/helpers/array_with_type.rb +4 -0
  17. data/lib/sequent/core/helpers/association_validator.rb +22 -7
  18. data/lib/sequent/core/helpers/attribute_support.rb +15 -6
  19. data/lib/sequent/core/helpers/param_support.rb +28 -29
  20. data/lib/sequent/core/helpers/self_applier.rb +4 -3
  21. data/lib/sequent/core/helpers/string_to_value_parsers.rb +10 -3
  22. data/lib/sequent/core/helpers/type_conversion_support.rb +5 -0
  23. data/lib/sequent/core/helpers/uuid_helper.rb +2 -2
  24. data/lib/sequent/core/helpers/value_validators.rb +2 -2
  25. data/lib/sequent/core/random_uuid_generator.rb +9 -0
  26. data/lib/sequent/core/record_sessions/active_record_session.rb +11 -5
  27. data/lib/sequent/core/record_sessions/replay_events_session.rb +138 -108
  28. data/lib/sequent/core/sequent_oj.rb +4 -3
  29. data/lib/sequent/core/stream_record.rb +1 -1
  30. data/lib/sequent/core/transactions/active_record_transaction_provider.rb +1 -1
  31. data/lib/sequent/migrations/migrate_events.rb +22 -15
  32. data/lib/sequent/rake/tasks.rb +102 -0
  33. data/lib/sequent/sequent.rb +4 -0
  34. data/lib/sequent/support.rb +3 -0
  35. data/lib/sequent/support/database.rb +55 -0
  36. data/lib/sequent/support/view_projection.rb +58 -0
  37. data/lib/sequent/support/view_schema.rb +22 -0
  38. data/lib/sequent/test/command_handler_helpers.rb +21 -8
  39. data/lib/sequent/test/event_handler_helpers.rb +7 -3
  40. data/lib/version.rb +1 -1
  41. metadata +54 -9
  42. data/lib/sequent/core/tenant_event_store.rb +0 -24
@@ -4,8 +4,10 @@ module Sequent
4
4
  module Core
5
5
  # small wrapper class to centralize oj and its settings.
6
6
  class Oj
7
-
8
- ::Oj.default_options={mode: :compat}
7
+ ::Oj.default_options = {
8
+ bigdecimal_as_decimal: false,
9
+ mode: :compat,
10
+ }
9
11
 
10
12
  def self.strict_load(json)
11
13
  ::Oj.strict_load(json, {})
@@ -14,7 +16,6 @@ module Sequent
14
16
  def self.dump(obj)
15
17
  ::Oj.dump(obj)
16
18
  end
17
-
18
19
  end
19
20
  end
20
21
  end
@@ -20,7 +20,7 @@ module Sequent
20
20
  validates_presence_of :aggregate_type, :aggregate_id
21
21
  validates_numericality_of :snapshot_threshold, only_integer: true, greater_than: 0, allow_nil: true
22
22
 
23
- has_many :events
23
+ has_many :event_records
24
24
 
25
25
  def event_stream
26
26
  EventStream.new(aggregate_type: aggregate_type, aggregate_id: aggregate_id, snapshot_threshold: snapshot_threshold, stream_record_id: id)
@@ -4,7 +4,7 @@ module Sequent
4
4
 
5
5
  class ActiveRecordTransactionProvider
6
6
  def transactional
7
- ActiveRecord::Base.transaction do
7
+ ActiveRecord::Base.transaction(requires_new: true) do
8
8
  yield
9
9
  end
10
10
  end
@@ -31,26 +31,33 @@ module Sequent
31
31
  #
32
32
  # @param current_version The current version of the application. E.g. 10
33
33
  # @param new_version The version to migrate to. E.g. 11
34
- # @param &after_migration_block an optional block to run after the migrations run. E.g. close resources
34
+ # @param &after_migration_block an optional block (with the current upgrade version as param) to run after the migrations run. E.g. close resources
35
35
  #
36
36
  def execute_migrations(current_version, new_version, &after_migration_block)
37
- if current_version != new_version and current_version > 0
38
- ((current_version + 1)..new_version).each do |upgrade_to_version|
39
- migration_class = begin
40
- Class.const_get("Database::MigrateToVersion#{upgrade_to_version}")
41
- rescue NameError
42
- nil
43
- end
44
- if migration_class
45
- begin
46
- migration_class.new(@env).migrate
47
- ensure
48
- after_migration_block.call if after_migration_block
49
- end
50
- end
37
+ migrations(current_version, new_version).each do |migration_class|
38
+ migration = migration_class.new(@env)
39
+ begin
40
+ migration.migrate
41
+ ensure
42
+ yield(migration.version) if block_given?
51
43
  end
52
44
  end
53
45
  end
46
+
47
+ def migrations(current_version, new_version)
48
+ return [] if current_version == 0
49
+ ((current_version + 1)..new_version).map do |upgrade_to_version|
50
+ begin
51
+ Class.const_get("Database::MigrateToVersion#{upgrade_to_version}")
52
+ rescue NameError
53
+ nil
54
+ end
55
+ end.compact
56
+ end
57
+
58
+ def has_migrations?(current_version, new_version)
59
+ migrations(current_version, new_version).any?
60
+ end
54
61
  end
55
62
  end
56
63
  end
@@ -0,0 +1,102 @@
1
+ require 'active_record'
2
+ require 'rake'
3
+ require 'rake/tasklib'
4
+
5
+ require 'sequent/support'
6
+
7
+ module Sequent
8
+ module Rake
9
+ class Tasks < ::Rake::TaskLib
10
+ include ::Rake::DSL
11
+
12
+ DEFAULT_OPTIONS = {
13
+ migrations_path: 'db/migrate',
14
+ event_store_schema: 'public'
15
+ }
16
+
17
+ attr_reader :options
18
+
19
+ def initialize(options)
20
+ @options = DEFAULT_OPTIONS.merge(options)
21
+ end
22
+
23
+ def register!
24
+ register_db_tasks!
25
+ register_view_schema_tasks!
26
+ end
27
+
28
+ def register_db_tasks!
29
+ namespace :db do
30
+ desc 'Create the database'
31
+ task :create do
32
+ current_environments.each do |env|
33
+ env_db = db_config(env)
34
+ puts "Create database #{env_db['database']}"
35
+ Sequent::Support::Database.create!(env_db)
36
+ end
37
+ end
38
+
39
+ desc 'Drop the database'
40
+ task :drop do
41
+ current_environments.each do |env|
42
+ env_db = db_config(env)
43
+ puts "Drop database #{env_db['database']}"
44
+ Sequent::Support::Database.drop!(env_db)
45
+ end
46
+ end
47
+
48
+ task :establish_connection do
49
+ env_db = db_config(options.fetch(:environment))
50
+ Sequent::Support::Database.establish_connection(env_db)
51
+ end
52
+
53
+ desc 'Migrate the database'
54
+ task migrate: :establish_connection do
55
+ database.create_schema!(options.fetch(:event_store_schema))
56
+ database.migrate(options.fetch(:migrations_path))
57
+ end
58
+ end
59
+ end
60
+
61
+ def register_view_schema_tasks!
62
+ namespace :view_schema do
63
+ desc 'Build the view schema'
64
+ task build: :'db:establish_connection' do
65
+ if database.schema_exists?(view_projection.schema_name)
66
+ puts "View version #{view_projection.version} already exists; no need to build it"
67
+ else
68
+ database.create_schema!(view_projection.schema_name)
69
+ view_projection.build!
70
+ end
71
+ end
72
+
73
+ desc 'Drop the view schema'
74
+ task drop: :'db:establish_connection' do
75
+ database.drop_schema!(view_projection.schema_name)
76
+ end
77
+ end
78
+ end
79
+
80
+ private
81
+
82
+ def current_environments
83
+ environment = options.fetch(:environment)
84
+ envs = [environment]
85
+ envs << 'test' if environment == 'development'
86
+ envs
87
+ end
88
+
89
+ def database
90
+ @database ||= Sequent::Support::Database.new
91
+ end
92
+
93
+ def db_config(environment)
94
+ options.fetch(:db_config_supplier)[environment] or fail "No database config for #{environment}"
95
+ end
96
+
97
+ def view_projection
98
+ options.fetch(:view_projection)
99
+ end
100
+ end
101
+ end
102
+ end
@@ -5,6 +5,10 @@ require_relative 'configuration'
5
5
  require 'logger'
6
6
 
7
7
  module Sequent
8
+ def self.new_uuid
9
+ Sequent.configuration.uuid_generator.uuid
10
+ end
11
+
8
12
  def self.logger
9
13
  @logger ||= Logger.new(STDOUT).tap {|l| l.level = Logger::INFO }
10
14
  end
@@ -0,0 +1,3 @@
1
+ require_relative 'support/database'
2
+ require_relative 'support/view_projection'
3
+ require_relative 'support/view_schema'
@@ -0,0 +1,55 @@
1
+ module Sequent
2
+ module Support
3
+ # Offers support operations for a postgres database.
4
+ #
5
+ # Class methods do establish their own database connections (and therefore
6
+ # take in a database configuration). Instance methods assume that a database
7
+ # connection yet is established.
8
+ class Database
9
+ attr_reader :db_config
10
+
11
+ def self.create!(db_config)
12
+ ActiveRecord::Base.establish_connection(db_config.merge('database' => 'postgres'))
13
+ ActiveRecord::Base.connection.create_database(db_config['database'])
14
+ end
15
+
16
+ def self.drop!(db_config)
17
+ ActiveRecord::Base.establish_connection(db_config.merge('database' => 'postgres'))
18
+ ActiveRecord::Base.connection.drop_database(db_config['database'])
19
+ end
20
+
21
+ def self.establish_connection(db_config)
22
+ ActiveRecord::Base.establish_connection(db_config)
23
+ end
24
+
25
+ def self.disconnect!
26
+ ActiveRecord::Base.connection_pool.disconnect!
27
+ end
28
+
29
+ def schema_exists?(schema)
30
+ ActiveRecord::Base.connection.execute(
31
+ "SELECT schema_name FROM information_schema.schemata WHERE schema_name like '#{schema}'"
32
+ ).count == 1
33
+ end
34
+
35
+ def create_schema!(schema)
36
+ sql = "CREATE SCHEMA IF NOT EXISTS #{schema}"
37
+ if user = ActiveRecord::Base.connection_config[:username]
38
+ sql += " AUTHORIZATION #{user}"
39
+ end
40
+ ActiveRecord::Base.connection.execute(sql)
41
+ end
42
+
43
+ def drop_schema!(schema)
44
+ ActiveRecord::Base.connection.execute(
45
+ "DROP SCHEMA IF EXISTS #{schema} CASCADE"
46
+ )
47
+ end
48
+
49
+ def migrate(migrations_path, verbose: true)
50
+ ActiveRecord::Migration.verbose = verbose
51
+ ActiveRecord::Migrator.migrate(migrations_path)
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,58 @@
1
+ require 'postgresql_cursor'
2
+
3
+ module Sequent
4
+ module Support
5
+ class ViewProjection
6
+ attr_reader :name, :version, :schema_definition
7
+ def initialize(options)
8
+ @name = options.fetch(:name)
9
+ @version = options.fetch(:version)
10
+ @schema_definition = options.fetch(:definition)
11
+ @replay_event_handlers = options.fetch(:event_handlers)
12
+ end
13
+
14
+ def build!
15
+ with_default_configuration do
16
+ Sequent.configuration.event_handlers = @replay_event_handlers
17
+
18
+ load schema_definition
19
+ event_store = Sequent.configuration.event_store
20
+ ordering = Events::ORDERED_BY_STREAM
21
+ event_store.replay_events_from_cursor(
22
+ block_size: 10_000,
23
+ get_events: ->() { ordering[event_store] }
24
+ )
25
+ end
26
+ end
27
+
28
+ def schema_name
29
+ "#{name}_#{version}"
30
+ end
31
+
32
+ private
33
+
34
+ def with_default_configuration
35
+ original_configuration = Sequent.configuration
36
+ Sequent::Configuration.reset
37
+ yield
38
+ Sequent::Configuration.restore(original_configuration)
39
+ end
40
+ end
41
+
42
+ module Events
43
+ extend ActiveRecord::ConnectionAdapters::Quoting
44
+
45
+ ORDERED_BY_STREAM = lambda do |event_store|
46
+ event_records = quote_table_name(event_store.event_record_class.table_name)
47
+ stream_records = quote_table_name(event_store.stream_record_class.table_name)
48
+ snapshot_event_type = quote(event_store.snapshot_event_class)
49
+
50
+ event_store.event_record_class
51
+ .select("event_type, event_json")
52
+ .joins("INNER JOIN #{stream_records} ON #{event_records}.stream_record_id = #{stream_records}.id")
53
+ .where("event_type <> #{snapshot_event_type}")
54
+ .order!("#{stream_records}.id, #{event_records}.sequence_number")
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,22 @@
1
+ module Sequent
2
+ module Support
3
+ class ViewSchema < ActiveRecord::Schema
4
+ def define(info, &block)
5
+ view_projection = info[:view_projection]
6
+ switch_to_schema(view_projection) if view_projection
7
+ super
8
+ ensure
9
+ switch_back_to_original_schema if view_projection
10
+ end
11
+
12
+ def switch_to_schema(view_projection)
13
+ @original_schema_search_path = connection.schema_search_path
14
+ connection.schema_search_path = view_projection.schema_name
15
+ end
16
+
17
+ def switch_back_to_original_schema
18
+ connection.schema_search_path = @original_schema_search_path
19
+ end
20
+ end
21
+ end
22
+ end
@@ -44,9 +44,17 @@ module Sequent
44
44
  end
45
45
 
46
46
  def load_events(aggregate_id)
47
- event_stream = @event_streams[aggregate_id]
48
- return nil unless event_stream
49
- [event_stream, deserialize_events(@all_events[aggregate_id])]
47
+ load_events_for_aggregates([aggregate_id])[0]
48
+ end
49
+
50
+ def load_events_for_aggregates(aggregate_ids)
51
+ return [] if aggregate_ids.none?
52
+
53
+ aggregate_ids.map do |aggregate_id|
54
+ @event_streams[aggregate_id]
55
+ end.compact.map do |event_stream|
56
+ [event_stream, deserialize_events(@all_events[event_stream.aggregate_id])]
57
+ end
50
58
  end
51
59
 
52
60
  def find_event_stream(aggregate_id)
@@ -119,16 +127,21 @@ module Sequent
119
127
 
120
128
  def when_command command
121
129
  raise "@command_handler is mandatory when using the #{self.class}" unless @command_handler
122
- raise "Command handler #{@command_handler} cannot handle command #{command}, please configure the command type (forgot an include in the command class?)" unless @command_handler.handles_message?(command)
130
+ raise "Command handler #{@command_handler} cannot handle command #{command}, please configure the command type (forgot an include in the command class?)" unless @command_handler.class.handles_message?(command)
123
131
  @command_handler.handle_message(command)
124
132
  @repository.commit(command)
125
133
  @repository.clear
126
134
  end
127
135
 
128
- def then_events *events
129
- expect(@event_store.stored_events.map(&:class)).to eq(events.flatten(1).map(&:class))
130
- @event_store.stored_events.zip(events.flatten(1)).each do |actual, expected|
131
- expect(Sequent::Core::Oj.strict_load(Sequent::Core::Oj.dump(actual.payload))).to eq(Sequent::Core::Oj.strict_load(Sequent::Core::Oj.dump(expected.payload))) if expected
136
+ def then_events(*expected_events)
137
+ expected_classes = expected_events.flatten(1).map { |event| event.class == Class ? event : event.class }
138
+ expect(@event_store.stored_events.map(&:class)).to eq(expected_classes)
139
+
140
+ @event_store.stored_events.zip(expected_events.flatten(1)).each_with_index do |(actual, expected), index|
141
+ next if expected.class == Class
142
+ _actual = Sequent::Core::Oj.strict_load(Sequent::Core::Oj.dump(actual.payload))
143
+ _expected = Sequent::Core::Oj.strict_load(Sequent::Core::Oj.dump(expected.payload))
144
+ expect(_actual).to eq(_expected), "#{index+1}th Event of type #{actual.class} not equal\nexpected: #{_expected.inspect}\n got: #{_actual.inspect}" if expected
132
145
  end
133
146
  end
134
147
 
@@ -39,9 +39,12 @@ module Sequent
39
39
  end
40
40
  end
41
41
 
42
- def then_events *events
43
- expect(@event_store.stored_events.map(&:class)).to eq(events.flatten(1).map(&:class))
44
- @event_store.stored_events.zip(events.flatten(1)).each do |actual, expected|
42
+ def then_events(*expected_events)
43
+ expected_classes = expected_events.flatten(1).map { |event| event.class == Class ? event : event.class }
44
+ expect(@event_store.stored_events.map(&:class)).to eq(expected_classes)
45
+
46
+ @event_store.stored_events.zip(expected_events.flatten(1)).each do |actual, expected|
47
+ next if expected.class == Class
45
48
  expect(Sequent::Core::Oj.strict_load(Sequent::Core::Oj.dump(actual.payload))).to eq(Sequent::Core::Oj.strict_load(Sequent::Core::Oj.dump(expected.payload))) if expected
46
49
  end
47
50
  end
@@ -58,6 +61,7 @@ module Sequent
58
61
  recorded = fake_command_service.recorded_commands
59
62
  expect(recorded.map(&:class)).to eq(commands.flatten(1).map(&:class))
60
63
  expect(fake_command_service.recorded_commands).to eq(commands.flatten(1))
64
+ expect(recorded).to all(be_valid)
61
65
  end
62
66
 
63
67
  def self.included(spec)
@@ -1,3 +1,3 @@
1
1
  module Sequent
2
- VERSION = '0.1.10'
2
+ VERSION = '1.0.0'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequent
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.10
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lars Vonk
@@ -10,36 +10,48 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2015-10-12 00:00:00.000000000 Z
13
+ date: 2017-06-30 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord
17
17
  requirement: !ruby/object:Gem::Requirement
18
18
  requirements:
19
- - - "~>"
19
+ - - ">="
20
20
  - !ruby/object:Gem::Version
21
21
  version: '4.0'
22
+ - - "<"
23
+ - !ruby/object:Gem::Version
24
+ version: '5.2'
22
25
  type: :runtime
23
26
  prerelease: false
24
27
  version_requirements: !ruby/object:Gem::Requirement
25
28
  requirements:
26
- - - "~>"
29
+ - - ">="
27
30
  - !ruby/object:Gem::Version
28
31
  version: '4.0'
32
+ - - "<"
33
+ - !ruby/object:Gem::Version
34
+ version: '5.2'
29
35
  - !ruby/object:Gem::Dependency
30
36
  name: activemodel
31
37
  requirement: !ruby/object:Gem::Requirement
32
38
  requirements:
33
- - - "~>"
39
+ - - ">="
34
40
  - !ruby/object:Gem::Version
35
41
  version: '4.0'
42
+ - - "<"
43
+ - !ruby/object:Gem::Version
44
+ version: '5.2'
36
45
  type: :runtime
37
46
  prerelease: false
38
47
  version_requirements: !ruby/object:Gem::Requirement
39
48
  requirements:
40
- - - "~>"
49
+ - - ">="
41
50
  - !ruby/object:Gem::Version
42
51
  version: '4.0'
52
+ - - "<"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.2'
43
55
  - !ruby/object:Gem::Dependency
44
56
  name: pg
45
57
  requirement: !ruby/object:Gem::Requirement
@@ -54,20 +66,34 @@ dependencies:
54
66
  - - "~>"
55
67
  - !ruby/object:Gem::Version
56
68
  version: '0.18'
69
+ - !ruby/object:Gem::Dependency
70
+ name: postgresql_cursor
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.6'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.6'
57
83
  - !ruby/object:Gem::Dependency
58
84
  name: oj
59
85
  requirement: !ruby/object:Gem::Requirement
60
86
  requirements:
61
87
  - - "~>"
62
88
  - !ruby/object:Gem::Version
63
- version: '2.10'
89
+ version: 2.17.5
64
90
  type: :runtime
65
91
  prerelease: false
66
92
  version_requirements: !ruby/object:Gem::Requirement
67
93
  requirements:
68
94
  - - "~>"
69
95
  - !ruby/object:Gem::Version
70
- version: '2.10'
96
+ version: 2.17.5
71
97
  - !ruby/object:Gem::Dependency
72
98
  name: thread_safe
73
99
  requirement: !ruby/object:Gem::Requirement
@@ -152,6 +178,20 @@ dependencies:
152
178
  - - "~>"
153
179
  - !ruby/object:Gem::Version
154
180
  version: '0.10'
181
+ - !ruby/object:Gem::Dependency
182
+ name: simplecov
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - "~>"
186
+ - !ruby/object:Gem::Version
187
+ version: 0.14.1
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - "~>"
193
+ - !ruby/object:Gem::Version
194
+ version: 0.14.1
155
195
  description: Sequent is a CQRS and event sourcing framework for Ruby.
156
196
  email:
157
197
  - lars.vonk@gmail.com
@@ -194,13 +234,13 @@ files:
194
234
  - lib/sequent/core/helpers/type_conversion_support.rb
195
235
  - lib/sequent/core/helpers/uuid_helper.rb
196
236
  - lib/sequent/core/helpers/value_validators.rb
237
+ - lib/sequent/core/random_uuid_generator.rb
197
238
  - lib/sequent/core/record_sessions/active_record_session.rb
198
239
  - lib/sequent/core/record_sessions/record_sessions.rb
199
240
  - lib/sequent/core/record_sessions/replay_events_session.rb
200
241
  - lib/sequent/core/sequent_oj.rb
201
242
  - lib/sequent/core/snapshots.rb
202
243
  - lib/sequent/core/stream_record.rb
203
- - lib/sequent/core/tenant_event_store.rb
204
244
  - lib/sequent/core/transactions/active_record_transaction_provider.rb
205
245
  - lib/sequent/core/transactions/no_transactions.rb
206
246
  - lib/sequent/core/transactions/transactions.rb
@@ -208,7 +248,12 @@ files:
208
248
  - lib/sequent/core/workflow.rb
209
249
  - lib/sequent/migrations/migrate_events.rb
210
250
  - lib/sequent/migrations/migrations.rb
251
+ - lib/sequent/rake/tasks.rb
211
252
  - lib/sequent/sequent.rb
253
+ - lib/sequent/support.rb
254
+ - lib/sequent/support/database.rb
255
+ - lib/sequent/support/view_projection.rb
256
+ - lib/sequent/support/view_schema.rb
212
257
  - lib/sequent/test.rb
213
258
  - lib/sequent/test/command_handler_helpers.rb
214
259
  - lib/sequent/test/event_handler_helpers.rb