resque-bus 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,88 +6,103 @@ This gem uses Redis and Resque to allow simple asynchronous communication betwee
6
6
 
7
7
  Application A can publish an event
8
8
 
9
- # config
10
- ResqueBus.redis = "192.168.1.1:6379"
9
+ ```ruby
10
+ # config
11
+ ResqueBus.redis = "192.168.1.1:6379"
11
12
 
12
- # business logic
13
- ResqueBus.publish("user_created", "id" => 42, "first_name" => "John", "last_name" => "Smith")
14
-
15
- # or do it later
16
- ResqueBus.publish_at(1.hour.from_now, "user_created", "id" => 42, "first_name" => "John", "last_name" => "Smith")
13
+ # business logic
14
+ ResqueBus.publish("user_created", "id" => 42, "first_name" => "John", "last_name" => "Smith")
15
+
16
+ # or do it later
17
+ ResqueBus.publish_at(1.hour.from_now, "user_created", "id" => 42, "first_name" => "John", "last_name" => "Smith")
18
+ ```
17
19
 
18
20
  Application B is subscribed to events
19
21
 
20
- # config
21
- ResqueBus.redis = "192.168.1.1:6379"
22
-
23
- # initializer
24
- ResqueBus.dispatch("app_b") do
25
- # processes event on app_b_default queue
26
- # subscribe is short-hand to subscribe to your 'default' queue and this block with process events with the name "user_created"
27
- subscribe "user_created" do |attributes|
28
- NameCount.find_or_create_by_name(attributes["last_name"]).increment!
29
- end
30
-
31
- # processes event on app_b_critical queue
32
- # critical is short-hand to subscribe to your 'critical' queue and this block with process events with the name "user_paid"
33
- critical "user_paid" do |attributes|
34
- CreditCard.charge!(attributes)
35
- end
36
-
37
- # you can pass any queue name you would like to process from as well IE: `banana "peeled" do |attributes|`
38
-
39
- # and regexes work as well. note that with the above configuration along with this regex,
40
- # the following as well as the corresponding block above would both be executed
41
- subscribe /^user_/ do |attributes|
42
- Metrics.record_user_action(attributes["bus_event_type"], attributes["id"])
43
- end
44
-
45
- # the above all filter on just the event_type, but you can filter on anything
46
- # this would be _any_ event that has a user_id and the page value of homepage regardless of bus_event_type
47
- subscribe "my_key", { "user_id" => :present, "page" => "homepage"} do
48
- Mixpanel.homepage_action!(attributes["action"])
49
- end
50
- end
22
+ ```ruby
23
+ # config
24
+ ResqueBus.redis = "192.168.1.1:6379"
25
+
26
+ # initializer
27
+ ResqueBus.dispatch("app_b") do
28
+ # processes event on app_b_default queue
29
+ # subscribe is short-hand to subscribe to your 'default' queue and this block with process events with the name "user_created"
30
+ subscribe "user_created" do |attributes|
31
+ NameCount.find_or_create_by_name(attributes["last_name"]).increment!
32
+ end
33
+
34
+ # processes event on app_b_critical queue
35
+ # critical is short-hand to subscribe to your 'critical' queue and this block with process events with the name "user_paid"
36
+ critical "user_paid" do |attributes|
37
+ CreditCard.charge!(attributes)
38
+ end
39
+
40
+ # you can pass any queue name you would like to process from as well IE: `banana "peeled" do |attributes|`
41
+
42
+ # and regexes work as well. note that with the above configuration along with this regex,
43
+ # the following as well as the corresponding block above would both be executed
44
+ subscribe /^user_/ do |attributes|
45
+ Metrics.record_user_action(attributes["bus_event_type"], attributes["id"])
46
+ end
47
+
48
+ # the above all filter on just the event_type, but you can filter on anything
49
+ # this would be _any_ event that has a user_id and the page value of homepage regardless of bus_event_type
50
+ subscribe "my_key", { "user_id" => :present, "page" => "homepage"} do
51
+ Mixpanel.homepage_action!(attributes["action"])
52
+ end
53
+ end
54
+ ```
51
55
 
