messaging 3.4.1 → 3.5.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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