messaging 3.4.1 → 3.5.3

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.
@@ -23,6 +23,8 @@ module Messaging
23
23
  self.expected_version = message.expected_version
24
24
  self.message_type = message.message_type
25
25
  self.stream = message.stream_name
26
+ self.stream_category = message.stream_category
27
+ self.stream_id = message.stream_id
26
28
  self.uuid = message.uuid
27
29
  end
28
30
 
@@ -1,3 +1,5 @@
1
+ require_relative 'category'
2
+ require_relative 'categories'
1
3
  require_relative 'stream'
2
4
  require_relative 'streams'
3
5
 
@@ -20,11 +22,16 @@ module Messaging
20
22
  # @see Streams
21
23
  attr_reader :streams
22
24
 
25
+ # @return [Categories] all the stream categories in the store
26
+ # @see Categories
27
+ attr_reader :categories
28
+
23
29
  # Should not be used directly. Access the store though
24
30
  # Messaging.message_store or Messaging::Adapters::Store[:postgres]
25
31
  # @api private
26
32
  def initialize
27
33
  @streams = Streams.new
34
+ @categories = Categories.new
28
35
  end
29
36
 
30
37
  # Get a specific stream by name
@@ -34,6 +41,13 @@ module Messaging
34
41
  streams[name]
35
42
  end
36
43
 
44
+ # Get a specific category by name
45
+ # @return [Stream]
46
+ # @see Messaging.category
47
+ def category(name)
48
+ categories[name]
49
+ end
50
+
37
51
  # Access to all messages.
38
52
  # Use with caution in production as there are probably
39
53
  # a lot of messages so queries could take a long time or timeout.
@@ -48,6 +62,15 @@ module Messaging
48
62
  SerializedMessage
49
63
  end
50
64
 
65
+ # Access to all messages in the given streams
66
+ #
67
+ # @param streams [Array<String, Stream>] List of one or more streams to get messages from
68
+ # @return [ActiveRecord::Relation]
69
+ # @see Messaging.messages_in_streams
70
+ def messages_in_streams(*streams)
71
+ SerializedMessage.where(stream: streams.flatten.map(&:to_s)).order(:id)
72
+ end
73
+
51
74
  # Writes the message to Postgres
52
75
  # Skips messages that hasn't defined a stream name
53
76
  # We do this to begin with so PG is opt-in per message
@@ -27,6 +27,10 @@ module Messaging
27
27
  messages.maximum(:stream_position) || -1
28
28
  end
29
29
 
30
+ def to_s
31
+ name
32
+ end
33
+
30
34
  def inspect
31
35
  info = "current_position: #{current_position}"
32
36
  "#<Stream:#{name}> #{info}>"
@@ -0,0 +1,19 @@
1
+ module Messaging
2
+ module Adapters
3
+ class Test
4
+ class Categories
5
+ def initialize
6
+ clear!
7
+ end
8
+
9
+ def [](name)
10
+ @categories[name]
11
+ end
12
+
13
+ def clear!
14
+ @categories = Hash.new { |h, k| h[k] = Category.new(k) }
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ module Messaging
2
+ module Adapters
3
+ class Test
4
+ class Category
5
+ attr_accessor :name
6
+
7
+ def initialize(name)
8
+ self.name = name
9
+ end
10
+
11
+ def messages
12
+ @messages ||= []
13
+ end
14
+
15
+ def delete_messages_older_than!(time)
16
+ messages.delete_if { |m| m.timestamp < time }
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,4 +1,6 @@
1
1
  require_relative 'stream'
2
+ require_relative 'category'
3
+ require_relative 'categories'
2
4
 
3
5
  module Messaging
4
6
  module Adapters
@@ -29,7 +31,10 @@ module Messaging
29
31
  # @see Stream
30
32
  attr_reader :streams
31
33
 
34
+ attr_reader :categories
35
+
32
36
  def initialize
37
+ @categories = Categories.new
33
38
  clear!
34
39
  end
35
40
 
@@ -40,8 +45,21 @@ module Messaging
40
45
  streams[name]
41
46
  end
42
47
 
48
+ def category(name)
49
+ categories[name]
50
+ end
51
+
52
+ # Access to all messages in the given streams
53
+ #
54
+ # @param streams [Array<String>] List of one or more streams to get messages from
55
+ # @return [Array<Messaging::Message>]
56
+ def messages_in_streams(*streams)
57
+ messages.select { |m| streams.flatten.map(&:to_s).include? m.stream_name }
58
+ end
59
+
43
60
  def clear!
