sandthorn_driver_sequel 1.1.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/Guardfile +7 -0
- data/lib/sandthorn_driver_sequel/access/aggregate_access.rb +50 -0
- data/lib/sandthorn_driver_sequel/access/event_access.rb +81 -0
- data/lib/sandthorn_driver_sequel/access/snapshot_access.rb +87 -0
- data/lib/sandthorn_driver_sequel/access.rb +20 -0
- data/lib/sandthorn_driver_sequel/errors.rb +47 -5
- data/lib/sandthorn_driver_sequel/event_query.rb +90 -0
- data/lib/sandthorn_driver_sequel/event_store.rb +90 -153
- data/lib/sandthorn_driver_sequel/event_store_context.rb +1 -0
- data/lib/sandthorn_driver_sequel/migration.rb +9 -1
- data/lib/sandthorn_driver_sequel/old_event_store.rb +228 -0
- data/lib/sandthorn_driver_sequel/sequel_driver.rb +8 -25
- data/lib/sandthorn_driver_sequel/storage.rb +46 -0
- data/lib/sandthorn_driver_sequel/utilities/array.rb +13 -0
- data/lib/sandthorn_driver_sequel/utilities.rb +1 -0
- data/lib/sandthorn_driver_sequel/version.rb +1 -1
- data/lib/sandthorn_driver_sequel/wrappers/event_wrapper.rb +12 -0
- data/lib/sandthorn_driver_sequel/wrappers/snapshot_wrapper.rb +11 -0
- data/lib/sandthorn_driver_sequel/wrappers.rb +2 -0
- data/lib/sandthorn_driver_sequel.rb +5 -0
- data/sandthorn_driver_sequel.gemspec +2 -2
- data/spec/aggregate_access_spec.rb +97 -0
- data/spec/asking_for_aggregates_to_snapshot_spec.rb +7 -4
- data/spec/driver_interface_spec.rb +23 -40
- data/spec/event_access_spec.rb +96 -0
- data/spec/event_store_with_context_spec.rb +4 -4
- data/spec/get_events_spec.rb +20 -13
- data/spec/migration_specifying_domain_spec.rb +10 -10
- data/spec/saving_events_spec.rb +42 -39
- data/spec/saving_snapshot_spec.rb +7 -7
- data/spec/snapshot_access_spec.rb +119 -0
- data/spec/spec_helper.rb +0 -4
- data/spec/storage_spec.rb +66 -0
- metadata +39 -18
| @@ -37,6 +37,15 @@ module SandthornDriverSequel | |
| 37 37 | 
             
                      was_migrated aggr_migration_0, db
         | 
| 38 38 | 
             
                    end
         | 
| 39 39 | 
             
                  end
         | 
| 40 | 
            +
                  aggr_migration_1 = "#{aggregates_table_name}-20141024"
         | 
| 41 | 
            +
                  unless has_been_migrated?(aggr_migration_1)
         | 
| 42 | 
            +
                    driver.execute do |db|
         | 
| 43 | 
            +
                      db.alter_table(aggregates_table_name) do
         | 
| 44 | 
            +
                        set_column_default :aggregate_version, 0
         | 
| 45 | 
            +
                      end
         | 
| 46 | 
            +
                    end
         | 
| 47 | 
            +
                  end
         | 
| 48 | 
            +
             | 
| 40 49 | 
             
                end
         | 
| 41 50 | 
             
                def events
         | 
| 42 51 | 
             
                  events_migration_0 = "#{events_table_name}-20130308"
         | 
| @@ -95,7 +104,6 @@ module SandthornDriverSequel | |
| 95 104 | 
             
                      String :migration_name, null: false
         | 
| 96 105 | 
             
                      index [:migration_name], unique: true
         | 
| 97 106 | 
             
                      DateTime :timestamp, :null=>false
         | 
| 98 | 
            -
                      index [:migration_name], unique: true
         | 
| 99 107 | 
             
                    end
         | 
| 100 108 | 
             
                  end
         | 
| 101 109 | 
             
                end
         | 
| @@ -0,0 +1,228 @@ | |
| 1 | 
            +
            require 'sandthorn_driver_sequel/sequel_driver'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module SandthornDriverSequel
         | 