52
56
  Applications can also subscribe within classes using the provided `Subscriber` module.
53
57
 
54
- class SimpleSubscriber
55
- include ResqueBus::Subscriber
56
- subscribe :my_method
57
-
58
- def my_method(attributes)
59
- # heavy lifting
60
- end
61
- end
58
+ ```ruby
59
+ class SimpleSubscriber
60
+ include ResqueBus::Subscriber
61
+ subscribe :my_method
62
+
63
+ def my_method(attributes)
64
+ # heavy lifting
65
+ end
66
+ end
67
+ ```
62
68
 
63
69
  The following is equivalent to the original initializer and shows more options:
64
70
 
65
- class OtherSubscriber
66
- include ResqueBus::Subscriber
67
- application :app_b
71
+ ```ruby
72
+ class OtherSubscriber
73
+ include ResqueBus::Subscriber
74
+ application :app_b
75
+
76
+ subscribe :user_created
77
+ subscribe_queue :app_b_critical, :user_paid
78
+ subscribe_queue :app_b_default, :user_action, :bus_event_type => /^user_/
79
+ subscribe :homepage_method, :user_id => :present, :page => "homepage"
68
80
 
69
- subscribe :user_created
70
- subscribe_queue :app_b_critical, :user_paid
71
- subscribe_queue :app_b_default, :user_action, :bus_event_type => /^user_/
72
- subscribe :homepage_method, :user_id => :present, :page => "homepage"
81
+ def user_created(attributes)
82
+ NameCount.find_or_create_by_name(attributes["last_name"]).increment!
83
+ end
73
84
 
74
- def user_created(attributes)
75
- NameCount.find_or_create_by_name(attributes["last_name"]).increment!
76
- end
85
+ def user_paid(attributes)
86
+ CreditCard.charge!(attributes)
87
+ end
77
88
 
78
- def user_paid(attributes)
79
- CreditCard.charge!(attributes)
80
- end
89
+ def user_action(attributes)
90
+ Metrics.record_user_action(attributes["bus_event_type"], attributes["id"])
91
+ end
81
92
 
82
- def user_action(attributes)
83
- Metrics.record_user_action(attributes["bus_event_type"], attributes["id"])
84
- end
93
+ def homepage_method
94
+ Mixpanel.homepage_action!(attributes["action"])
95
+ end
96
+ end
97
+ ```
85
98
 
86
- def homepage_method
87
- Mixpanel.homepage_action!(attributes["action"])
88
- end
89
- end
99
+ Note: This subscribes when this class is loaded, so it needs to be in your load or otherwise referenced/required during app initialization to work properly.
90
100
 
101
+ ### Commands
102
+
103
+ Each app needs to tell Redis about its subscriptions:
104
+
105
+ $ rake resquebus:subscribe
91
106
 
92
107
  The subscription block is run inside a Resque worker which needs to be started for each app.
93
108
 
@@ -99,30 +114,35 @@ The incoming queue also needs to be processed on a dedicated or all the app serv
99
114
 
100
115
  If you want retry to work for subscribing apps, you should run resque-scheduler
101
116
 
102
- $ rake resquebus:driver resque:scheduler
117
+ $ rake resque:scheduler
103
118
 
104
119
  ### Heartbeat
105
120
 
106
- We've found it useful to have the bus act like cron. Triggering timed jobs throughout the system. Resque Bus calls this a heartbeat.
121
+ We've found it useful to have the bus act like `cron`, triggering timed jobs throughout the system. Resque Bus calls this a heartbeat.
107
122
  It uses resque-scheduler to trigger the events. You can enable it in your Rakefile.
108
123
 
109
- # resque.rake
110
- namespace :resque do
111
- task :setup => [:environment] do
112
- ResqueBus.heartbeat!
113
- end
114
- end
124
+ ```ruby
125
+ # resque.rake
126
+ namespace :resque do
127
+ task :setup => [:environment] do
128
+ ResqueBus.heartbeat!
129
+ end
130
+ end
131
+ ```
115
132
 
116
133
  Or add it to your `schedule.yml` directly
117
134
 
