ruby_event_store 2.1.0 → 2.4.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/lib/ruby_event_store/batch_enumerator.rb +3 -3
- data/lib/ruby_event_store/broker.rb +5 -4
- data/lib/ruby_event_store/client.rb +81 -48
- data/lib/ruby_event_store/composed_dispatcher.rb +1 -3
- data/lib/ruby_event_store/correlated_commands.rb +4 -15
- data/lib/ruby_event_store/errors.rb +11 -10
- data/lib/ruby_event_store/event.rb +9 -14
- data/lib/ruby_event_store/expected_version.rb +3 -7
- data/lib/ruby_event_store/in_memory_repository.rb +100 -37
- data/lib/ruby_event_store/instrumented_dispatcher.rb +11 -2
- data/lib/ruby_event_store/instrumented_repository.rb +13 -8
- data/lib/ruby_event_store/link_by_metadata.rb +4 -21
- data/lib/ruby_event_store/mappers/default.rb +6 -4
- data/lib/ruby_event_store/mappers/encryption_key.rb +7 -16
- data/lib/ruby_event_store/mappers/encryption_mapper.rb +6 -6
- data/lib/ruby_event_store/mappers/forgotten_data.rb +1 -1
- data/lib/ruby_event_store/mappers/in_memory_encryption_key_repository.rb +1 -1
- data/lib/ruby_event_store/mappers/null_mapper.rb +0 -1
- data/lib/ruby_event_store/mappers/pipeline.rb +3 -10
- data/lib/ruby_event_store/mappers/pipeline_mapper.rb +1 -0
- data/lib/ruby_event_store/mappers/transformation/domain_event.rb +22 -12
- data/lib/ruby_event_store/mappers/transformation/encryption.rb +21 -25
- data/lib/ruby_event_store/mappers/transformation/event_class_remapper.rb +6 -5
- data/lib/ruby_event_store/mappers/transformation/stringify_metadata_keys.rb +6 -5
- data/lib/ruby_event_store/mappers/transformation/symbolize_metadata_keys.rb +6 -5
- data/lib/ruby_event_store/mappers/transformation/upcast.rb +2 -6
- data/lib/ruby_event_store/metadata.rb +46 -17
- data/lib/ruby_event_store/projection.rb +12 -20
- data/lib/ruby_event_store/record.rb +14 -26
- data/lib/ruby_event_store/serialized_record.rb +14 -26
- data/lib/ruby_event_store/serializers/yaml.rb +17 -0
- data/lib/ruby_event_store/spec/broker_lint.rb +38 -28
- data/lib/ruby_event_store/spec/event_lint.rb +10 -10
- data/lib/ruby_event_store/spec/event_repository_lint.rb +746 -730
- data/lib/ruby_event_store/spec/mapper_lint.rb +2 -2
- data/lib/ruby_event_store/spec/subscriptions_lint.rb +58 -68
- data/lib/ruby_event_store/specification.rb +20 -16
- data/lib/ruby_event_store/specification_reader.rb +2 -3
- data/lib/ruby_event_store/specification_result.rb +52 -46
- data/lib/ruby_event_store/stream.rb +3 -7
- data/lib/ruby_event_store/subscriptions.rb +17 -17
- data/lib/ruby_event_store/transform_keys.rb +1 -1
- data/lib/ruby_event_store/version.rb +1 -1
- data/lib/ruby_event_store.rb +44 -43
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 21f9e0f89d5ab86baa4f840b70c2093d518e8077ee8380cce54144c0f123aaf0
|
4
|
+
data.tar.gz: 9927020649c8f492843ce48af29b020e7831efae54a949cb2e8078288254bed9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4763bae7c34f07594ff9757f6482de78c84550bec861210e89930624438025ad0882bb7476eba0f1b511b02b2ff17c63c82a39b9ab065586dfc2704e6b21b6ba
|
7
|
+
data.tar.gz: 7ed29eadf34c3b9c3e22169250dfc5c9da81f2fa6f470c57c39313f79125c14ea57f2fd0a2c30715a915417ae84eb1b69bd8ec06a6b01de94e5bc000bea2f8ba
|
@@ -3,9 +3,9 @@
|
|
3
3
|
module RubyEventStore
|
4
4
|
class BatchEnumerator
|
5
5
|
def initialize(batch_size, total_limit, reader)
|
6
|
-
@batch_size
|
6
|
+
@batch_size = batch_size
|
7
7
|
@total_limit = total_limit
|
8
|
-
@reader
|
8
|
+
@reader = reader
|
9
9
|
end
|
10
10
|
|
11
11
|
def each
|
@@ -13,7 +13,7 @@ module RubyEventStore
|
|
13
13
|
|
14
14
|
0.step(total_limit - 1, batch_size) do |batch_offset|
|
15
15
|
batch_limit = [batch_size, total_limit - batch_offset].min
|
16
|
-
result
|
16
|
+
result = reader.call(batch_offset, batch_limit)
|
17
17
|
|
18
18
|
break if result.empty?
|
19
19
|
yield result
|
@@ -9,9 +9,7 @@ module RubyEventStore
|
|
9
9
|
|
10
10
|
def call(event, record)
|
11
11
|
subscribers = subscriptions.all_for(event.event_type)
|
12
|
-
subscribers.each
|
13
|
-
dispatcher.call(subscriber, event, record)
|
14
|
-
end
|
12
|
+
subscribers.each { |subscriber| dispatcher.call(subscriber, event, record) }
|
15
13
|
end
|
16
14
|
|
17
15
|
def add_subscription(subscriber, event_types)
|
@@ -35,11 +33,14 @@ module RubyEventStore
|
|
35
33
|
end
|
36
34
|
|
37
35
|
private
|
36
|
+
|
38
37
|
attr_reader :dispatcher, :subscriptions
|
39
38
|
|
40
39
|
def verify_subscription(subscriber)
|
41
40
|
raise SubscriberNotExist, "subscriber must be first argument or block" unless subscriber
|
42
|
-
|
41
|
+
unless dispatcher.verify(subscriber)
|
42
|
+
raise InvalidHandler.new("Handler #{subscriber} is invalid for dispatcher #{dispatcher}")
|
43
|
+
end
|
43
44
|
end
|
44
45
|
end
|
45
46
|
end
|
@@ -1,27 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "concurrent"
|
4
4
|
|
5
5
|
module RubyEventStore
|
6
6
|
class Client
|
7
|
-
def initialize(
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
@repository
|
16
|
-
@mapper
|
17
|
-
@subscriptions
|
18
|
-
@broker
|
19
|
-
@clock
|
20
|
-
@metadata
|
7
|
+
def initialize(
|
8
|
+
repository:,
|
9
|
+
mapper: Mappers::Default.new,
|
10
|
+
subscriptions: Subscriptions.new,
|
11
|
+
dispatcher: Dispatcher.new,
|
12
|
+
clock: default_clock,
|
13
|
+
correlation_id_generator: default_correlation_id_generator
|
14
|
+
)
|
15
|
+
@repository = repository
|
16
|
+
@mapper = mapper
|
17
|
+
@subscriptions = subscriptions
|
18
|
+
@broker = Broker.new(subscriptions: subscriptions, dispatcher: dispatcher)
|
19
|
+
@clock = clock
|
20
|
+
@metadata = Concurrent::ThreadLocalVar.new
|
21
21
|
@correlation_id_generator = correlation_id_generator
|
22
22
|
end
|
23
23
|
|
24
|
-
|
25
24
|
# Persists events and notifies subscribed handlers about them
|
26
25
|
#
|
27
26
|
# @param events [Array<Event>, Event] event(s)
|
@@ -30,13 +29,10 @@ module RubyEventStore
|
|
30
29
|
# @return [self]
|
31
30
|
def publish(events, stream_name: GLOBAL_STREAM, expected_version: :any)
|
32
31
|
enriched_events = enrich_events_metadata(events)
|
33
|
-
records
|
32
|
+
records = transform(enriched_events)
|
34
33
|
append_records_to_stream(records, stream_name: stream_name, expected_version: expected_version)
|
35
34
|
enriched_events.zip(records) do |event, record|
|
36
|
-
with_metadata(
|
37
|
-
correlation_id: event.metadata.fetch(:correlation_id),
|
38
|
-
causation_id: event.event_id,
|
39
|
-
) do
|
35
|
+
with_metadata(correlation_id: event.metadata.fetch(:correlation_id), causation_id: event.event_id) do
|
40
36
|
broker.(event, record)
|
41
37
|
end
|
42
38
|
end
|
@@ -94,6 +90,43 @@ module RubyEventStore
|
|
94
90
|
repository.streams_of(event_id)
|
95
91
|
end
|
96
92
|
|
93
|
+
# Gets position of the event in given stream
|
94
|
+
#
|
95
|
+
# The position is always nonnegative.
|
96
|
+
# Returns nil if the event has no specific position in stream.
|
97
|
+
# Raise error if event is not present in stream.
|
98
|
+
#
|
99
|
+
# @param event_id [String]
|
100
|
+
# @param stream_name [String]
|
101
|
+
# @return [Integer] nonnegative integer position of event in stream
|
102
|
+
# @raise [EventNotInStream]
|
103
|
+
def position_in_stream(event_id, stream_name)
|
104
|
+
repository.position_in_stream(event_id, Stream.new(stream_name))
|
105
|
+
end
|
106
|
+
|
107
|
+
# Gets position of the event in global stream
|
108
|
+
#
|
109
|
+
# The position is always nonnegative.
|
110
|
+
# Global position may have gaps, meaning, there may be event at
|
111
|
+
# position 40, but no event at position 39.
|
112
|
+
#
|
113
|
+
# @param event_id [String]
|
114
|
+
# @raise [EventNotFound]
|
115
|
+
# @return [Integer] nonnegno ative integer position of event in global stream
|
116
|
+
def global_position(event_id)
|
117
|
+
repository.global_position(event_id)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Checks whether event is linked in given stream
|
121
|
+
#
|
122
|
+
# @param event_id [String]
|
123
|
+
# @param stream_name [String]
|
124
|
+
# @return [Boolean] true if event is linked to given stream, false otherwise
|
125
|
+
def event_in_stream?(event_id, stream_name)
|
126
|
+
stream = Stream.new(stream_name)
|
127
|
+
stream.global? ? repository.has_event?(event_id) : repository.event_in_stream?(event_id, stream)
|
128
|
+
end
|
129
|
+
|
97
130
|
# Subscribes a handler (subscriber) that will be invoked for published events of provided type.
|
98
131
|
#
|
99
132
|
# @overload subscribe(subscriber, to:)
|
@@ -131,8 +164,8 @@ module RubyEventStore
|
|
131
164
|
#
|
132
165
|
# @param to [Class, String] type of events to get list of sybscribed handlers
|
133
166
|
# @return [Array<Object, Class>]
|
134
|
-
def subscribers_for(
|
135
|
-
subscriptions.all_for(
|
167
|
+
def subscribers_for(event_class)
|
168
|
+
subscriptions.all_for(event_type_resolver.call(event_class))
|
136
169
|
end
|
137
170
|
|
138
171
|
# Builder object for collecting temporary handlers (subscribers)
|
@@ -143,7 +176,7 @@ module RubyEventStore
|
|
143
176
|
@block = block
|
144
177
|
@broker = broker
|
145
178
|
@global_subscribers = []
|
146
|
-
@subscribers = Hash.new {[]}
|
179
|
+
@subscribers = Hash.new { [] }
|
147
180
|
end
|
148
181
|
|
149
182
|
# Subscribes temporary handlers that
|
@@ -175,7 +208,7 @@ module RubyEventStore
|
|
175
208
|
# @param to [Array<Class>] types of events to subscribe
|
176
209
|
# @param handler [Proc] handler passed as proc
|
177
210
|
# @return [self]
|
178
|
-
def subscribe(handler=nil, to:, &handler2)
|
211
|
+
def subscribe(handler = nil, to:, &handler2)
|
179
212
|
raise ArgumentError if handler && handler2
|
180
213
|
@subscribers[handler || handler2] += Array(to)
|
181
214
|
self
|
@@ -187,7 +220,7 @@ module RubyEventStore
|
|
187
220
|
#
|
188
221
|
# @return [Object] value returned by the invoked block of code
|
189
222
|
def call
|
190
|
-
unsubs
|
223
|
+
unsubs = add_thread_global_subscribers
|
191
224
|
unsubs += add_thread_subscribers
|
192
225
|
@block.call
|
193
226
|
ensure
|
@@ -197,15 +230,11 @@ module RubyEventStore
|
|
197
230
|
private
|
198
231
|
|
199
232
|
def add_thread_subscribers
|
200
|
-
@subscribers.map
|
201
|
-
@broker.add_thread_subscription(subscriber, types)
|
202
|
-
end
|
233
|
+
@subscribers.map { |subscriber, types| @broker.add_thread_subscription(subscriber, types) }
|
203
234
|
end
|
204
235
|
|
205
236
|
def add_thread_global_subscribers
|
206
|
-
@global_subscribers.map
|
207
|
-
@broker.add_thread_global_subscription(subscriber)
|
208
|
-
end
|
237
|
+
@global_subscribers.map { |subscriber| @broker.add_thread_global_subscription(subscriber) }
|
209
238
|
end
|
210
239
|
end
|
211
240
|
|
@@ -238,19 +267,19 @@ module RubyEventStore
|
|
238
267
|
#
|
239
268
|
# @return [Event] deserialized event
|
240
269
|
def deserialize(serializer:, event_type:, event_id:, data:, metadata:, timestamp: nil, valid_at: nil)
|
241
|
-
extract_timestamp = lambda
|
242
|
-
(m[:timestamp] || Time.parse(m.fetch('timestamp'))).iso8601
|
243
|
-
end
|
270
|
+
extract_timestamp = lambda { |m| (m[:timestamp] || Time.parse(m.fetch("timestamp"))).iso8601 }
|
244
271
|
|
245
272
|
mapper.record_to_event(
|
246
|
-
SerializedRecord
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
273
|
+
SerializedRecord
|
274
|
+
.new(
|
275
|
+
event_type: event_type,
|
276
|
+
event_id: event_id,
|
277
|
+
data: data,
|
278
|
+
metadata: metadata,
|
279
|
+
timestamp: timestamp || timestamp_ = extract_timestamp[serializer.load(metadata)],
|
280
|
+
valid_at: valid_at || timestamp_
|
281
|
+
)
|
282
|
+
.deserialize(serializer)
|
254
283
|
)
|
255
284
|
end
|
256
285
|
|
@@ -311,14 +340,14 @@ module RubyEventStore
|
|
311
340
|
|
312
341
|
def enrich_events_metadata(events)
|
313
342
|
events = Array(events)
|
314
|
-
events.each{|event| enrich_event_metadata(event) }
|
343
|
+
events.each { |event| enrich_event_metadata(event) }
|
315
344
|
events
|
316
345
|
end
|
317
346
|
|
318
347
|
def enrich_event_metadata(event)
|
319
348
|
metadata.each { |key, value| event.metadata[key] ||= value }
|
320
|
-
event.metadata[:timestamp]
|
321
|
-
event.metadata[:valid_at]
|
349
|
+
event.metadata[:timestamp] ||= clock.call
|
350
|
+
event.metadata[:valid_at] ||= event.metadata.fetch(:timestamp)
|
322
351
|
event.metadata[:correlation_id] ||= correlation_id_generator.call
|
323
352
|
end
|
324
353
|
|
@@ -328,16 +357,20 @@ module RubyEventStore
|
|
328
357
|
|
329
358
|
protected
|
330
359
|
|
360
|
+
def event_type_resolver
|
361
|
+
subscriptions.event_type_resolver
|
362
|
+
end
|
363
|
+
|
331
364
|
def metadata=(value)
|
332
365
|
@metadata.value = value
|
333
366
|
end
|
334
367
|
|
335
368
|
def default_clock
|
336
|
-
->{ Time.now.utc.round(TIMESTAMP_PRECISION) }
|
369
|
+
-> { Time.now.utc.round(TIMESTAMP_PRECISION) }
|
337
370
|
end
|
338
371
|
|
339
372
|
def default_correlation_id_generator
|
340
|
-
->{ SecureRandom.uuid }
|
373
|
+
-> { SecureRandom.uuid }
|
341
374
|
end
|
342
375
|
|
343
376
|
attr_reader :repository, :mapper, :subscriptions, :broker, :clock, :correlation_id_generator
|
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
module RubyEventStore
|
4
4
|
class CorrelatedCommands
|
5
|
-
|
6
5
|
def initialize(event_store, command_bus)
|
7
6
|
@event_store = event_store
|
8
7
|
@command_bus = command_bus
|
@@ -13,23 +12,13 @@ module RubyEventStore
|
|
13
12
|
|
14
13
|
def call(command)
|
15
14
|
correlation_id = event_store.metadata[:correlation_id]
|
16
|
-
causation_id
|
15
|
+
causation_id = event_store.metadata[:causation_id]
|
17
16
|
|
18
17
|
if correlation_id && causation_id
|
19
|
-
command.correlate_with(MiniEvent.new(
|
20
|
-
|
21
|
-
causation_id,
|
22
|
-
)) if command.respond_to?(:correlate_with)
|
23
|
-
event_store.with_metadata(
|
24
|
-
causation_id: command.message_id,
|
25
|
-
) do
|
26
|
-
command_bus.call(command)
|
27
|
-
end
|
18
|
+
command.correlate_with(MiniEvent.new(correlation_id, causation_id)) if command.respond_to?(:correlate_with)
|
19
|
+
event_store.with_metadata(causation_id: command.message_id) { command_bus.call(command) }
|
28
20
|
else
|
29
|
-
event_store.with_metadata(
|
30
|
-
correlation_id: command.message_id,
|
31
|
-
causation_id: command.message_id,
|
32
|
-
) do
|
21
|
+
event_store.with_metadata(correlation_id: command.message_id, causation_id: command.message_id) do
|
33
22
|
command_bus.call(command)
|
34
23
|
end
|
35
24
|
end
|
@@ -1,17 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module RubyEventStore
|
4
|
-
Error
|
4
|
+
Error = Class.new(StandardError)
|
5
5
|
WrongExpectedEventVersion = Class.new(Error)
|
6
|
-
InvalidExpectedVersion
|
7
|
-
IncorrectStreamData
|
8
|
-
SubscriberNotExist
|
9
|
-
InvalidPageStart
|
10
|
-
InvalidPageStop
|
11
|
-
InvalidPageSize
|
12
|
-
EventDuplicatedInStream
|
13
|
-
ReservedInternalName
|
14
|
-
InvalidHandler
|
6
|
+
InvalidExpectedVersion = Class.new(Error)
|
7
|
+
IncorrectStreamData = Class.new(Error)
|
8
|
+
SubscriberNotExist = Class.new(Error)
|
9
|
+
InvalidPageStart = Class.new(Error)
|
10
|
+
InvalidPageStop = Class.new(Error)
|
11
|
+
InvalidPageSize = Class.new(Error)
|
12
|
+
EventDuplicatedInStream = Class.new(Error)
|
13
|
+
ReservedInternalName = Class.new(Error)
|
14
|
+
InvalidHandler = Class.new(Error)
|
15
|
+
EventNotFoundInStream = Class.new(Error)
|
15
16
|
|
16
17
|
class EventNotFound < Error
|
17
18
|
attr_reader :event_id
|
@@ -1,9 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "securerandom"
|
4
4
|
|
5
5
|
module RubyEventStore
|
6
|
-
|
7
6
|
# Data structure representing an event
|
8
7
|
class Event
|
9
8
|
# Instantiates a new event
|
@@ -17,7 +16,7 @@ module RubyEventStore
|
|
17
16
|
def initialize(event_id: SecureRandom.uuid, metadata: nil, data: {})
|
18
17
|
@event_id = event_id.to_s
|
19
18
|
@metadata = Metadata.new(metadata.to_h)
|
20
|
-
@data
|
19
|
+
@data = data
|
21
20
|
end
|
22
21
|
|
23
22
|
attr_reader :event_id, :metadata, :data
|
@@ -31,7 +30,7 @@ module RubyEventStore
|
|
31
30
|
# Type of event. Used when matching with subscribed handlers.
|
32
31
|
# @return [String]
|
33
32
|
def event_type
|
34
|
-
self.class.name
|
33
|
+
metadata[:event_type] || self.class.name
|
35
34
|
end
|
36
35
|
|
37
36
|
# Timestamp from metadata
|
@@ -50,6 +49,7 @@ module RubyEventStore
|
|
50
49
|
|
51
50
|
# Two events are equal if:
|
52
51
|
# * they are of the same class
|
52
|
+
# * have identical event type
|
53
53
|
# * have identical event id
|
54
54
|
# * have identical data (verified with eql? method)
|
55
55
|
#
|
@@ -58,9 +58,8 @@ module RubyEventStore
|
|
58
58
|
# Event equality ignores metadata!
|
59
59
|
# @return [TrueClass, FalseClass]
|
60
60
|
def ==(other_event)
|
61
|
-
other_event.instance_of?(self.class) &&
|
62
|
-
other_event.event_id.eql?(event_id) &&
|
63
|
-
other_event.data.eql?(data)
|
61
|
+
other_event.instance_of?(self.class) && other_event.event_type.eql?(event_type) &&
|
62
|
+
other_event.event_id.eql?(event_id) && other_event.data.eql?(data)
|
64
63
|
end
|
65
64
|
|
66
65
|
# @private
|
@@ -78,11 +77,7 @@ module RubyEventStore
|
|
78
77
|
# * data
|
79
78
|
def hash
|
80
79
|
# We don't use metadata because == does not use metadata
|
81
|
-
[
|
82
|
-
self.class,
|
83
|
-
event_id,
|
84
|
-
data
|
85
|
-
].hash ^ BIG_VALUE
|
80
|
+
[self.class, event_type, event_id, data].hash ^ BIG_VALUE
|
86
81
|
end
|
87
82
|
|
88
83
|
# Reads correlation_id from metadata.
|
@@ -116,7 +111,7 @@ module RubyEventStore
|
|
116
111
|
# @param val [String]
|
117
112
|
# @return [String]
|
118
113
|
def causation_id=(val)
|
119
|
-
metadata[:causation_id]= val
|
114
|
+
metadata[:causation_id] = val
|
120
115
|
end
|
121
116
|
|
122
117
|
# Sets correlation_id and causation_id in metadata based
|
@@ -127,7 +122,7 @@ module RubyEventStore
|
|
127
122
|
# @return [String] set causation_id
|
128
123
|
def correlate_with(other_message)
|
129
124
|
self.correlation_id = other_message.correlation_id || other_message.message_id
|
130
|
-
self.causation_id
|
125
|
+
self.causation_id = other_message.message_id
|
131
126
|
self
|
132
127
|
end
|
133
128
|
|
@@ -21,7 +21,7 @@ module RubyEventStore
|
|
21
21
|
|
22
22
|
def initialize(version)
|
23
23
|
@version = version
|
24
|
-
invalid_version! unless [Integer, :any, :none, :auto].any? {|i| i === version}
|
24
|
+
invalid_version! unless [Integer, :any, :none, :auto].any? { |i| i === version }
|
25
25
|
end
|
26
26
|
|
27
27
|
def any?
|
@@ -53,15 +53,11 @@ module RubyEventStore
|
|
53
53
|
private_constant :BIG_VALUE
|
54
54
|
|
55
55
|
def hash
|
56
|
-
[
|
57
|
-
self.class,
|
58
|
-
version
|
59
|
-
].hash ^ BIG_VALUE
|
56
|
+
[self.class, version].hash ^ BIG_VALUE
|
60
57
|
end
|
61
58
|
|
62
59
|
def ==(other_expected_version)
|
63
|
-
other_expected_version.instance_of?(self.class) &&
|
64
|
-
other_expected_version.version.equal?(version)
|
60
|
+
other_expected_version.instance_of?(self.class) && other_expected_version.version.equal?(version)
|
65
61
|
end
|
66
62
|
|
67
63
|
alias_method :eql?, :==
|