| 4 | 
            +
              class OldEventStore
         | 
| 5 | 
            +
                include EventStoreContext
         | 
| 6 | 
            +
                attr_reader :driver, :context, :url
         | 
| 7 | 
            +
                def initialize url: nil, context: nil
         | 
| 8 | 
            +
                  @driver = SequelDriver.new url: url
         | 
| 9 | 
            +
                  @context = context
         | 
| 10 | 
            +
                  @url = url
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def save_events aggregate_events, originating_aggregate_version, aggregate_id, class_name
         | 
| 14 | 
            +
                  current_aggregate_version = originating_aggregate_version
         | 
| 15 | 
            +
                  aggregate_type = class_name.to_s
         | 
| 16 | 
            +
                  driver.execute_in_transaction do |db|
         | 
| 17 | 
            +
                    if current_aggregate_version == 0
         | 
| 18 | 
            +
                      pk_id = register_new_aggregate(aggregate_id, aggregate_type, db)
         | 
| 19 | 
            +
                    else
         | 
| 20 | 
            +
                      current_aggregate = get_current_aggregate_from_aggregates_table(aggregate_id, aggregate_type, db)
         | 
| 21 | 
            +
                      check_initial_aggregate_version!(current_aggregate, current_aggregate_version)
         | 
| 22 | 
            +
                      pk_id = current_aggregate[:id]
         | 
| 23 | 
            +
                    end
         | 
| 24 | 
            +
                    timestamp = Time.now.utc
         | 
| 25 | 
            +
                    aggregate_events.each do |event|
         | 
| 26 | 
            +
                      current_aggregate_version += 1
         | 
| 27 | 
            +
                      check_event_aggregate_version!(event, class_name, current_aggregate_version)
         | 
| 28 | 
            +
                      insert_event(db, event, pk_id, timestamp)
         | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
                    db[aggregates_table_name].where(id: pk_id).update(aggregate_version: current_aggregate_version)
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                def insert_event(db, event, pk_id, timestamp)
         | 
| 35 | 
            +
                  to_insert = {
         | 
| 36 | 
            +
                      aggregate_table_id: pk_id,
         | 
| 37 | 
            +
                      aggregate_version: event[:aggregate_version],
         | 
| 38 | 
            +
                      event_name: event[:event_name],
         | 
| 39 | 
            +
                      event_data: event[:event_data],
         | 
| 40 | 
            +
                      timestamp: timestamp
         | 
| 41 | 
            +
                  }
         | 
| 42 | 
            +
                  db[events_table_name].insert(to_insert)
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                def check_event_aggregate_version!(event, aggregate_type, current_aggregate_version)
         | 
| 46 | 
            +
                  if event[:aggregate_version] != current_aggregate_version
         | 
| 47 | 
            +
                    raise SandthornDriverSequel::Errors::ConcurrencyError, event, aggregate_type, current_aggregate_version
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                def check_initial_aggregate_version!(aggregate, current_aggregate_version)
         | 
| 52 | 
            +
                  if aggregate[:aggregate_version] != current_aggregate_version
         | 
| 53 | 
            +
                    raise SandthornDriverSequel::Errors::WrongAggregateVersionError, aggregate, current_aggregate_version
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                def register_new_aggregate(aggregate_id, aggregate_type, db)
         | 
| 58 | 
            +
                  to_insert = {
         | 
| 59 | 
            +
                      aggregate_id: aggregate_id,
         | 
| 60 | 
            +
                      aggregate_type: aggregate_type,
         | 
| 61 | 
            +
                      aggregate_version: 0
         | 
| 62 | 
            +
                  }
         | 
| 63 | 
            +
                  pk_id = db[aggregates_table_name].insert(to_insert)
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                def save_snapshot aggregate_snapshot, aggregate_id, class_name
         | 
| 67 | 
            +
                  driver.execute_in_transaction do |db|
         | 
| 68 | 
            +
                    current_aggregate = get_current_aggregate_from_aggregates_table aggregate_id, class_name, db
         | 
| 69 | 
            +
                    pk_id = current_aggregate[:id]
         | 
| 70 | 
            +
                    current_snapshot = get_current_snapshot pk_id, db
         | 