118
- resquebus_heartbeat:
119
- cron: "* * * * *"
120
- class: "::ResqueBus::Heartbeat"
121
- queue: resquebus_incoming
122
- description: "I publish a heartbeat_minutes event every minute"
135
+ ```yaml
136
+ resquebus_heartbeat:
137
+ cron: "* * * * *"
138
+ class: "::ResqueBus::Heartbeat"
139
+ queue: resquebus_incoming
140
+ description: "I publish a heartbeat_minutes event every minute"
141
+ ```
123
142
 
124
143
  It is the equivalent of doing this every minute
125
144
 
145
+ ```ruby
126
146
  seconds = minutes * (60)
127
147
  hours = minutes / (60)
128
148
  days = minutes / (60*24)
@@ -131,74 +151,81 @@ now = Time.at(seconds)
131
151
 
132
152
  attributes = {}
133
153
 
134
-
135
- now = Time.now
136
- seconds = now.to_i
137
- ResqueBus.publish("hearbeat_minutes", {
138
- "epoch_seconds" => seconds,
139
- "epoch_minutes" => seconds / 1.minute,
140
- "epoch_hours" => seconds / 1.hour,
141
- "epoch_days" => seconds / 1.day,
142
- "minute" => now.min
143
- "hour" => now.hour
144
- "day" => now.day
145
- "month" => now.month
146
- "year" => now.year
147
- "yday" => now.yday
148
- "wday" => now.wday
149
- })
154
+ now = Time.now
155
+ seconds = now.to_i
156
+ ResqueBus.publish("hearbeat_minutes", {
157
+ "epoch_seconds" => seconds,
158
+ "epoch_minutes" => seconds / 1.minute,
159
+ "epoch_hours" => seconds / 1.hour,
160
+ "epoch_days" => seconds / 1.day,
161
+ "minute" => now.min
162
+ "hour" => now.hour
163
+ "day" => now.day
164
+ "month" => now.month
165
+ "year" => now.year
166
+ "yday" => now.yday
167
+ "wday" => now.wday
168
+ })
169
+ ```
150
170
 
151
171
  This allows you do something like this:
152
172
 
153
- ResqueBus.dispatch("app_c") do
154
- # runs at 10:20, 11:20, etc
155
- subscribe "once_an_hour", 'bus_event_type' => 'heartbeat_minutes', 'minute' => 20 do |attributes|
156
- Sitemap.generate!
157
- end
158
-
159
- # runs every five minutes
160
- subscribe "every_five_minutes", 'bus_event_type' => 'heartbeat_minutes' do |attributes|
161
- next unless attributes["epoch_minutes"] % 5 == 0
162
- HealthCheck.run!
163
- end
164
-
165
- # runs at 8am on the first of every month
166
- subscribe "new_month_morning", 'bus_event_type' => 'heartbeat_minutes', 'day' => 1, hour' => 8, 'minute' => 0, do |attributes|
167
- next unless attributes["epoch_minutes"] % 5 == 0
168
- Token.old.expire!
169
- end
170
- end
171
-
173
+ ```ruby
174
+ ResqueBus.dispatch("app_c") do
175
+ # runs at 10:20, 11:20, etc
176
+ subscribe "once_an_hour", 'bus_event_type' => 'heartbeat_minutes', 'minute' => 20 do |attributes|
177
+ Sitemap.generate!
178
+ end
179
+
180
+ # runs every five minutes
181
+ subscribe "every_five_minutes", 'bus_event_type' => 'heartbeat_minutes' do |attributes|
182
+ next unless attributes["epoch_minutes"] % 5 == 0
183
+ HealthCheck.run!
184
+ end
185
+
186
+ # runs at 8am on the first of every month
187
+ subscribe "new_month_morning", 'bus_event_type' => 'heartbeat_minutes', 'day' => 1, hour' => 8, 'minute' => 0, do |attributes|
188
+ next unless attributes["epoch_minutes"] % 5 == 0
189
+ Token.old.expire!
190
+ end
191
+ end
192
+ ```
172
193
 
173
194
  ### Compatibility
174
195
 