44
61
  @streams = Hash.new { |h, k| h[k] = Stream.new(k) }
62
+ categories.clear!
45
63
  @messages = []
46
64
  end
47
65
 
@@ -55,10 +73,12 @@ module Messaging
55
73
  return message unless message.stream_name
56
74
 
57
75
  stream = stream(message.stream_name)
76
+ category = category(message.stream_name.split('$').first)
58
77
  ExpectedVersion.new(message.expected_version || :any).match!(stream.current_position)
59
78
  persisted_message = message.class.new(message.attributes.merge(stream_position: stream.current_position + 1))
60
79
  @messages << persisted_message
61
80
  stream.messages << persisted_message
81
+ category.messages << persisted_message
62
82
  persisted_message
63
83
  end
64
84
  end
@@ -12,6 +12,10 @@ module Messaging
12
12
  @messages ||= []
13
13
  end
14
14
 
15
+ def to_s
16
+ name
17
+ end
18
+
15
19
  def current_position
16
20
  messages.length - 1
17
21
  end
@@ -92,6 +92,18 @@ module Messaging
92
92
  nil
93
93
  end
94
94
 
95
+ def stream_category
96
+ return unless stream_name
97
+
98
+ stream_name.split('$').first
99
+ end
100
+
101
+ def stream_id
102
+ return unless stream_name
103
+
104
+ stream_name.split('$').last
105
+ end
106
+
95
107
  def message_type
96
108
  self.class.to_s
97
109
  end
@@ -28,7 +28,7 @@ module Messaging
28
28
  # the class has been unloaded and update the consumer with the reloaded classes.
29
29
  initializer 'messaging.add_reloader' do |app|
30
30
  app.reloader.after_class_unload do
31
- Messaging.routes.reload_consumer_subscriptions!
31
+ Messaging.routes.reload_consumer_routes!
32
32
  end
33
33
  end
34
34
  end
@@ -22,12 +22,25 @@ module Messaging
22
22
  end
23
23
  end
24
24
 
25
+ def inline!(&block)
26
+ current_routes = @routes.dup
27
+ consumer_definitions.each do |_, definition|
28
+ definition.fetch(:block)&.call(self)
29
+ end
30
+
31
+ block.call
32
+
33
+ ensure
34
+ clear_routes!
35
+ @routes = current_routes
36
+ end
37
+
25
38
  # Keeps the consumers, but reload their subscriptions so code reloading works.
26
39
  # The consumer has a reference to the class name of each of its handlers,
27
40
  # if the handler is reloaded the reference would point to an old instance.
28
- def reload_consumer_subscriptions!
41
+ def reload_consumer_routes!
29
42
  consumers.each do |c|
30
- c.clear_subscribers!
43
+ c.clear_routes!
31
44
  consumer_definitions[c.name].fetch(:block)&.call(c)
32
45
  end
33
46
  end
@@ -1,13 +1,39 @@
1
1
  require 'messaging/routing/message_matcher'
2
- require 'messaging/routing/subscriber'
3
- require 'messaging/routing/background_job_subscriber'
2
+ require 'messaging/routing/route'
3
+ require 'messaging/routing/enqueued_route'
4
4
  require 'messaging/routing/enqueue_message_handler'
5
5
 
6
6
  module Messaging
7
7
  module Routing
8
- # Public: Sets up subscribers for the events that matches the given pattern
8
+ def self.included(base)
9
+ base.send :extend, ClassMethods
10
+ end
11
+
12
+ module ClassMethods
13
+ def definitions
14
+ @definitions ||= []
15
+ end
16
+
17
+ def on(pattern, **options, &block)
18
+ definitions << { pattern: pattern, options: options, block: block }
19
+ end
20
+
21
+ def new(*args, &block)
22
+ instance = allocate
23
+
24
+ # Pre-initialize
25
+ definitions.each do |definition|
26
+ instance.on(definition[:pattern], definition[:options], &definition[:block])
27
+ end
28
+
29
+ instance.send(:initialize, *args, &block)
30
+ instance
31
+ end
32
+ end
33
+
34
+ # Public: Sets up routes for the events that matches the given pattern
9
35
  #
10
- # pattern - Which messages to subscribe to. Can be a string, a regexp,
36
+ # pattern - Which messages to route. Can be a string, a regexp,
11
37
  # a Message class, a module or anything that responds to call.
12
38
  #