| 71 | 
            +
                    aggregate_version = aggregate_snapshot[:aggregate_version]
         | 
| 72 | 
            +
                    return if snapshot_fresh?(current_snapshot, aggregate_version)
         | 
| 73 | 
            +
                    check_snapshot_version!(current_aggregate, aggregate_version)
         | 
| 74 | 
            +
                    if current_snapshot.nil?
         | 
| 75 | 
            +
                      to_insert = {aggregate_version: aggregate_version, snapshot_data: aggregate_snapshot[:event_data], aggregate_table_id: pk_id }
         | 
| 76 | 
            +
                      db[snapshots_table_name].insert(to_insert)
         | 
| 77 | 
            +
                    else
         | 
| 78 | 
            +
                      to_update = {aggregate_version: aggregate_version, snapshot_data: aggregate_snapshot[:event_data] }
         | 
| 79 | 
            +
                      db[snapshots_table_name].where(aggregate_table_id: pk_id).update(to_update)
         | 
| 80 | 
            +
                    end
         | 
| 81 | 
            +
                  end
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                def snapshot_fresh?(current_snapshot, aggregate_version)
         | 
| 85 | 
            +
                  !current_snapshot.nil? && current_snapshot[:aggregate_version] == aggregate_version
         | 
| 86 | 
            +
                end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                def check_snapshot_version!(aggregate, aggregate_version)
         | 
| 89 | 
            +
                  if aggregate[:aggregate_version] < aggregate_version
         | 
| 90 | 
            +
                    raise SandthornDriverSequel::Errors::WrongSnapshotVersionError, aggregate, version
         | 
| 91 | 
            +
                  end
         | 
| 92 | 
            +
                end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                def get_aggregate_events aggregate_id, *class_name
         | 
| 95 | 
            +
                  #aggregate_type = class_name.to_s unless class_name.nil?
         | 
| 96 | 
            +
                  return aggregate_events aggregate_id: aggregate_id
         | 
| 97 | 
            +
                end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                def get_aggregate aggregate_id, *class_name
         | 
| 100 | 
            +
                  snapshot = get_snapshot aggregate_id, class_name
         | 
| 101 | 
            +
                  after_aggregate_version = 0
         | 
| 102 | 
            +
                  after_aggregate_version = snapshot[:aggregate_version] unless snapshot.nil?
         | 
| 103 | 
            +
                  events = aggregate_events after_aggregate_version: after_aggregate_version, aggregate_id: aggregate_id
         | 
| 104 | 
            +
                  unless snapshot.nil?
         | 
| 105 | 
            +
                    snap_event = snapshot
         | 
| 106 | 
            +
                    snap_event[:event_name] = "aggregate_set_from_snapshot"
         | 
| 107 | 
            +
                    events = events.unshift(snap_event)
         | 
| 108 | 
            +
                  end
         | 
| 109 | 
            +
                  events
         | 
| 110 | 
            +
                end
         | 
| 111 | 
            +
                def get_aggregate_list_by_typename class_name
         | 
| 112 | 
            +
                  aggregate_type = class_name.to_s
         | 
| 113 | 
            +
                  driver.execute do |db|
         | 
| 114 | 
            +
                    db[aggregates_table_name].where(aggregate_type: aggregate_type).select(:aggregate_id).map { |e| e[:aggregate_id] }
         | 
| 115 | 
            +
                  end
         | 
| 116 | 
            +
                end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                def get_all_typenames
         | 
| 119 | 
            +
                  driver.execute do |db|
         | 
| 120 | 
            +
                    db[aggregates_table_name].select(:aggregate_type).distinct.order(:aggregate_type).map{|e| e[:aggregate_type]}
         | 
| 121 | 
            +
                  end
         | 
| 122 | 
            +
                end
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                def get_snapshot aggregate_id, *class_name
         | 
| 125 | 
            +
                  aggregate_type = class_name.first.to_s
         | 
| 126 | 
            +
                  driver.execute do |db|
         | 
| 127 | 
            +
                    current_aggregate = get_current_aggregate_from_aggregates_table aggregate_id, aggregate_type, db
         | 
| 128 | 
            +
                    snap = get_current_snapshot current_aggregate[:id], db
         | 