175
196
  ResqueBus can live along side another instance of Resque that points at a different Redis server.
176
-
177
- # config
178
- Resque.redis = "192.168.1.0:6379"
179
- ResqueBus.redis = "192.168.1.1:6379"
197
+
198
+ ```ruby
199
+ # config
200
+ Resque.redis = "192.168.1.0:6379"
201
+ ResqueBus.redis = "192.168.1.1:6379"
202
+ ```
180
203
 
181
204
  If no Redis instance is given specifically, ResqueBus will use the Resque one.
182
205
 
183
- # config
184
- Resque.redis = "192.168.1.0:6379"
206
+ ```ruby
207
+ # config
208
+ Resque.redis = "192.168.1.0:6379"
209
+ ```
185
210
 
186
211
  That will use the default (resque) namespace which can be helpful for using the tooling. Conflict with queue names are unlikely. You can change the namespace if you like though.
187
212
 
188
- # config
189
- Resque.redis = "192.168.1.0:6379"
190
- ResqusBus.redis.namespace = :get_on_the_bus
191
-
213
+ ```ruby
214
+ # config
215
+ Resque.redis = "192.168.1.0:6379"
216
+ ResqusBus.redis.namespace = :get_on_the_bus
217
+ ```
192
218
 
193
219
  ### Local Mode
194
220
 
195
- For development, a local mode is also provided and is specified in the
196
- configuration.
221
+ For development, a local mode is also provided and is specified in the configuration.
197
222
 
198
- # config
199
- ResqueBus.local_mode = :standalone
200
- or
201
- ResqueBus.local_mode = :inline
223
+ ```ruby
224
+ # config
225
+ ResqueBus.local_mode = :standalone
226
+ or
227
+ ResqueBus.local_mode = :inline
228
+ ```
202
229
 
203
230
  Standalone mode does not require a separate resquebus:driver task to be running to process the
204
231
  incoming queue. Simply publishing to the bus will distribute the incoming events
@@ -215,7 +242,6 @@ event to the appropriate code block.
215
242
  * Make this not freak out in development without Redis or when Redis is down
216
243
  * We might not actually need to publish in tests
217
244
  * Add some rspec helpers for the apps to use: should_ post an event_publish or something along those lines
218
- * A synchronous version will be needed for several use cases. Make it so that an event can go in real-time to one subscriber and still be async to the rest.
219
- * Should this use resque-retry or should they jsut go into the failure queue?
245
+ * Allow calling resquebus:setup and resquebus:driver together (append to ENV['QUEUES'], don't replace it)
220
246
 
221
247
  Copyright (c) 2011 Brian Leonard, released under the MIT license
@@ -41,9 +41,19 @@ module ResqueBus
41
41
  "resquebus:heartbeat:timestamp"
42
42
  end
43
43
 
44
+ def environment_name
45
+ ENV["RAILS_ENV"] || ENV["RACK_ENV"] || ENV["RESQUEBUS_ENV"]
46
+ end
47
+
44
48
  def get_saved_minute!
45
49
  key = ResqueBus.redis.get(redis_key)
46
50
  return nil if key.nil?
51
+ case environment_name
52
+ when 'development', 'test'
53
+ # only 3 minutes in development; otherwise, TONS of events if not run in a while
54
+ three_ago = Time.now.to_i - 3*60*60
55
+ key = three_ago if key.to_i < three_ago
56
+ end
47
57
  return key.to_i
48
58
  end
49
59
 
@@ -1,5 +1,5 @@
1
1
  module Resque
2
2
  module Bus
3
- VERSION = "0.3.0"
3
+ VERSION = "0.3.1"
4
4
  end
5
5
  end
@@ -35,8 +35,6 @@ module ResqueBus
35
35
  Heartbeat.perform
36
36
  end
37
37
 
38
- debugger
39
-
40
38
  Timecop.freeze "12/12/2013 12:02:01" do
41
39
  ResqueBus.should_receive(:publish).with("heartbeat_minutes", now_attributes)
42
40
  Heartbeat.perform
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resque-bus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-09-26 00:00:00.000000000 Z
12
+ date: 2013-10-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: resque