action_pubsub 0.1.1 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/README.md +100 -7
- data/action_pubsub.gemspec +1 -2
- data/lib/action_pubsub.rb +64 -15
- data/lib/action_pubsub/active_record.rb +1 -0
- data/lib/action_pubsub/active_record/subscriber.rb +5 -2
- data/lib/action_pubsub/active_record/subscription.rb +3 -3
- data/lib/action_pubsub/active_record/with_connection.rb +19 -0
- data/lib/action_pubsub/actors.rb +8 -0
- data/lib/action_pubsub/actors/silent_dead_letter_handler.rb +12 -0
- data/lib/action_pubsub/balancer.rb +37 -0
- data/lib/action_pubsub/channel.rb +4 -0
- data/lib/action_pubsub/channels.rb +10 -0
- data/lib/action_pubsub/config.rb +5 -0
- data/lib/action_pubsub/errors.rb +16 -0
- data/lib/action_pubsub/exchanges.rb +21 -0
- data/lib/action_pubsub/has_subscriptions.rb +23 -0
- data/lib/action_pubsub/queue.rb +1 -1
- data/lib/action_pubsub/registry.rb +17 -0
- data/lib/action_pubsub/subscriber.rb +2 -36
- data/lib/action_pubsub/subscriptions.rb +4 -0
- data/lib/action_pubsub/version.rb +1 -1
- metadata +17 -22
- data/lib/action_pubsub/exchange_registry.rb +0 -15
- data/lib/action_pubsub/types.rb +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: add736363fea3bee32c27d452e27658e6c15ecca
|
4
|
+
data.tar.gz: 343e314ddd56701f6d8b50c68c45a648507f54cf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1d66f335d116c6fe845f82eab144b09499142fd60a7e9ef1bdec5aad16dd76a2f651556a8cdc478d9bec6d7c5dbe593018c0956f9070f914031e7a5c99c9006b
|
7
|
+
data.tar.gz: ffc57559d5a8513fc6d372ed00ade3bc9ddd2250c30cf53ccb19eaa637e12fd324b8c2c8e51bc52fd2ec67bb99bed1a5d1a13b59a5af1da0978295f13f57474d
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
In process, concurrent observers, loosely modeled after rabbitmq
|
4
4
|
|
5
|
-
## Example
|
5
|
+
## Active Record Example
|
6
6
|
|
7
7
|
Lets say we have a blog app, and our posts have comments enabled on a case by case basis.
|
8
8
|
When someone leaves a new comment, we want to blast out an email to everyone who
|
@@ -41,6 +41,101 @@ module Blogger
|
|
41
41
|
end
|
42
42
|
```
|
43
43
|
|
44
|
+
## Non Active Record Subscribers
|
45
|
+
|
46
|
+
As of 0.2.0, we can make anything subscribable, and publish to specific non "exchange" queues.
|
47
|
+
|
48
|
+
``` ruby
|
49
|
+
module Blogger
|
50
|
+
class Comment < ::ActiveRecord::Base
|
51
|
+
after_create :publish_after_create, :publish_after_save
|
52
|
+
|
53
|
+
def publish_after_create
|
54
|
+
::ActionPubsub.publish('blogger/comment/created')
|
55
|
+
end
|
56
|
+
|
57
|
+
def publish_after_save
|
58
|
+
::ActionPubsub.publish('blogger/comment/saved')
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
```
|
63
|
+
|
64
|
+
``` ruby
|
65
|
+
class CommentSentimentAnalyzer
|
66
|
+
include ::ActionPubsub::HasSubscriptions
|
67
|
+
include ::ActionPubsub::ActiveRecord::WithConnection
|
68
|
+
|
69
|
+
class_attribute :sentiment_scores
|
70
|
+
self.sentiment_scores = []
|
71
|
+
|
72
|
+
on 'blogger/comment/created' do |record|
|
73
|
+
result = perform_sentiment_analysis(record)
|
74
|
+
|
75
|
+
if(result.is_beligerent?)
|
76
|
+
record.status = :hidden
|
77
|
+
record.sentiment_score = result.value
|
78
|
+
else
|
79
|
+
record.sentiment_score = result.value
|
80
|
+
end
|
81
|
+
|
82
|
+
self.class.sentiment_scores << record.sentiment_score
|
83
|
+
|
84
|
+
record.save
|
85
|
+
end
|
86
|
+
|
87
|
+
def perform_sentiment_analysis(record)
|
88
|
+
##hit some 3rd party sentiment analysis api
|
89
|
+
return record
|
90
|
+
end
|
91
|
+
|
92
|
+
def average_sentiment
|
93
|
+
sentiment_scores.calculate_average
|
94
|
+
end
|
95
|
+
end
|
96
|
+
```
|
97
|
+
|
98
|
+
Notes:
|
99
|
+
1. The cool thing about the above, is not only is it a really lean, loosely coupled subscription, but its subscribes *asynchronously*, so we can call the 3rd party sentiment analysis api
|
100
|
+
directly from our subscriber without worrying about blocking the main thread.
|
101
|
+
|
102
|
+
2. the use of
|
103
|
+
``` ruby
|
104
|
+
include ::ActionPubsub::ActiveRecord::WithConnection
|
105
|
+
```
|
106
|
+
|
107
|
+
This ensures that the active record connection gets checked out and checked back into the pool, else youll run out of connections quickly. If you are not doing anything with active record, dont include it. If you are doing ANYTHING with active record (running a query or anything), make sure to include it, or wrap every on action in a connection checkout block, i.e.
|
108
|
+
|
109
|
+
``` ruby
|
110
|
+
::ActiveRecord::Base.connection_pool.with_connection
|
111
|
+
```
|
112
|
+
|
113
|
+
### Alternative Methods of Publish/Subscription
|
114
|
+
|
115
|
+
``` ruby
|
116
|
+
::ActionPubsub.publish('blogger/comment/created', {:my => :comment})
|
117
|
+
|
118
|
+
#accessing and publishing to the channel directly
|
119
|
+
::ActionPubsub['blogger/comment/created'] << {:my => :comment}
|
120
|
+
|
121
|
+
#subscribing to channel directly
|
122
|
+
::ActionPubsub.on('blogger/comment/created') do |comment|
|
123
|
+
puts comment.inspect
|
124
|
+
end
|
125
|
+
|
126
|
+
#multiple subscriptions per channel are allowed as well
|
127
|
+
::ActionPubsub.on('blogger/comment/created') do |comment|
|
128
|
+
puts "do something else with comment #{comment}"
|
129
|
+
end
|
130
|
+
|
131
|
+
#as well as unique keyed subscriptions by name, i.e.
|
132
|
+
::ActionPubsub.on('blogger/comment/created', :as => '/user/1/blogger/comment/created') do |comment|
|
133
|
+
puts "do something specific to user 1 with comment"
|
134
|
+
end
|
135
|
+
|
136
|
+
#will ensure that the existing subscription does not get duplicated.
|
137
|
+
```
|
138
|
+
|
44
139
|
### What is the advantage of this pattern?
|
45
140
|
|
46
141
|
The advantage is it makes your app incredibly reactive. Rather than have to fatten
|
@@ -52,12 +147,10 @@ application, and do nothing but react to the events occurring within the system.
|
|
52
147
|
|
53
148
|
### Callbacks
|
54
149
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
So as a best practice, we only broadcast the creation after it's been commited.
|
150
|
+
The ActiveRecord callbacks from the ActionPubsub::ActiveRecord module, are only fired after_commit.
|
151
|
+
If for some reason that's a problem, you probably don't want a subscriber to begin with,
|
152
|
+
and should stick with standard callbacks. Meaning, subscribers are primarily intended to perform duties
|
153
|
+
relevant to the model, but that do not change the model itself.
|
61
154
|
|
62
155
|
This also allows for complex chains of events to occur, with no knowledge of each other,
|
63
156
|
but that do their one job, and do it well. If that subscriber happens to create a new record,
|
data/action_pubsub.gemspec
CHANGED
@@ -24,8 +24,7 @@ Gem::Specification.new do |spec|
|
|
24
24
|
|
25
25
|
spec.add_dependency "active_attr"
|
26
26
|
spec.add_dependency "activesupport"
|
27
|
-
spec.add_dependency "concurrent-ruby"
|
28
|
-
spec.add_dependency "trax_core"
|
27
|
+
spec.add_dependency "concurrent-ruby", "~> 0.9.1"
|
29
28
|
spec.add_development_dependency "bundler", "~> 1.10"
|
30
29
|
spec.add_development_dependency 'guard', '~> 2'
|
31
30
|
spec.add_development_dependency 'guard-rspec', '~> 4'
|
data/lib/action_pubsub.rb
CHANGED
@@ -5,22 +5,27 @@ require "active_attr"
|
|
5
5
|
require "concurrent/lazy_register"
|
6
6
|
require "concurrent/actor"
|
7
7
|
require "concurrent/agent"
|
8
|
-
require "trax_core"
|
9
8
|
|
10
9
|
module ActionPubsub
|
11
10
|
extend ::ActiveSupport::Autoload
|
12
11
|
|
13
12
|
autoload :ActiveRecord
|
13
|
+
autoload :Actors
|
14
|
+
autoload :Balancer
|
14
15
|
autoload :Config
|
16
|
+
autoload :Channel
|
17
|
+
autoload :Channels
|
18
|
+
autoload :Errors
|
15
19
|
autoload :Event
|
16
|
-
autoload :
|
17
|
-
autoload :
|
20
|
+
autoload :Exchanges
|
21
|
+
autoload :HasSubscriptions
|
18
22
|
autoload :Publish
|
19
23
|
autoload :Publishable
|
20
24
|
autoload :Logging
|
21
25
|
autoload :Subscriber
|
26
|
+
autoload :Subscriptions
|
27
|
+
autoload :Registry
|
22
28
|
autoload :Queue
|
23
|
-
autoload :Types
|
24
29
|
|
25
30
|
@configuration ||= ::ActionPubsub::Config.new
|
26
31
|
|
@@ -28,12 +33,29 @@ module ActionPubsub
|
|
28
33
|
block.call(config)
|
29
34
|
end
|
30
35
|
|
36
|
+
def self.channels
|
37
|
+
@channels ||= ::ActionPubsub::Channels.new
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.channel?(channel_path)
|
41
|
+
channels.key?(channel_path)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.disable_all!
|
45
|
+
configure do |config|
|
46
|
+
config.disabled = true
|
47
|
+
end
|
48
|
+
|
49
|
+
subscriptions.all.map{ |_subscription| _subscription << :terminate! }
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
31
53
|
def self.event_count
|
32
54
|
@event_count ||= ::Concurrent::Agent.new(0)
|
33
55
|
end
|
34
56
|
|
35
|
-
def self.
|
36
|
-
@
|
57
|
+
def self.exchanges
|
58
|
+
@exchanges ||= ::ActionPubsub::Exchanges.new
|
37
59
|
end
|
38
60
|
|
39
61
|
def self.destination_tuple_from_path(path_string)
|
@@ -50,18 +72,33 @@ module ActionPubsub
|
|
50
72
|
[segs.join("/"), action]
|
51
73
|
end
|
52
74
|
|
75
|
+
def self.on(*paths, as:nil, &block)
|
76
|
+
paths.map do |path|
|
77
|
+
target_channel = ::ActionPubsub.channels[path]
|
78
|
+
subscription_path = "#{path}:#{as || SecureRandom.uuid}"
|
79
|
+
|
80
|
+
::ActionPubsub.subscriptions[subscription_path] ||= ::Concurrent::Actor::Utils::AdHoc.spawn(subscription_path) do
|
81
|
+
target_channel << :subscribe
|
82
|
+
-> message {
|
83
|
+
block.call(message)
|
84
|
+
}
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
53
89
|
def self.symbolize_routing_key(routing_key)
|
54
90
|
:"#{routing_key.split('.').join('_')}"
|
55
91
|
end
|
56
92
|
|
93
|
+
def self.publish(path, message)
|
94
|
+
self[path] << message
|
95
|
+
end
|
96
|
+
|
57
97
|
def self.publish_event(routing_key, event)
|
58
98
|
#need to loop through exchanges and publish to them
|
59
99
|
#maybe there is a better way to do this?
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
queue_names.each do |queue_name|
|
64
|
-
exchange_registry[routing_key][queue_name] << serialize_event(event)
|
100
|
+
exchanges[routing_key].keys.each do |queue_name|
|
101
|
+
exchanges[routing_key][queue_name] << serialize_event(event)
|
65
102
|
end
|
66
103
|
end
|
67
104
|
|
@@ -69,6 +106,18 @@ module ActionPubsub
|
|
69
106
|
event
|
70
107
|
end
|
71
108
|
|
109
|
+
def self.subscriptions
|
110
|
+
@subscriptions ||= ::ActionPubsub::Subscriptions.new
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.silent_dead_letter_handler
|
114
|
+
@silent_dead_letter_handler ||= ::ActionPubsub::Actors::SilentDeadLetterHandler.spawn('action_pubsub/silent_dead_letter_handler')
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.subscription?(path)
|
118
|
+
subscriptions.key?(path)
|
119
|
+
end
|
120
|
+
|
72
121
|
def self.deserialize_event(event)
|
73
122
|
event
|
74
123
|
end
|
@@ -76,10 +125,10 @@ module ActionPubsub
|
|
76
125
|
class << self
|
77
126
|
attr_accessor :configuration
|
78
127
|
alias_method :config, :configuration
|
79
|
-
alias_method :exchanges, :exchange_registry
|
80
128
|
|
81
|
-
delegate :
|
82
|
-
delegate :
|
83
|
-
delegate :
|
129
|
+
delegate :[], :to => :channels
|
130
|
+
delegate :register_queue, :to => :exchanges
|
131
|
+
delegate :register_channel, :to => :exchanges
|
132
|
+
delegate :register_exchange, :to => :exchanges
|
84
133
|
end
|
85
134
|
end
|
@@ -30,6 +30,9 @@ module ActionPubsub
|
|
30
30
|
subklass.event_processed_count = ::Concurrent::AtomicFixnum.new(0)
|
31
31
|
end
|
32
32
|
|
33
|
+
def self.disable_all!
|
34
|
+
end
|
35
|
+
|
33
36
|
def self.on(event_name, **options, &block)
|
34
37
|
reactions[event_name] = {}.tap do |hash|
|
35
38
|
hash[:block] = block
|
@@ -69,11 +72,11 @@ module ActionPubsub
|
|
69
72
|
target_exchange = [exchange_prefix, event_name].join("/")
|
70
73
|
subscriber_key = name.underscore
|
71
74
|
queue_key = [target_exchange, subscriber_key].join("/")
|
72
|
-
|
73
75
|
::ActionPubsub.register_queue(target_exchange, subscriber_key)
|
74
76
|
|
75
77
|
self.concurrency.times do |i|
|
76
|
-
|
78
|
+
queue_address = "#{queue_key}/#{i}"
|
79
|
+
::ActionPubsub.subscriptions[queue_address] ||= self.subscription.spawn(queue_address) do
|
77
80
|
self.subscription.bind_subscription(target_exchange, subscriber_key)
|
78
81
|
end
|
79
82
|
end
|
@@ -4,7 +4,7 @@ module ActionPubsub
|
|
4
4
|
class_attribute :subscriber
|
5
5
|
|
6
6
|
def self.bind_subscription(target_exchange, subscriber_key)
|
7
|
-
::ActionPubsub.
|
7
|
+
::ActionPubsub.exchanges[target_exchange][subscriber_key] << :subscribe
|
8
8
|
-> message {
|
9
9
|
::ActiveRecord::Base.connection_pool.with_connection do
|
10
10
|
begin
|
@@ -21,10 +21,10 @@ module ActionPubsub
|
|
21
21
|
self.class.bind_subscription(target_exchange, subscriber_key)
|
22
22
|
rescue => e
|
23
23
|
#ensure we rebind subscription regardless
|
24
|
-
self.class.bind_subscription(target_exchange, subscriber_key)
|
24
|
+
self.class.bind_subscription(target_exchange, subscriber_key) unless message.is_a?(Symbol)
|
25
25
|
message = ::ActionPubsub.deserialize_event(message)
|
26
26
|
|
27
|
-
failure_message = ::ActionPubsub::
|
27
|
+
failure_message = ::ActionPubsub::Errors::SubscriptionReactionErrorMessage.new(
|
28
28
|
:target_exchange => target_exchange,
|
29
29
|
:subscriber_key => subscriber_key,
|
30
30
|
:error => e,
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ActionPubsub
|
2
|
+
module ActiveRecord
|
3
|
+
module WithConnection
|
4
|
+
extend ::ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def on(*paths, as:nil, &block)
|
8
|
+
wrapped_block = lambda{ |message|
|
9
|
+
::ActiveRecord::Base.connection_pool.with_connection { block.call(message) }
|
10
|
+
}
|
11
|
+
|
12
|
+
_subscriptions = ::ActionPubsub.on(*paths, as:(as || _as), &wrapped_block)
|
13
|
+
_subscriptions.each { |_subscription| subscriptions << _subscription }
|
14
|
+
subscriptions
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
#dead letter routing not working ATM
|
2
|
+
module ActionPubsub
|
3
|
+
module Actors
|
4
|
+
class SilentDeadLetterHandler < ::Concurrent::Actor::RestartingContext
|
5
|
+
def on_messaage(dead_letter)
|
6
|
+
puts "SILENCE DEAD LETTER HANDLER GOT MESSAGE"
|
7
|
+
puts dead_letter.inspect
|
8
|
+
super(dead_letter)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module ActionPubsub
|
2
|
+
class Balancer < ::Concurrent::Actor::Utils::Balancer
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@receivers = []
|
6
|
+
@buffer = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def on_message(message)
|
10
|
+
case message
|
11
|
+
when :subscribe
|
12
|
+
@receivers << envelope.sender
|
13
|
+
distribute
|
14
|
+
true
|
15
|
+
when :unsubscribe
|
16
|
+
@receivers.delete envelope.sender
|
17
|
+
true
|
18
|
+
when :subscribed?
|
19
|
+
@receivers.include? envelope.sender
|
20
|
+
else
|
21
|
+
@buffer << envelope
|
22
|
+
distribute
|
23
|
+
::Concurrent::Actor::Behaviour::MESSAGE_PROCESSED
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def distribute
|
28
|
+
while !@receivers.empty? && !@buffer.empty?
|
29
|
+
redirect @receivers.shift, @buffer.shift
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def dead_letter_routing
|
34
|
+
::ActionPubsub.silent_dead_letter_handler
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/action_pubsub/config.rb
CHANGED
@@ -6,6 +6,7 @@ module ActionPubsub
|
|
6
6
|
super(*args)
|
7
7
|
|
8
8
|
self[:debug] = false
|
9
|
+
self[:disabled] = false
|
9
10
|
self[:serializer] = nil
|
10
11
|
self[:_on_error_block] = nil
|
11
12
|
end
|
@@ -14,6 +15,10 @@ module ActionPubsub
|
|
14
15
|
::Concurrent.use_stdlib_logger(Logger::DEBUG) if val
|
15
16
|
end
|
16
17
|
|
18
|
+
def disabled?
|
19
|
+
self[:disabled]
|
20
|
+
end
|
21
|
+
|
17
22
|
def serializer=(val)
|
18
23
|
if val && val.ancestors.include?(::ActiveSupport::Concern)
|
19
24
|
::ActionPubsub.include(val)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'active_model'
|
2
|
+
|
3
|
+
module ActionPubsub
|
4
|
+
module Errors
|
5
|
+
class SubscriptionReactionErrorMessage
|
6
|
+
attr_accessor :message, :error, :target_exchange, :subscriber_key
|
7
|
+
|
8
|
+
def initialize(*args, message:, error:, target_exchange:nil, subscriber_key:nil, **options)
|
9
|
+
@message = message
|
10
|
+
@error = error
|
11
|
+
@target_exchange = target_exchange
|
12
|
+
@subscriber_key = subscriber_key
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ActionPubsub
|
2
|
+
class Exchanges < ::ActionPubsub::Registry
|
3
|
+
def register_queue(exchange_name, subscriber_name)
|
4
|
+
queue_name = [exchange_name, subscriber_name].join("/")
|
5
|
+
queue_exists = self[exchange_name].all.any?{ |queue| queue.name == queue_name }
|
6
|
+
self[exchange_name].add(subscriber_name) { ::ActionPubsub::Queue.spawn(queue_name) } unless queue_exists
|
7
|
+
end
|
8
|
+
|
9
|
+
def register_exchange(exchange_name)
|
10
|
+
add(exchange_name) { ::ActionPubsub::Exchanges.new }
|
11
|
+
self[exchange_name]
|
12
|
+
end
|
13
|
+
|
14
|
+
def [](val)
|
15
|
+
return super(val) if key?(val)
|
16
|
+
|
17
|
+
add(val){ ::ActionPubsub::Exchanges.new }
|
18
|
+
super(val)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ActionPubsub
|
2
|
+
module HasSubscriptions
|
3
|
+
extend ::ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
class_attribute :_as
|
7
|
+
class_attribute :subscriptions
|
8
|
+
self.subscriptions = []
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def as(val)
|
13
|
+
self._as = val
|
14
|
+
end
|
15
|
+
|
16
|
+
def on(*paths, as:nil, &block)
|
17
|
+
_subscriptions = ::ActionPubsub.on(*paths, as:(as || _as), &block)
|
18
|
+
_subscriptions.each { |_subscription| subscriptions << _subscription }
|
19
|
+
subscriptions
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/action_pubsub/queue.rb
CHANGED
@@ -1,39 +1,5 @@
|
|
1
|
-
### NOTE: Don't use this. Only the active record subscriber is working ATM.
|
2
1
|
module ActionPubsub
|
3
|
-
class Subscriber
|
4
|
-
|
5
|
-
self.concurrency = 1
|
6
|
-
|
7
|
-
def self.inherited(subklass)
|
8
|
-
subklass.watches = {}
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.register_event_watcher(event_name)
|
12
|
-
target_exchange = [exchange_prefix, event_name].join("/")
|
13
|
-
subscriber_key = name.underscore
|
14
|
-
queue_key = [target_exchange, subscriber_key].join("/")
|
15
|
-
|
16
|
-
::ActionPubsub.register_queue(target_exchange, subscriber_key)
|
17
|
-
|
18
|
-
self.concurrency.times do |i|
|
19
|
-
spawn("#{queue_key}/#{i}") do
|
20
|
-
bind_subscription(target_exchange, subscriber_key)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.on(event_name, &block)
|
26
|
-
watches[event_name] = block
|
27
|
-
register_event_watcher(event_name)
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.bind_subscription(target_exchange, subscriber_key)
|
31
|
-
::ActionPubsub.exchange_registry[target_exchange][subscriber_key] << :subscribe
|
32
|
-
-> message {
|
33
|
-
self.class.watches[message["action"]].call(message["record"])
|
34
|
-
|
35
|
-
self.class.bind_subscription(target_exchange, subscriber_key)
|
36
|
-
}
|
37
|
-
end
|
2
|
+
class Subscriber
|
3
|
+
include ::ActionPubsub::HasSubscriptions
|
38
4
|
end
|
39
5
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: action_pubsub
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jason Ayre
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-01-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: active_attr
|
@@ -42,30 +42,16 @@ dependencies:
|
|
42
42
|
name: concurrent-ruby
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
48
|
-
type: :runtime
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: trax_core
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
45
|
+
- - "~>"
|
60
46
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
47
|
+
version: 0.9.1
|
62
48
|
type: :runtime
|
63
49
|
prerelease: false
|
64
50
|
version_requirements: !ruby/object:Gem::Requirement
|
65
51
|
requirements:
|
66
|
-
- - "
|
52
|
+
- - "~>"
|
67
53
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
54
|
+
version: 0.9.1
|
69
55
|
- !ruby/object:Gem::Dependency
|
70
56
|
name: bundler
|
71
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -290,14 +276,23 @@ files:
|
|
290
276
|
- lib/action_pubsub/active_record/publishable.rb
|
291
277
|
- lib/action_pubsub/active_record/subscriber.rb
|
292
278
|
- lib/action_pubsub/active_record/subscription.rb
|
279
|
+
- lib/action_pubsub/active_record/with_connection.rb
|
280
|
+
- lib/action_pubsub/actors.rb
|
281
|
+
- lib/action_pubsub/actors/silent_dead_letter_handler.rb
|
282
|
+
- lib/action_pubsub/balancer.rb
|
283
|
+
- lib/action_pubsub/channel.rb
|
284
|
+
- lib/action_pubsub/channels.rb
|
293
285
|
- lib/action_pubsub/config.rb
|
286
|
+
- lib/action_pubsub/errors.rb
|
294
287
|
- lib/action_pubsub/event.rb
|
295
|
-
- lib/action_pubsub/
|
288
|
+
- lib/action_pubsub/exchanges.rb
|
289
|
+
- lib/action_pubsub/has_subscriptions.rb
|
296
290
|
- lib/action_pubsub/queue.rb
|
291
|
+
- lib/action_pubsub/registry.rb
|
297
292
|
- lib/action_pubsub/serialization.rb
|
298
293
|
- lib/action_pubsub/serialization/marshal.rb
|
299
294
|
- lib/action_pubsub/subscriber.rb
|
300
|
-
- lib/action_pubsub/
|
295
|
+
- lib/action_pubsub/subscriptions.rb
|
301
296
|
- lib/action_pubsub/version.rb
|
302
297
|
homepage: http://github.com/jasonayre/action_pubsub
|
303
298
|
licenses:
|
@@ -1,15 +0,0 @@
|
|
1
|
-
module ActionPubsub
|
2
|
-
class ExchangeRegistry < ::Concurrent::LazyRegister
|
3
|
-
def register_queue(exchange_name, subscriber_name)
|
4
|
-
register_exchange(exchange_name) unless key?(exchange_name)
|
5
|
-
exchange_hash = self[exchange_name].instance_variable_get("@data").value
|
6
|
-
exchange_keys = exchange_hash.keys
|
7
|
-
queue_name = [exchange_name, subscriber_name].join("/")
|
8
|
-
self[exchange_name].add(subscriber_name) { ::ActionPubsub::Queue.spawn(queue_name) }
|
9
|
-
end
|
10
|
-
|
11
|
-
def register_exchange(exchange_name)
|
12
|
-
add(exchange_name) { ::Concurrent::LazyRegister.new }
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
data/lib/action_pubsub/types.rb
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
require 'trax_core'
|
2
|
-
|
3
|
-
module ActionPubsub
|
4
|
-
class Types < ::Trax::Core::Blueprint
|
5
|
-
class SubscriptionReactionError < ::Trax::Core::Types::Struct
|
6
|
-
string :target_exchange
|
7
|
-
string :subscriber_key
|
8
|
-
|
9
|
-
attr_accessor :message, :error
|
10
|
-
|
11
|
-
def initialize(*args, message:, error:, **options)
|
12
|
-
super(*args, **options)
|
13
|
-
|
14
|
-
@message = message
|
15
|
-
@error = error
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|