| 129 | 
            +
                    return nil if snap.nil?
         | 
| 130 | 
            +
                    return {aggregate_version: snap[:aggregate_version], event_data: snap[:snapshot_data]}
         | 
| 131 | 
            +
                  end
         | 
| 132 | 
            +
                end
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                def get_new_events_after_event_id_matching_classname event_id, class_name, args = {}
         | 
| 135 | 
            +
                  take = args.fetch(:take, 0)
         | 
| 136 | 
            +
                  aggregate_type = class_name.to_s
         | 
| 137 | 
            +
                  driver.execute do |db|
         | 
| 138 | 
            +
                    query = db[events_table_name].join(aggregates_table_name, id: :aggregate_table_id, aggregate_type: aggregate_type)
         | 
| 139 | 
            +
                    query = query.where{sequence_number > event_id}
         | 
| 140 | 
            +
                    rel = "#{events_table_name}__aggregate_version".to_sym
         | 
| 141 | 
            +
                    query = query.select(:aggregate_type, rel, :aggregate_id, :sequence_number, :event_name, :event_data, :timestamp)
         | 
| 142 | 
            +
                    query = query.limit(take) if take > 0
         | 
| 143 | 
            +
                    return query.order(:sequence_number).all
         | 
| 144 | 
            +
                  end
         | 
| 145 | 
            +
                end
         | 
| 146 | 
            +
                def get_events aggregate_types: [], take: 0, after_sequence_number: 0, include_events: [], exclude_events: []
         | 
| 147 | 
            +
                  include_events = include_events.map { |e| e.to_s  }
         | 
| 148 | 
            +
                  exclude_events = exclude_events.map { |e| e.to_s  }
         | 
| 149 | 
            +
                  aggregate_types = aggregate_types.map { |e| e.to_s  }
         | 
| 150 | 
            +
                  driver.execute do |db|
         | 
| 151 | 
            +
                    if aggregate_types.empty?
         | 
| 152 | 
            +
                      query = db[events_table_name].join(aggregates_table_name, id: :aggregate_table_id)
         | 
| 153 | 
            +
                    else
         | 
| 154 | 
            +
                      query = db[events_table_name].join(aggregates_table_name, id: :aggregate_table_id, aggregate_type: aggregate_types)
         | 
| 155 | 
            +
                    end
         | 
| 156 | 
            +
                    query = query.where{sequence_number > after_sequence_number}
         | 
| 157 | 
            +
                    unless include_events.empty?
         | 
| 158 | 
            +
                      query = query.where(event_name: include_events)
         | 
| 159 | 
            +
                    end
         | 
| 160 | 
            +
                    unless exclude_events.empty?
         | 
| 161 | 
            +
                      query = query.exclude(event_name: exclude_events)
         | 
| 162 | 
            +
                    end
         | 
| 163 | 
            +
                    rel = "#{events_table_name}__aggregate_version".to_sym
         | 
| 164 | 
            +
                    query = query.select(:aggregate_type, rel, :aggregate_id, :sequence_number, :event_name, :event_data, :timestamp)
         | 
| 165 | 
            +
                    query = query.limit(take) if take > 0
         | 
| 166 | 
            +
                    return query.order(:sequence_number).all
         | 
| 167 | 
            +
                  end
         | 
| 168 | 
            +
                end
         | 
| 169 | 
            +
                def obsolete_snapshots aggregate_types: [], max_event_distance: 100
         | 
| 170 | 
            +
                  driver.execute do |db|
         | 
| 171 | 
            +
                    rel = "#{snapshots_table_name}__aggregate_version".to_sym
         | 
| 172 | 
            +
                    aggr_rel = "#{aggregates_table_name}__aggregate_version".to_sym
         | 
| 173 | 
            +
                    query_select = eval("lambda{(#{aggr_rel} - coalesce(#{rel},0)).as(distance)}")
         | 
| 174 | 
            +
                    query = db[aggregates_table_name].left_outer_join(snapshots_table_name, aggregate_table_id: :id)
         | 
| 175 | 
            +
                    query = query.select &query_select
         | 
| 176 | 
            +
                    query = query.select_append(:aggregate_id, :aggregate_type)
         | 