13
39
  # call: - Any object that responds to call.
@@ -37,31 +63,31 @@ module Messaging
37
63
  # on ->(m) { m.topic == 'my-topic' }, call: DoSometing, enqueue: DoSomethingElseWithSidekiq
38
64
  # end
39
65
  #
40
- def on(pattern = /.*/, topic: nil, call: nil, enqueue: nil, &block)
41
- subscribers << Subscriber.new(pattern, topic, call) if call
42
- subscribers << Subscriber.new(pattern, topic, block) if block_given?
43
- subscribers << BackgroundJobSubscriber.new(pattern, topic, enqueue) if enqueue
66
+ def on(pattern = /.*/, call: nil, enqueue: nil, &block)
67
+ routes << Route.new(pattern, call) if call
68
+ routes << Route.new(pattern, block) if block_given?
69
+ routes << EnqueuedRoute.new(pattern, enqueue) if enqueue
44
70
  end
45
71
 
46
72
  # Internal: Handles the message with the matching subscribers
47
- def handle(message)
48
- subscribers.map { |subscriber| subscriber.call(message) }
73
+ def handle(message, context = self)
74
+ routes.map { |route| route.call(message, context) }
49
75
  message
50
76
  end
51
77
 
52
78
  # Internal: Used by Rails reloading in development.
53
- def clear_subscribers!
54
- @subscribers = Set.new
79
+ def clear_routes!
80
+ @routes = Set.new
55
81
  end
56
82
 
57
83
  private
58
84
 
59
- def subscribers
60
- @subscribers ||= Set.new
85
+ def routes
86
+ @routes ||= Set.new
61
87
  end
62
88
 
63
89
  def topics
64
- subscribers.flat_map(&:topics).map(&:to_s).uniq
90
+ routes.flat_map(&:topics).map(&:to_s).uniq
65
91
  end
66
92
  end
67
93
  end
@@ -1,8 +1,8 @@
1
1
  module Messaging
2
2
  module Routing
3
3
  # Internal: Used for enqueing background jobs instead of calling the handler directly
4
- class BackgroundJobSubscriber < Subscriber
5
- def initialize(pattern, topic, handler)
4
+ class EnqueuedRoute < Route
5
+ def initialize(pattern, handler)
6
6
  super
7
7
  @handler = EnqueueMessageHandler.new(handler)
8
8
  end
@@ -3,21 +3,17 @@ module Messaging
3
3
  module Routing
4
4
  # Internal: Used by subscribers to match messages.
5
5
  class MessageMatcher
6
- include Dry::Equalizer(:pattern, :topic_pattern)
6
+ include Dry::Equalizer(:pattern)
7
7
 
8
8
  attr_accessor :pattern
9
- attr_accessor :topic_pattern
10
9
 
11
- def initialize(pattern:, topic: nil)
10
+ def initialize(pattern:)
12
11
  self.pattern = pattern
13
- self.topic_pattern = topic
14
12
  end
15
13
 
16
14
  # Internal: See routing.rb for examples on how it is used.
17
-
18
- # Med case statement
19
15
  def matches?(message)
20
- matches_topic?(message) && matches_pattern?(message)
16
+ matches_pattern?(message)
21
17
  end
22
18
 
23
19
  def matches_pattern?(message)
@@ -33,16 +29,6 @@ module Messaging
33
29
  end
34
30
  end
35
31
 
36
- def matches_topic?(message)
37
- return true unless topic_pattern
38
-
39
- case message.topic.to_s
40
- when topic_pattern then true
41
- else
42
- false
43
- end
44
- end
45
-
46
32
  def call(message)
47
33
  matches?(message)
48
34
  end
@@ -55,7 +41,7 @@ module Messaging
55
41
  #
56
42
  # Used by the Kafka adapter to setup consumers.
57
43
  def topics
58
- (Array(topic_pattern) + all_matching_messages.map(&:topic)).uniq
44
+ all_matching_messages.map(&:topic).uniq
59
45
  end
60
46
  end
61
47
  end
@@ -3,22 +3,22 @@ require 'dry-equalizer'
3
3
  module Messaging
4
4
  module Routing
5
5
  # Internal: Used by routing to match a pattern against a callable (handler)
6
- class Subscriber
6
+ class Route
7
7
  include Dry::Equalizer(:matcher, :handler)
8
8
 
9
9
  attr_reader :matcher
10
10
  attr_reader :handler
11
11
 
