From d3b725b331306e0e63097e0a5dd3f09a862f16c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Pacana?= Date: Thu, 2 Oct 2025 12:58:26 +0200 Subject: [PATCH 1/9] SpecificationResult goes Data --- .../lib/ruby_event_store/specification.rb | 83 ++- .../ruby_event_store/specification_result.rb | 482 +++++++----------- 2 files changed, 206 insertions(+), 359 deletions(-) diff --git a/ruby_event_store/lib/ruby_event_store/specification.rb b/ruby_event_store/lib/ruby_event_store/specification.rb index 7096c94455..8744f1b3ef 100644 --- a/ruby_event_store/lib/ruby_event_store/specification.rb +++ b/ruby_event_store/lib/ruby_event_store/specification.rb @@ -18,7 +18,7 @@ def initialize(reader, result = SpecificationResult.new) # @param stream_name [String] name of the stream to get events from # @return [Specification] def stream(stream_name) - Specification.new(reader, result.dup { |r| r.stream = Stream.new(stream_name) }) + Specification.new(reader, result.with(stream: Stream.new(stream_name))) end # Limits the query to events before or after another event. @@ -28,7 +28,8 @@ def stream(stream_name) # @return [Specification] def from(start) raise InvalidPageStart if start.nil? || start.empty? - Specification.new(reader, result.dup { |r| r.start = start }) + + Specification.new(reader, result.with(start: start)) end # Limits the query to events before or after another event. @@ -38,7 +39,8 @@ def from(start) # @return [Specification] def to(stop) raise InvalidPageStop if stop.nil? || stop.empty? - Specification.new(reader, result.dup { |r| r.stop = stop }) + + Specification.new(reader, result.with(stop: stop)) end # Limits the query to events that occurred before given time. @@ -48,13 +50,8 @@ def to(stop) # @return [Specification] def older_than(time) raise ArgumentError unless time.respond_to?(:to_time) - Specification.new( - reader, - result.dup do |r| - r.older_than = time - r.older_than_or_equal = nil - end, - ) + + Specification.new(reader, result.with(older_than: time, older_than_or_equal: nil)) end # Limits the query to events that occurred on or before given time. @@ -64,13 +61,8 @@ def older_than(time) # @return [Specification] def older_than_or_equal(time) raise ArgumentError unless time.respond_to?(:to_time) - Specification.new( - reader, - result.dup do |r| - r.older_than = nil - r.older_than_or_equal = time - end, - ) + + Specification.new(reader, result.with(older_than: nil, older_than_or_equal: time)) end # Limits the query to events that occurred after given time. @@ -80,13 +72,8 @@ def older_than_or_equal(time) # @return [Specification] def newer_than(time) raise ArgumentError unless time.respond_to?(:to_time) - Specification.new( - reader, - result.dup do |r| - r.newer_than_or_equal = nil - r.newer_than = time - end, - ) + + Specification.new(reader, result.with(newer_than: time, newer_than_or_equal: nil)) end # Limits the query to events that occurred on or after given time. @@ -96,13 +83,8 @@ def newer_than(time) # @return [Specification] def newer_than_or_equal(time) raise ArgumentError unless time.respond_to?(:to_time) - Specification.new( - reader, - result.dup do |r| - r.newer_than_or_equal = time - r.newer_than = nil - end, - ) + + Specification.new(reader, result.with(newer_than: nil, newer_than_or_equal: time)) end # Limits the query to events within given time range. @@ -123,7 +105,7 @@ def between(time_range) # # @return [Specification] def as_at - Specification.new(reader, result.dup { |r| r.time_sort_by = :as_at }) + Specification.new(reader, result.with(time_sort_by: :as_at)) end # Sets the order of time sorting using validity time @@ -131,7 +113,7 @@ def as_at # # @return [Specification] def as_of - Specification.new(reader, result.dup { |r| r.time_sort_by = :as_of }) + Specification.new(reader, result.with(time_sort_by: :as_of)) end # Sets the order of reading events to ascending (forward from the start). @@ -139,7 +121,7 @@ def as_of # # @return [Specification] def forward - Specification.new(reader, result.dup { |r| r.direction = :forward }) + Specification.new(reader, result.with(direction: :forward)) end # Sets the order of reading events to descending (backward from the start). @@ -147,7 +129,7 @@ def forward # # @return [Specification] def backward - Specification.new(reader, result.dup { |r| r.direction = :backward }) + Specification.new(reader, result.with(direction: :backward)) end # Limits the query to specified number of events. @@ -157,7 +139,8 @@ def backward # @return [Specification] def limit(count) raise InvalidPageSize unless count && count > 0 - Specification.new(reader, result.dup { |r| r.count = count }) + + Specification.new(reader, result.with(limit: count)) end # Executes the query based on the specification built up to this point. @@ -166,10 +149,10 @@ def limit(count) # # @yield [Array] batch of events # @return [Enumerator, nil] Enumerator is returned when block not given - def each_batch + def each_batch(&block) return to_enum(:each_batch) unless block_given? - reader.each(in_batches(result.batch_size).result) { |batch| yield batch } + reader.each(in_batches(result.batch_size).result, &block) end # Executes the query based on the specification built up to this point. @@ -178,10 +161,10 @@ def each_batch # # @yield [Event] event # @return [Enumerator, nil] Enumerator is returned when block not given - def each + def each(&block) return to_enum unless block_given? - each_batch { |batch| batch.each { |event| yield event } } + each_batch { |batch| batch.each(&block) } end # Executes the query based on the specification built up to this point @@ -191,6 +174,7 @@ def each # @return [Array] of mapped result def map(&block) raise ArgumentError.new("Block must be given") unless block_given? + each.map(&block) end @@ -202,6 +186,7 @@ def map(&block) # @return reduce result as defined by block given def reduce(accumulator = nil, &block) raise ArgumentError.new("Block must be given") unless block_given? + each.reduce(accumulator, &block) end @@ -236,13 +221,7 @@ def to_a # @param batch_size [Integer] number of events to read in a single batch # @return [Specification] def in_batches(batch_size = DEFAULT_BATCH_SIZE) - Specification.new( - reader, - result.dup do |r| - r.read_as = :batch - r.batch_size = batch_size - end, - ) + Specification.new(reader, result.with(read_as: :batch, batch_size: batch_size)) end alias in_batches_of in_batches @@ -251,7 +230,7 @@ def in_batches(batch_size = DEFAULT_BATCH_SIZE) # # @return [Specification] def read_first - Specification.new(reader, result.dup { |r| r.read_as = :first }) + Specification.new(reader, result.with(read_as: :first)) end # Specifies that only last event should be read. @@ -259,7 +238,7 @@ def read_first # # @return [Specification] def read_last - Specification.new(reader, result.dup { |r| r.read_as = :last }) + Specification.new(reader, result.with(read_as: :last)) end # Executes the query based on the specification built up to this point. @@ -286,9 +265,9 @@ def last # @types [Class, Array(Class)] types of event to look for. # @return [Specification] def of_type(*types) - Specification.new(reader, result.dup { |r| r.with_types = types.flatten }) + Specification.new(reader, result.with(with_types: types.flatten.map(&:to_s))) end - alias_method :of_types, :of_type + alias of_types of_type # Limits the query to certain events by given even ids. # {http://railseventstore.org/docs/read/ Find out more}. @@ -296,7 +275,7 @@ def of_type(*types) # @param event_ids [Array(String)] ids of event to look for. # @return [Specification] def with_id(event_ids) - Specification.new(reader, result.dup { |r| r.with_ids = event_ids }) + Specification.new(reader, result.with(with_ids: event_ids)) end # Reads single event from repository. diff --git a/ruby_event_store/lib/ruby_event_store/specification_result.rb b/ruby_event_store/lib/ruby_event_store/specification_result.rb index 0bb12cd180..2a4ac417ea 100644 --- a/ruby_event_store/lib/ruby_event_store/specification_result.rb +++ b/ruby_event_store/lib/ruby_event_store/specification_result.rb @@ -1,312 +1,180 @@ # frozen_string_literal: true module RubyEventStore - class SpecificationResult - def initialize( - direction: :forward, - start: nil, - stop: nil, - older_than: nil, - older_than_or_equal: nil, - newer_than: nil, - newer_than_or_equal: nil, - time_sort_by: nil, - count: nil, - stream: Stream.new(GLOBAL_STREAM), - read_as: :all, - batch_size: Specification::DEFAULT_BATCH_SIZE, - with_ids: nil, - with_types: nil - ) - @attributes = - Struct.new( - :direction, - :start, - :stop, - :older_than, - :older_than_or_equal, - :newer_than, - :newer_than_or_equal, - :time_sort_by, - :count, - :stream, - :read_as, - :batch_size, - :with_ids, - :with_types, - ).new( - direction, - start, - stop, - older_than, - older_than_or_equal, - newer_than, - newer_than_or_equal, - time_sort_by, - count, - stream, - read_as, - batch_size, - with_ids, - with_types, - ) - freeze + SpecificationResult = + Data.define( + :direction, + :start, + :stop, + :older_than, + :older_than_or_equal, + :newer_than, + :newer_than_or_equal, + :time_sort_by, + :limit, + :stream, + :read_as, + :batch_size, + :with_ids, + :with_types, + ) do + def initialize( + direction: :forward, + start: nil, + stop: nil, + older_than: nil, + older_than_or_equal: nil, + newer_than: nil, + newer_than_or_equal: nil, + time_sort_by: nil, + limit: nil, + stream: Stream.new(GLOBAL_STREAM), + read_as: :all, + batch_size: Specification::DEFAULT_BATCH_SIZE, + with_ids: nil, + with_types: nil + ) + super + end + + # Limited results. True if number of read elements are limited + # {http://railseventstore.org/docs/read/ Find out more}. + # + # @return [Boolean] + def limit? + !to_h[:limit].nil? + end + + # Results limit or infinity if limit not defined + # {http://railseventstore.org/docs/read/ Find out more}. + # + # @return [Integer|Infinity] + def limit = to_h[:limit] || Float::INFINITY + + # Stream definition. Stream to be read or nil + # {http://railseventstore.org/docs/read/ Find out more}. + # + # @return [Stream|nil] + # def stream = super + + # Starting position. Event id of starting event + # {http://railseventstore.org/docs/read/ Find out more}. + # + # @return [String] + # def start = super + + # Stop position. Event id of stopping event + # {http://railseventstore.org/docs/read/ Find out more}. + # + # @return [String|Symbol] + # def stop = super + + # Ending time. + # {http://railseventstore.org/docs/read/ Find out more}. + # + # @return [Time] + # def older_than = super + + # Ending time. + # {http://railseventstore.org/docs/read/ Find out more}. + # + # @return [Time] + # def older_than_or_equal = super + + # Starting time. + # {http://railseventstore.org/docs/read/ Find out more}. + # + # @return [Time] + # def newer_than = super + + # Starting time. + # {http://railseventstore.org/docs/read/ Find out more}. + # + # @return [Time] + # def newer_than_or_equal = super + + # Time sorting strategy. Nil when not specified. + # {http://railseventstore.org/docs/read/ Find out more}. + # + # @return [Symbol] + # def time_sort_by = super + + # Read direction. True is reading forward + # {http://railseventstore.org/docs/read/ Find out more}. + # + # @return [Boolean] + def forward? = direction.equal?(:forward) + + # Read direction. True is reading backward + # {http://railseventstore.org/docs/read/ Find out more}. + # + # @return [Boolean] + def backward? = direction.equal?(:backward) + + # Size of batch to read (only for :batch read strategy) + # {http://railseventstore.org/docs/read/ Find out more}. + # + # @return [Integer] + # def batch_size = super + + # Ids of specified event to be read (if any given) + # {http://railseventstore.org/docs/read/ Find out more}. + # + # @return [Array|nil] + # def with_ids = super + + # Read by specified ids. True if event ids have been specified. + # {http://railseventstore.org/docs/read/ Find out more}. + # + # @return [Boolean] + def with_ids? = !with_ids.nil? + + # Event types to be read (if any given) + # {http://railseventstore.org/docs/read/ Find out more}. + # + # @return [Array|nil] + # def with_types = self.with_types&.map(&:to_s) + + # Read by specified event types. True if event types have been specified. + # {http://railseventstore.org/docs/read/ Find out more}. + # + # @return [Boolean] + def with_types? = !(with_types || []).empty? + + # Read strategy. True if items will be read in batches + # {http://railseventstore.org/docs/read/ Find out more}. + # + # @return [Boolean] + def batched? = read_as.equal?(:batch) + + # Read strategy. True if first item will be read + # {http://railseventstore.org/docs/read/ Find out more}. + # + # @return [Boolean] + def first? = read_as.equal?(:first) + + # Read strategy. True if last item will be read + # {http://railseventstore.org/docs/read/ Find out more}. + # + # @return [Boolean] + def last? = read_as.equal?(:last) + + # Read strategy. True if all items will be read + # {http://railseventstore.org/docs/read/ Find out more}. + # + # @return [Boolean] + def all? = read_as.equal?(:all) + + # Read strategy. True if results will be sorted by timestamp + # {http://railseventstore.org/docs/read/ Find out more}. + # + # @return [Boolean] + def time_sort_by_as_at? = time_sort_by.equal?(:as_at) + + # Read strategy. True if results will be sorted by valid_at + # {http://railseventstore.org/docs/read/ Find out more}. + # + # @return [Boolean] + def time_sort_by_as_of? = time_sort_by.equal?(:as_of) end - - # Limited results. True if number of read elements are limited - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Boolean] - def limit? - !attributes.count.nil? - end - - # Results limit or infinity if limit not defined - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Integer|Infinity] - def limit - attributes.count || Float::INFINITY - end - - # Stream definition. Stream to be read or nil - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Stream|nil] - def stream - attributes.stream - end - - # Starting position. Event id of starting event - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [String] - def start - attributes.start - end - - # Stop position. Event id of stopping event - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [String|Symbol] - def stop - attributes.stop - end - - # Ending time. - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Time] - def older_than - attributes.older_than - end - - # Ending time. - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Time] - def older_than_or_equal - attributes.older_than_or_equal - end - - # Starting time. - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Time] - def newer_than - attributes.newer_than - end - - # Starting time. - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Time] - def newer_than_or_equal - attributes.newer_than_or_equal - end - - # Time sorting strategy. Nil when not specified. - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Symbol] - def time_sort_by - attributes.time_sort_by - end - - # Read direction. True is reading forward - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Boolean] - def forward? - get_direction.equal?(:forward) - end - - # Read direction. True is reading backward - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Boolean] - def backward? - get_direction.equal?(:backward) - end - - # Size of batch to read (only for :batch read strategy) - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Integer] - def batch_size - attributes.batch_size - end - - # Ids of specified event to be read (if any given) - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Array|nil] - def with_ids - attributes.with_ids - end - - # Read by specified ids. True if event ids have been specified. - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Boolean] - def with_ids? - !with_ids.nil? - end - - # Event types to be read (if any given) - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Array|nil] - def with_types - attributes.with_types&.map(&:to_s) - end - - # Read by specified event types. True if event types have been specified. - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Boolean] - def with_types? - !(with_types || []).empty? - end - - # Read strategy. True if items will be read in batches - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Boolean] - def batched? - attributes.read_as.equal?(:batch) - end - - # Read strategy. True if first item will be read - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Boolean] - def first? - attributes.read_as.equal?(:first) - end - - # Read strategy. True if last item will be read - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Boolean] - def last? - attributes.read_as.equal?(:last) - end - - # Read strategy. True if all items will be read - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Boolean] - def all? - attributes.read_as.equal?(:all) - end - - # Read strategy. True if results will be sorted by timestamp - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Boolean] - def time_sort_by_as_at? - time_sort_by.equal?(:as_at) - end - - # Read strategy. True if results will be sorted by valid_at - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Boolean] - def time_sort_by_as_of? - time_sort_by.equal?(:as_of) - end - - # Clone [SpecificationResult] - # If block is given cloned attributes might be modified. - # - # @return [SpecificationResult] - def dup - new_attributes = attributes.dup - yield new_attributes if block_given? - SpecificationResult.new(**new_attributes.to_h) - end - - # Two specification attributess are equal if: - # * they are of the same class - # * have identical data (verified with eql? method) - # - # @param other_spec [SpecificationResult, Object] object to compare - # - # @return [TrueClass, FalseClass] - def ==(other_spec) - other_spec.hash.eql?(hash) - end - - # Generates a Fixnum hash value for this object. This function - # have the property that a.eql?(b) implies a.hash == b.hash. - # - # The hash value is used along with eql? by the Hash class to - # determine if two objects reference the same hash key. - # - # This hash is based on - # * class - # * direction - # * start - # * stop - # * older_than - # * older_than_or_equal - # * newer_than - # * newer_than_or_equal - # * time_sort_by - # * count - # * stream - # * read_as - # * batch_size - # * with_ids - # * with_types - # - # @return [Integer] - def hash - [ - get_direction, - start, - stop, - older_than, - older_than_or_equal, - newer_than, - newer_than_or_equal, - time_sort_by, - limit, - stream, - attributes.read_as, - batch_size, - with_ids, - with_types, - ].hash ^ self.class.hash - end - - private - - attr_reader :attributes - - def get_direction - attributes.direction - end - end end From 7805b1b9e4861b999acee1f8939f7f98cf931958 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Pacana?= Date: Thu, 2 Oct 2025 13:04:29 +0200 Subject: [PATCH 2/9] This is internal API --- .../ruby_event_store/specification_result.rb | 129 +----------------- 1 file changed, 1 insertion(+), 128 deletions(-) diff --git a/ruby_event_store/lib/ruby_event_store/specification_result.rb b/ruby_event_store/lib/ruby_event_store/specification_result.rb index 2a4ac417ea..d14e70aa87 100644 --- a/ruby_event_store/lib/ruby_event_store/specification_result.rb +++ b/ruby_event_store/lib/ruby_event_store/specification_result.rb @@ -37,144 +37,17 @@ def initialize( super end - # Limited results. True if number of read elements are limited - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Boolean] - def limit? - !to_h[:limit].nil? - end - - # Results limit or infinity if limit not defined - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Integer|Infinity] + def limit? = !to_h[:limit].nil? def limit = to_h[:limit] || Float::INFINITY - - # Stream definition. Stream to be read or nil - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Stream|nil] - # def stream = super - - # Starting position. Event id of starting event - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [String] - # def start = super - - # Stop position. Event id of stopping event - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [String|Symbol] - # def stop = super - - # Ending time. - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Time] - # def older_than = super - - # Ending time. - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Time] - # def older_than_or_equal = super - - # Starting time. - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Time] - # def newer_than = super - - # Starting time. - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Time] - # def newer_than_or_equal = super - - # Time sorting strategy. Nil when not specified. - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Symbol] - # def time_sort_by = super - - # Read direction. True is reading forward - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Boolean] def forward? = direction.equal?(:forward) - - # Read direction. True is reading backward - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Boolean] def backward? = direction.equal?(:backward) - - # Size of batch to read (only for :batch read strategy) - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Integer] - # def batch_size = super - - # Ids of specified event to be read (if any given) - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Array|nil] - # def with_ids = super - - # Read by specified ids. True if event ids have been specified. - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Boolean] def with_ids? = !with_ids.nil? - - # Event types to be read (if any given) - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Array|nil] - # def with_types = self.with_types&.map(&:to_s) - - # Read by specified event types. True if event types have been specified. - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Boolean] def with_types? = !(with_types || []).empty? - - # Read strategy. True if items will be read in batches - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Boolean] def batched? = read_as.equal?(:batch) - - # Read strategy. True if first item will be read - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Boolean] def first? = read_as.equal?(:first) - - # Read strategy. True if last item will be read - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Boolean] def last? = read_as.equal?(:last) - - # Read strategy. True if all items will be read - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Boolean] def all? = read_as.equal?(:all) - - # Read strategy. True if results will be sorted by timestamp - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Boolean] def time_sort_by_as_at? = time_sort_by.equal?(:as_at) - - # Read strategy. True if results will be sorted by valid_at - # {http://railseventstore.org/docs/read/ Find out more}. - # - # @return [Boolean] def time_sort_by_as_of? = time_sort_by.equal?(:as_of) end end From e65d0bd0ee53f4ef8506b60cedba16d60796a4e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Pacana?= Date: Thu, 2 Oct 2025 13:06:27 +0200 Subject: [PATCH 3/9] Refactor --- ruby_event_store/lib/ruby_event_store.rb | 1 - .../lib/ruby_event_store/specification.rb | 52 ++++++++++++++++++ .../ruby_event_store/specification_result.rb | 53 ------------------- 3 files changed, 52 insertions(+), 54 deletions(-) delete mode 100644 ruby_event_store/lib/ruby_event_store/specification_result.rb diff --git a/ruby_event_store/lib/ruby_event_store.rb b/ruby_event_store/lib/ruby_event_store.rb index f57650bf2e..32661cf828 100644 --- a/ruby_event_store/lib/ruby_event_store.rb +++ b/ruby_event_store/lib/ruby_event_store.rb @@ -10,7 +10,6 @@ require_relative "ruby_event_store/client" require_relative "ruby_event_store/metadata" require_relative "ruby_event_store/specification" -require_relative "ruby_event_store/specification_result" require_relative "ruby_event_store/specification_reader" require_relative "ruby_event_store/event" require_relative "ruby_event_store/stream" diff --git a/ruby_event_store/lib/ruby_event_store/specification.rb b/ruby_event_store/lib/ruby_event_store/specification.rb index 8744f1b3ef..5b7e85371b 100644 --- a/ruby_event_store/lib/ruby_event_store/specification.rb +++ b/ruby_event_store/lib/ruby_event_store/specification.rb @@ -3,6 +3,56 @@ module RubyEventStore # Used for building and executing the query specification. class Specification + Result = + Data.define( + :direction, + :start, + :stop, + :older_than, + :older_than_or_equal, + :newer_than, + :newer_than_or_equal, + :time_sort_by, + :limit, + :stream, + :read_as, + :batch_size, + :with_ids, + :with_types, + ) do + def initialize( + direction: :forward, + start: nil, + stop: nil, + older_than: nil, + older_than_or_equal: nil, + newer_than: nil, + newer_than_or_equal: nil, + time_sort_by: nil, + limit: nil, + stream: Stream.new(GLOBAL_STREAM), + read_as: :all, + batch_size: Specification::DEFAULT_BATCH_SIZE, + with_ids: nil, + with_types: nil + ) + super + end + + def limit? = !to_h[:limit].nil? + def limit = to_h[:limit] || Float::INFINITY + def forward? = direction.equal?(:forward) + def backward? = direction.equal?(:backward) + def with_ids? = !with_ids.nil? + def with_types? = !(with_types || []).empty? + def batched? = read_as.equal?(:batch) + def first? = read_as.equal?(:first) + def last? = read_as.equal?(:last) + def all? = read_as.equal?(:all) + def time_sort_by_as_at? = time_sort_by.equal?(:as_at) + def time_sort_by_as_of? = time_sort_by.equal?(:as_of) + end + DEFAULT_BATCH_SIZE = 100 # @api private @@ -315,4 +365,6 @@ def events(event_ids) attr_reader :reader end + + SpecificationResult = Specification::Result end diff --git a/ruby_event_store/lib/ruby_event_store/specification_result.rb b/ruby_event_store/lib/ruby_event_store/specification_result.rb deleted file mode 100644 index d14e70aa87..0000000000 --- a/ruby_event_store/lib/ruby_event_store/specification_result.rb +++ /dev/null @@ -1,53 +0,0 @@ -# frozen_string_literal: true - -module RubyEventStore - SpecificationResult = - Data.define( - :direction, - :start, - :stop, - :older_than, - :older_than_or_equal, - :newer_than, - :newer_than_or_equal, - :time_sort_by, - :limit, - :stream, - :read_as, - :batch_size, - :with_ids, - :with_types, - ) do - def initialize( - direction: :forward, - start: nil, - stop: nil, - older_than: nil, - older_than_or_equal: nil, - newer_than: nil, - newer_than_or_equal: nil, - time_sort_by: nil, - limit: nil, - stream: Stream.new(GLOBAL_STREAM), - read_as: :all, - batch_size: Specification::DEFAULT_BATCH_SIZE, - with_ids: nil, - with_types: nil - ) - super - end - - def limit? = !to_h[:limit].nil? - def limit = to_h[:limit] || Float::INFINITY - def forward? = direction.equal?(:forward) - def backward? = direction.equal?(:backward) - def with_ids? = !with_ids.nil? - def with_types? = !(with_types || []).empty? - def batched? = read_as.equal?(:batch) - def first? = read_as.equal?(:first) - def last? = read_as.equal?(:last) - def all? = read_as.equal?(:all) - def time_sort_by_as_at? = time_sort_by.equal?(:as_at) - def time_sort_by_as_of? = time_sort_by.equal?(:as_of) - end -end From 7ba66db514da63ee50b89225840382310b4eb5e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Pacana?= Date: Thu, 2 Oct 2025 13:10:15 +0200 Subject: [PATCH 4/9] Simpler --- .../lib/ruby_event_store/specification.rb | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/ruby_event_store/lib/ruby_event_store/specification.rb b/ruby_event_store/lib/ruby_event_store/specification.rb index 5b7e85371b..cb087c49d7 100644 --- a/ruby_event_store/lib/ruby_event_store/specification.rb +++ b/ruby_event_store/lib/ruby_event_store/specification.rb @@ -20,31 +20,12 @@ class Specification :with_ids, :with_types, ) do - def initialize( - direction: :forward, - start: nil, - stop: nil, - older_than: nil, - older_than_or_equal: nil, - newer_than: nil, - newer_than_or_equal: nil, - time_sort_by: nil, - limit: nil, - stream: Stream.new(GLOBAL_STREAM), - read_as: :all, - batch_size: Specification::DEFAULT_BATCH_SIZE, - with_ids: nil, - with_types: nil - ) - super - end - def limit? = !to_h[:limit].nil? def limit = to_h[:limit] || Float::INFINITY - def forward? = direction.equal?(:forward) - def backward? = direction.equal?(:backward) def with_ids? = !with_ids.nil? def with_types? = !(with_types || []).empty? + def forward? = direction.equal?(:forward) + def backward? = direction.equal?(:backward) def batched? = read_as.equal?(:batch) def first? = read_as.equal?(:first) def last? = read_as.equal?(:last) @@ -57,7 +38,25 @@ def time_sort_by_as_of? = time_sort_by.equal?(:as_of) # @api private # @private - def initialize(reader, result = SpecificationResult.new) + def initialize( + reader, + result = SpecificationResult.new( + direction: :forward, + start: nil, + stop: nil, + older_than: nil, + older_than_or_equal: nil, + newer_than: nil, + newer_than_or_equal: nil, + time_sort_by: nil, + limit: nil, + stream: Stream.new(GLOBAL_STREAM), + read_as: :all, + batch_size: DEFAULT_BATCH_SIZE, + with_ids: nil, + with_types: nil, + ) + ) @reader = reader @result = result end From 281e7a9deea53fc1b819a75e44c884f1327446db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Pacana?= Date: Thu, 2 Oct 2025 13:13:13 +0200 Subject: [PATCH 5/9] No point in testing core use of Data --- ruby_event_store/spec/specification_spec.rb | 91 --------------------- 1 file changed, 91 deletions(-) diff --git a/ruby_event_store/spec/specification_spec.rb b/ruby_event_store/spec/specification_spec.rb index b2ab5312dd..a8f4a532d9 100644 --- a/ruby_event_store/spec/specification_spec.rb +++ b/ruby_event_store/spec/specification_spec.rb @@ -644,97 +644,6 @@ module RubyEventStore specify { expect(specification.result.frozen?).to be(true) } specify { expect(specification.backward.result.frozen?).to be(true) } - specify "#hash" do - expect(specification.result.hash).to eq(specification.forward.result.hash) - expect(specification.forward.result.hash).not_to eq(specification.backward.result.hash) - - expect(specification.read_first.result.hash).to eq(specification.read_first.result.hash) - expect(specification.read_last.result.hash).to eq(specification.read_last.result.hash) - expect(specification.read_first.result.hash).not_to eq(specification.read_last.result.hash) - - expect(specification.result.hash).not_to eq(specification.limit(10).result.hash) - expect(specification.in_batches.result.hash).to eq( - specification.in_batches(Specification::DEFAULT_BATCH_SIZE).result.hash, - ) - expect(specification.in_batches.result.hash).not_to eq(specification.in_batches(10).result.hash) - expect(specification.result.hash).to eq(specification.stream(GLOBAL_STREAM).result.hash) - expect(specification.result.hash).not_to eq(specification.stream("dummy").result.hash) - - expect(specification.with_id(event_id).result.hash).to eq(specification.with_id(event_id).result.hash) - expect(specification.with_id(event_id).result.hash).not_to eq( - specification.with_id(SecureRandom.uuid).result.hash, - ) - - expect(specification.of_type([TestEvent]).result.hash).to eq(specification.of_type([TestEvent]).result.hash) - expect(specification.of_type([TestEvent]).result.hash).not_to eq( - specification.of_type([OrderCreated]).result.hash, - ) - - expect(specification.result.hash).not_to eq(specification.as_at.result.hash) - expect(specification.result.hash).not_to eq(specification.as_of.result.hash) - expect(specification.as_at.result.hash).not_to eq(specification.as_of.result.hash) - - with_event_of_id(event_id) do - expect(specification.from(event_id).result.hash).not_to eq(specification.result.hash) - expect(specification.to(event_id).result.hash).not_to eq(specification.result.hash) - end - - expect(specification.older_than(target_date).result.hash).not_to eq(specification.result.hash) - expect(specification.older_than_or_equal(target_date).result.hash).not_to eq(specification.result.hash) - expect(specification.newer_than(target_date).result.hash).not_to eq(specification.result.hash) - expect(specification.newer_than_or_equal(target_date).result.hash).not_to eq(specification.result.hash) - - expect(specification.result.hash).not_to eq( - [ - SpecificationResult, - :forward, - nil, - nil, - nil, - nil, - nil, - nil, - nil, - Float::INFINITY, - Stream.new(GLOBAL_STREAM), - :all, - Specification::DEFAULT_BATCH_SIZE, - nil, - nil, - ].hash, - ) - - expect(Class.new(SpecificationResult).new.hash).not_to eq(specification.result.hash) - end - - specify "#eql?" do - expect(specification.result).to eq(specification.forward.result) - expect(specification.forward.result).not_to eq(specification.backward.result) - - expect(specification.read_first.result).to eq(specification.read_first.result) - expect(specification.read_last.result).to eq(specification.read_last.result) - expect(specification.read_first.result).not_to eq(specification.read_last.result) - - expect(specification.result).not_to eq(specification.limit(10).result) - expect(specification.in_batches.result).to eq(specification.in_batches(Specification::DEFAULT_BATCH_SIZE).result) - expect(specification.in_batches.result).not_to eq(specification.in_batches(10).result) - expect(specification.result).to eq(specification.stream(GLOBAL_STREAM).result) - expect(specification.result).not_to eq(specification.stream("dummy").result) - - expect(specification.with_id(event_id).result).to eq(specification.with_id(event_id).result) - expect(specification.with_id(event_id).result).not_to eq(specification.with_id(SecureRandom.uuid).result) - - with_event_of_id(event_id) do - expect(specification.from(event_id).result).not_to eq(specification.result) - expect(specification.older_than(target_date).result).not_to eq(specification.result) - end - end - - specify "#dup" do - expect(specification.result.dup).to eq(specification.result) - specification.result.dup { |result| expect(result.object_id).not_to eq(specification.result.object_id) } - end - specify "#count" do expect(specification.count).to eq(0) (1..3).each { repository.append_to_stream([test_record], Stream.new(stream_name), ExpectedVersion.any) } From 34e9da976351e5ad27940e580aee171be9079daf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Pacana?= Date: Thu, 2 Oct 2025 13:16:21 +0200 Subject: [PATCH 6/9] Refactor --- ruby_event_store/lib/ruby_event_store/specification.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ruby_event_store/lib/ruby_event_store/specification.rb b/ruby_event_store/lib/ruby_event_store/specification.rb index cb087c49d7..cb90f11995 100644 --- a/ruby_event_store/lib/ruby_event_store/specification.rb +++ b/ruby_event_store/lib/ruby_event_store/specification.rb @@ -20,8 +20,7 @@ class Specification :with_ids, :with_types, ) do - def limit? = !to_h[:limit].nil? - def limit = to_h[:limit] || Float::INFINITY + def limit? = !limit.infinite? def with_ids? = !with_ids.nil? def with_types? = !(with_types || []).empty? def forward? = direction.equal?(:forward) @@ -49,7 +48,7 @@ def initialize( newer_than: nil, newer_than_or_equal: nil, time_sort_by: nil, - limit: nil, + limit: Float::INFINITY, stream: Stream.new(GLOBAL_STREAM), read_as: :all, batch_size: DEFAULT_BATCH_SIZE, @@ -189,7 +188,7 @@ def backward def limit(count) raise InvalidPageSize unless count && count > 0 - Specification.new(reader, result.with(limit: count)) + Specification.new(reader, result.with(limit: count || Float::INFINITY)) end # Executes the query based on the specification built up to this point. From 4994472876ff5dc48566266519c031139ec9792d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Pacana?= Date: Thu, 2 Oct 2025 13:24:12 +0200 Subject: [PATCH 7/9] Simpler --- ruby_event_store/lib/ruby_event_store/specification.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby_event_store/lib/ruby_event_store/specification.rb b/ruby_event_store/lib/ruby_event_store/specification.rb index cb90f11995..57c249db95 100644 --- a/ruby_event_store/lib/ruby_event_store/specification.rb +++ b/ruby_event_store/lib/ruby_event_store/specification.rb @@ -188,7 +188,7 @@ def backward def limit(count) raise InvalidPageSize unless count && count > 0 - Specification.new(reader, result.with(limit: count || Float::INFINITY)) + Specification.new(reader, result.with(limit: count)) end # Executes the query based on the specification built up to this point. From 740764f56335077ff5d7b685a0d589b1b964304b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Pacana?= Date: Thu, 2 Oct 2025 13:33:44 +0200 Subject: [PATCH 8/9] Refactor --- ruby_event_store/lib/ruby_event_store/specification.rb | 5 +++-- ruby_event_store/spec/specification_spec.rb | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ruby_event_store/lib/ruby_event_store/specification.rb b/ruby_event_store/lib/ruby_event_store/specification.rb index 57c249db95..51d0923d5a 100644 --- a/ruby_event_store/lib/ruby_event_store/specification.rb +++ b/ruby_event_store/lib/ruby_event_store/specification.rb @@ -22,7 +22,7 @@ class Specification ) do def limit? = !limit.infinite? def with_ids? = !with_ids.nil? - def with_types? = !(with_types || []).empty? + def with_types? = !with_types.nil? def forward? = direction.equal?(:forward) def backward? = direction.equal?(:backward) def batched? = read_as.equal?(:batch) @@ -313,7 +313,8 @@ def last # @types [Class, Array(Class)] types of event to look for. # @return [Specification] def of_type(*types) - Specification.new(reader, result.with(with_types: types.flatten.map(&:to_s))) + types_ = types.flatten.map(&:to_s) unless types.empty? + Specification.new(reader, result.with(with_types: types_)) end alias of_types of_type diff --git a/ruby_event_store/spec/specification_spec.rb b/ruby_event_store/spec/specification_spec.rb index a8f4a532d9..942f82b782 100644 --- a/ruby_event_store/spec/specification_spec.rb +++ b/ruby_event_store/spec/specification_spec.rb @@ -68,6 +68,7 @@ module RubyEventStore specify { expect(specification.with_id([]).result.with_ids?).to be(true) } specify { expect(specification.result.with_types).to be_nil } + specify { expect(specification.of_type.result.with_types).to be_nil } specify { expect(specification.of_type([TestEvent]).result.with_types).to eq(["TestEvent"]) } specify { expect(specification.result.with_types?).to be(false) } specify { expect(specification.of_type([TestEvent]).result.with_types?).to be(true) } From b61b4cb9462ffba5d9f515ee54c57e9457514b44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Pacana?= Date: Thu, 2 Oct 2025 13:37:32 +0200 Subject: [PATCH 9/9] Breaking internals is not breaking --- ruby_event_store/lib/ruby_event_store/specification.rb | 7 +++---- ruby_event_store/spec/specification_spec.rb | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/ruby_event_store/lib/ruby_event_store/specification.rb b/ruby_event_store/lib/ruby_event_store/specification.rb index 51d0923d5a..cf0401ec34 100644 --- a/ruby_event_store/lib/ruby_event_store/specification.rb +++ b/ruby_event_store/lib/ruby_event_store/specification.rb @@ -22,7 +22,7 @@ class Specification ) do def limit? = !limit.infinite? def with_ids? = !with_ids.nil? - def with_types? = !with_types.nil? + def with_types? = !with_types.empty? def forward? = direction.equal?(:forward) def backward? = direction.equal?(:backward) def batched? = read_as.equal?(:batch) @@ -53,7 +53,7 @@ def initialize( read_as: :all, batch_size: DEFAULT_BATCH_SIZE, with_ids: nil, - with_types: nil, + with_types: [], ) ) @reader = reader @@ -313,8 +313,7 @@ def last # @types [Class, Array(Class)] types of event to look for. # @return [Specification] def of_type(*types) - types_ = types.flatten.map(&:to_s) unless types.empty? - Specification.new(reader, result.with(with_types: types_)) + Specification.new(reader, result.with(with_types: types.flatten.map(&:to_s))) end alias of_types of_type diff --git a/ruby_event_store/spec/specification_spec.rb b/ruby_event_store/spec/specification_spec.rb index 942f82b782..7535ef5140 100644 --- a/ruby_event_store/spec/specification_spec.rb +++ b/ruby_event_store/spec/specification_spec.rb @@ -67,8 +67,8 @@ module RubyEventStore specify { expect(specification.with_id([event_id]).result.with_ids?).to be(true) } specify { expect(specification.with_id([]).result.with_ids?).to be(true) } - specify { expect(specification.result.with_types).to be_nil } - specify { expect(specification.of_type.result.with_types).to be_nil } + specify { expect(specification.result.with_types).to eq([]) } + specify { expect(specification.of_type.result.with_types).to eq([]) } specify { expect(specification.of_type([TestEvent]).result.with_types).to eq(["TestEvent"]) } specify { expect(specification.result.with_types?).to be(false) } specify { expect(specification.of_type([TestEvent]).result.with_types?).to be(true) }