| 177 | 
            +
                    query_where = eval("lambda{(#{aggr_rel} - coalesce(#{rel},0)) > max_event_distance}")
         | 
| 178 | 
            +
                    query = query.where &query_where 
         | 
| 179 | 
            +
                    unless class_names.empty?
         | 
| 180 | 
            +
                      class_names.map! {|c|c.to_s}
         | 
| 181 | 
            +
                      query = query.where(aggregate_type: class_names)
         | 
| 182 | 
            +
                    end
         | 
| 183 | 
            +
                    query.all
         | 
| 184 | 
            +
                  end
         | 
| 185 | 
            +
                end
         | 
| 186 | 
            +
                private
         | 
| 187 | 
            +
             | 
| 188 | 
            +
                def aggregate_events after_aggregate_version: 0, aggregate_id: nil
         | 
| 189 | 
            +
                  
         | 
| 190 | 
            +
                  rel = "#{events_table_name}__aggregate_version".to_sym
         | 
| 191 | 
            +
                  where_proc = eval("lambda{ #{rel} > after_aggregate_version }")
         | 
| 192 | 
            +
                  driver.execute do |db|
         | 
| 193 | 
            +
                    query = db[events_table_name].join(aggregates_table_name, id: :aggregate_table_id, aggregate_id: aggregate_id)
         | 
| 194 | 
            +
                    query = query.where &where_proc
         | 
| 195 | 
            +
                    result = query.select(rel, :aggregate_id, :sequence_number, :event_name, :event_data, :timestamp).order(:sequence_number).all
         | 
| 196 | 
            +
                  end
         | 
| 197 | 
            +
                      
         | 
| 198 | 
            +
                  # result = nil
         | 
| 199 | 
            +
                  # Benchmark.bm do |x|
         | 
| 200 | 
            +
                  #   x.report("find") { 
         | 
| 201 | 
            +
                  #     rel = "#{events_table_name}__aggregate_version".to_sym
         | 
| 202 | 
            +
                  #     where_proc = eval("lambda{ #{rel} > after_aggregate_version }")
         | 
| 203 | 
            +
                  #     driver.execute do |db|
         | 
| 204 | 
            +
                  #       query = db[events_table_name].join(aggregates_table_name, id: :aggregate_table_id, aggregate_id: aggregate_id)
         | 
| 205 | 
            +
                  #       query = query.where &where_proc
         | 
| 206 | 
            +
                  #       result = query.select(rel, :aggregate_id, :sequence_number, :event_name, :event_data, :timestamp).order(:sequence_number).all
         | 
| 207 | 
            +
                  #     end
         | 
| 208 | 
            +
                  #   }
         | 
| 209 | 
            +
             | 
| 210 | 
            +
                  # end
         | 
| 211 | 
            +
                  # result
         | 
| 212 | 
            +
                end
         | 
| 213 | 
            +
                def get_current_aggregate_from_aggregates_table aggregate_id, aggregate_type, db
         | 
| 214 | 
            +
                  aggregate_type = aggregate_type.to_s
         | 
| 215 | 
            +
                  current_aggregate = db[aggregates_table_name].where(aggregate_id: aggregate_id)
         | 
| 216 | 
            +
                  if current_aggregate.empty?
         | 
| 217 | 
            +
                    error_message = "#{aggregate_type} with id #{aggregate_id} was not found in the eventstore."
         | 
| 218 | 
            +
                    raise SandthornDriverSequel::Errors::NoAggregateError.new(error_message)
         | 
| 219 | 
            +
                  end
         | 
| 220 | 
            +
                  current_aggregate.first
         | 
| 221 | 
            +
                end
         | 
| 222 | 
            +
                def get_current_snapshot aggregate_table_id, db
         | 
| 223 | 
            +
                  snap = db[snapshots_table_name].where(aggregate_table_id: aggregate_table_id)
         | 
| 224 | 
            +
                  return nil if snap.empty?
         | 
| 225 | 
            +
                  snap.first
         | 
| 226 | 
            +
                end
         | 
| 227 | 
            +
              end
         | 
| 228 | 
            +
            end
         | 
| @@ -2,39 +2,22 @@ require 'sequel' | |
| 2 2 |  | 
| 3 3 | 
             
            module SandthornDriverSequel
         | 