12
- def initialize(pattern, topic, handler)
13
- @matcher = MessageMatcher.new(pattern: pattern, topic: topic)
14
- @handler = handler
12
+ def initialize(pattern, handler)
13
+ @matcher = MessageMatcher.new(pattern: pattern)
14
+ @handler = handler.respond_to?(:to_proc) ? handler : handler.method(:call)
15
15
  verify_handler!
16
16
  end
17
17
 
18
- def call(message)
18
+ def call(message, context = self)
19
19
  return unless @matcher.matches?(message)
20
20
 
21
- @handler.call(message)
21
+ context.instance_exec(message, &@handler)
22
22
  end
23
23
 
24
24
  def topics
@@ -1,3 +1,3 @@
1
1
  module Messaging
2
- VERSION = '3.4.1'.freeze
2
+ VERSION = '3.5.3'.freeze
3
3
  end
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_dependency 'activerecord'
22
22
  spec.add_dependency 'activesupport'
23
- spec.add_dependency 'bukowskis_after_transaction'
23
+ spec.add_dependency 'after_transaction'
24
24
  spec.add_dependency 'method_object'
25
25
  spec.add_dependency 'concurrent-ruby', '>= 1.0.2'
26
26
  spec.add_dependency 'concurrent-ruby-ext', '>= 1.0.2'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: messaging
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.4.1
4
+ version: 3.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bukowskis
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-10-03 00:00:00.000000000 Z
11
+ date: 2020-05-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -39,7 +39,7 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: bukowskis_after_transaction
42
+ name: after_transaction
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
@@ -279,8 +279,16 @@ files:
279
279
  - README.md
280
280
  - Rakefile
281
281
  - _config.yml
282
+ - _data/navigation.yml
283
+ - _layouts/single.html
282
284
  - bin/console
283
285
  - bin/setup
286
+ - docs/adapters.md
287
+ - docs/consuming.md
288
+ - docs/installation.md
289
+ - docs/messages.md
290
+ - docs/publishing.md
291
+ - docs/routing_and_handlers.md
284
292
  - exe/messaging
285
293
  - lib/messaging.rb
286
294
  - lib/messaging/adapters.rb
@@ -289,15 +297,18 @@ files:
289
297
  - lib/messaging/adapters/kafka/producer.rb
290
298
  - lib/messaging/adapters/postgres.rb
291
299
  - lib/messaging/adapters/postgres/advisory_transaction_lock.rb
300
+ - lib/messaging/adapters/postgres/categories.rb
301
+ - lib/messaging/adapters/postgres/category.rb
292
302
  - lib/messaging/adapters/postgres/serialized_message.rb
293
303
  - lib/messaging/adapters/postgres/store.rb
294
304
  - lib/messaging/adapters/postgres/stream.rb
295
305
  - lib/messaging/adapters/postgres/streams.rb
296
306
  - lib/messaging/adapters/test.rb
307
+ - lib/messaging/adapters/test/categories.rb
308
+ - lib/messaging/adapters/test/category.rb
297
309
  - lib/messaging/adapters/test/consumer.rb
298
310
  - lib/messaging/adapters/test/store.rb
299
311
  - lib/messaging/adapters/test/stream.rb
300
- - lib/messaging/base_handler.rb
301
312
  - lib/messaging/cli.rb
302
313
  - lib/messaging/config.rb
303
314
  - lib/messaging/consumer_supervisor.rb
@@ -315,10 +326,10 @@ files:
315
326
  - lib/messaging/resque_worker.rb
316
327
  - lib/messaging/routes.rb
317
328
  - lib/messaging/routing.rb
318
- - lib/messaging/routing/background_job_subscriber.rb
319
329
  - lib/messaging/routing/enqueue_message_handler.rb
330
+ - lib/messaging/routing/enqueued_route.rb
320
331
  - lib/messaging/routing/message_matcher.rb
321
- - lib/messaging/routing/subscriber.rb
332
+ - lib/messaging/routing/route.rb
322
333
  - lib/messaging/sidekiq_worker.rb
323
334
  - lib/messaging/version.rb
324
335
  - messaging.gemspec
@@ -340,8 +351,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
340
351
  - !ruby/object:Gem::Version
341
352
  version: '0'
342
353
  requirements: []
343
- rubyforge_project:
344
- rubygems_version: 2.5.1
354
+ rubygems_version: 3.0.6
345
355
  signing_key:
346
356
  specification_version: 4
347
357
  summary: A library for decoupling applications by using messaging to communicate between