| 4 4 | 
             
              class SequelDriver
         | 
| 5 | 
            +
             | 
| 5 6 | 
             
                def initialize args = {}
         | 
| 6 7 | 
             
                  @url = args.fetch(:url)
         | 
| 7 8 | 
             
                  Sequel.default_timezone = :utc
         | 
| 8 9 | 
             
                  @db = Sequel.connect(@url)
         | 
| 9 10 | 
             
                end
         | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 11 | 
            +
             | 
| 12 | 
            +
                def execute
         | 
| 13 | 
            +
                  yield @db
         | 
| 12 14 | 
             
                end
         | 
| 15 | 
            +
             | 
| 13 16 | 
             
                def execute_in_transaction &block
         | 
| 14 | 
            -
                  @db.transaction  | 
| 15 | 
            -
                     | 
| 16 | 
            -
                   | 
| 17 | 
            +
                  @db.transaction do
         | 
| 18 | 
            +
                    block.call(@db)
         | 
| 19 | 
            +
                  end
         | 
| 17 20 | 
             
                end
         | 
| 18 21 |  | 
| 19 22 | 
             
              end
         | 
| 20 23 | 
             
            end
         | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
            # module SandthornDriverSequel
         | 
| 24 | 
            -
            #   class SequelDriver
         | 
| 25 | 
            -
            #     def initialize args = {}
         | 
| 26 | 
            -
            #       @url = args.fetch(:url)
         | 
| 27 | 
            -
            #       Sequel.default_timezone = :utc
         | 
| 28 | 
            -
            #     end
         | 
| 29 | 
            -
            #     def execute &block
         | 
| 30 | 
            -
            #       Sequel.connect(@url) { |db| return block.call db}
         | 
| 31 | 
            -
            #     end
         | 
| 32 | 
            -
            #     def execute_in_transaction &block
         | 
| 33 | 
            -
            #       Sequel.connect(@url) do |db|
         | 
| 34 | 
            -
            #         db.transaction do
         | 
| 35 | 
            -
            #           return block.call db
         | 
| 36 | 
            -
            #         end
         | 
| 37 | 
            -
            #       end
         | 
| 38 | 
            -
            #     end 
         | 
| 39 | 
            -
            #   end
         | 
| 40 | 
            -
            # end
         | 
| @@ -0,0 +1,46 @@ | |
| 1 | 
            +
            module SandthornDriverSequel
         | 
| 2 | 
            +
              class Storage
         | 
| 3 | 
            +
                # = Storage
         | 
| 4 | 
            +
                # Abstracts access to contextualized database tables.
         | 
| 5 | 
            +
                #
         | 
| 6 | 
            +
                # == Rationale
         | 
| 7 | 
            +
                # Provide object-oriented access to the different tables to other objects.
         | 
| 8 | 
            +
                # Make it unnecessary for them to know about the current context.
         | 
| 9 | 
            +
                include EventStoreContext
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                attr_reader :db
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def initialize(db, context)
         | 
| 14 | 
            +
                  @db = db
         | 
| 15 | 
            +
                  @context = context
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                # Returns a Sequel::Model for accessing aggregates
         | 
| 19 | 
            +
                def aggregates
         | 
| 20 | 
            +
                  Class.new(Sequel::Model(aggregates_table))
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                # Returns a Sequel::Model for accessing events
         | 
| 24 | 
            +
                def events
         | 
| 25 | 
            +
                  Class.new(Sequel::Model(events_table))
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                # Returns a Sequel::Model for accessing snapshots
         | 
| 29 | 
            +
                def snapshots
         | 
| 30 | 
            +
                  Class.new(Sequel::Model(snapshots_table))
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                def aggregates_table
         | 
| 34 | 
            +
                  db[aggregates_table_name]
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                def events_table
         | 
| 38 | 
            +
                  db[events_table_name]
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                def snapshots_table
         | 
| 42 | 
            +
                  db[snapshots_table_name]
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
              end
         | 
| 46 | 
            +
            end
         | 
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            require "sandthorn_driver_sequel/utilities/array"
         | 
| @@ -0,0 +1,12 @@ | |
| 1 | 
            +
            require 'delegate'
         | 
| 2 | 
            +
            module SandthornDriverSequel
         | 
| 3 | 
            +
              class EventWrapper < SimpleDelegator
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                [:aggregate_version, :event_name, :event_data, :timestamp, :aggregate_table_id].each do |attribute|
         | 
| 6 | 
            +
                  define_method(attribute) do
         | 
| 7 | 
            +
                    fetch(attribute)
         | 
| 8 | 
            +
                  end
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              end
         | 
| 12 | 
            +
            end
         | 
| @@ -1,5 +1,10 @@ | |
| 1 1 | 
             
            require "sandthorn_driver_sequel/version"
         | 
| 2 | 
            +
            require "sandthorn_driver_sequel/utilities"
         | 
| 3 | 
            +
            require "sandthorn_driver_sequel/wrappers"
         | 
| 4 | 
            +
            require "sandthorn_driver_sequel/event_query"
         | 
| 2 5 | 
             
            require "sandthorn_driver_sequel/event_store_context"
         | 
| 6 | 
            +
            require "sandthorn_driver_sequel/access"
         | 
| 7 | 
            +
            require "sandthorn_driver_sequel/storage"
         | 
| 3 8 | 
             
            require 'sandthorn_driver_sequel/event_store'
         | 
| 4 9 | 
             
            require 'sandthorn_driver_sequel/errors'
         | 
| 5 10 | 
             
            require 'sandthorn_driver_sequel/migration'
         | 
| @@ -22,7 +22,6 @@ Gem::Specification.new do |spec| | |
| 22 22 | 
             
              spec.add_development_dependency "rake"
         | 
| 23 23 |  | 
| 24 24 | 
             
              spec.add_development_dependency "rspec"
         | 
| 25 | 
            -
              spec.add_development_dependency "coveralls"
         | 
| 26 25 | 
             
              spec.add_development_dependency "gem-release"
         | 
| 27 26 | 
             
              spec.add_development_dependency "sqlite3"
         | 
| 28 27 | 
             
              spec.add_development_dependency "pry"
         | 
| @@ -33,7 +32,8 @@ Gem::Specification.new do |spec| | |
| 33 32 | 
             
              spec.add_development_dependency "ruby-beautify"
         | 
| 34 33 | 
             
              spec.add_development_dependency "msgpack"
         | 
| 35 34 | 
             
              spec.add_development_dependency "snappy"
         | 
| 35 | 
            +
              spec.add_development_dependency "guard-rspec"
         | 
| 36 36 |  | 
| 37 | 
            -
              spec.add_runtime_dependency     "sequel"
         | 
| 37 | 
            +
              spec.add_runtime_dependency     "sequel", "~> 4.17"
         | 
| 38 38 | 
             
              spec.add_runtime_dependency     "pg"
         | 
| 39 39 | 
             
            end
         | 
| @@ -0,0 +1,97 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module SandthornDriverSequel
         | 
| 4 | 
            +
              describe AggregateAccess do
         | 
| 5 | 
            +
                include EventStoreContext
         | 
| 6 | 
            +
                let(:context) { :test }
         | 
| 7 | 
            +
                let(:db) { Sequel.connect(event_store_url)}
         | 
| 8 | 
            +
                let(:aggregate_id) { generate_uuid }
         | 
| 9 | 
            +
                let(:storage) { Storage.new(db, :test) }
         | 
| 10 | 
            +
                let(:access) { AggregateAccess.new(storage) }
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                before { prepare_for_test }
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                describe "#find" do
         | 
| 15 | 
            +
                  it "finds by table id" do
         | 
| 16 | 
            +
                    aggregate = access.register_aggregate(aggregate_id, "boo")
         | 
| 17 | 
            +
                    aggregate = access.find(aggregate.id)
         | 
| 18 | 
            +
                    expect(aggregate.aggregate_id).to eq(aggregate_id)
         | 
| 19 | 
            +
                    expect(aggregate.aggregate_type).to eq("boo")
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  it "doesn't find by table id" do
         | 
| 23 | 
            +
                    access.register_aggregate(aggregate_id, "foo")
         | 
| 24 | 
            +
                    max_id = db[aggregates_table_name].max(:id)
         | 
| 25 | 
            +
                    expect(access.find(max_id + 1)).to be_nil
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                describe "#find_by_aggregate_id" do
         | 
| 30 | 
            +
                  context "when the aggregate is registered" do
         | 
| 31 | 
            +
                    it "returns the aggregate" do
         | 
| 32 | 
            +
                      access.register_aggregate(aggregate_id, "bar")
         | 
| 33 | 
            +
                      aggregate = access.find_by_aggregate_id(aggregate_id)
         | 
| 34 | 
            +
                      expect(aggregate.aggregate_id).to eq(aggregate_id)
         | 
| 35 | 
            +
                    end
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  context "when the aggregate isn't registered" do
         | 
| 39 | 
            +
                    it "returns nil" do
         | 
| 40 | 
            +
                      expect(access.find_by_aggregate_id(aggregate_id)).to be_nil
         | 
| 41 | 
            +
                    end
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                describe "#find_or_register" do
         | 
| 46 | 
            +
                  context "when the aggregate is registered" do
         | 
| 47 | 
            +
                    it "returns the aggregate" do
         | 
| 48 | 
            +
                      access.register_aggregate(aggregate_id, "baz")
         | 
| 49 | 
            +
                      aggregate = access.find_or_register(aggregate_id, "qux")
         | 
| 50 | 
            +
                      expect(aggregate.aggregate_id).to eq(aggregate_id)
         | 
| 51 | 
            +
                      expect(aggregate.aggregate_type).to eq("baz")
         | 
| 52 | 
            +
                    end
         | 
| 53 | 
            +
                  end
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                describe "#register_aggregate" do
         | 
| 57 | 
            +
                  it "returns the aggregate" do
         | 
| 58 | 
            +
                    aggregate = access.register_aggregate(aggregate_id, "bar")
         | 
| 59 | 
            +
                    expect(aggregate.aggregate_id).to eq(aggregate_id)
         | 
| 60 | 
            +
                    expect(aggregate.aggregate_type).to eq("bar")
         | 
| 61 | 
            +
                    expect(aggregate.id).to_not be_nil
         | 
| 62 | 
            +
                  end
         | 
| 63 | 
            +
                end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                describe "#aggregate_types" do
         | 
| 66 | 
            +
                  it "returns all aggregate types in the event store" do
         | 
| 67 | 
            +
                    types = ["foo", "bar", "qux"]
         | 
| 68 | 
            +
                    types.each do |type|
         | 
| 69 | 
            +
                      access.register_aggregate(generate_uuid, type)
         | 
| 70 | 
            +
                    end
         | 
| 71 | 
            +
                    expect(access.aggregate_types).to eq(types.sort)
         | 
| 72 | 
            +
                  end
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                describe "#aggregate_ids" do
         | 
| 76 | 
            +
                  context "when given no argument" do
         | 
| 77 | 
            +
                    it "returns all aggregate ids" do
         | 
| 78 | 
            +
                      aggregate_ids = 3.times.map { generate_uuid }
         | 
| 79 | 
            +
                      aggregate_ids.each { |id| access.register_aggregate(id, "foo") }
         | 
| 80 | 
            +
                      expect(access.aggregate_ids).to eq(aggregate_ids)
         | 
| 81 | 
            +
                    end
         | 
| 82 | 
            +
                  end
         | 
| 83 | 
            +
                  context "when given an aggregate type" do
         | 
| 84 | 
            +
                    it "returns only aggregates of that type" do
         | 
| 85 | 
            +
                      foo_agg_id, bar_agg_id = generate_uuid, generate_uuid
         | 
| 86 | 
            +
                      access.register_aggregate(foo_agg_id, "foo")
         | 
| 87 | 
            +
                      access.register_aggregate(bar_agg_id, "bar")
         | 
| 88 | 
            +
                      expect(access.aggregate_ids(aggregate_type: "foo")).to eq([foo_agg_id])
         | 
| 89 | 
            +
                    end        
         | 
| 90 | 
            +
                  end
         | 
| 91 | 
            +
                end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                def generate_uuid
         | 
| 94 | 
            +
                  SecureRandom.uuid
         | 
| 95 | 
            +
                end
         | 
| 96 | 
            +
              end
         | 
| 97 | 
            +
            end
         |