message_router 0.1.3 → 0.2.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 +7 -0
- data/.ruby-version +1 -0
- data/.travis.yml +10 -0
- data/Gemfile +8 -4
- data/Gemfile.lock +50 -18
- data/Guardfile +29 -5
- data/README.rdoc +40 -37
- data/lib/message_router/router.rb +97 -77
- data/lib/message_router/version.rb +1 -1
- data/message_router.gemspec +2 -2
- data/spec/message_router_spec.rb +178 -169
- metadata +29 -45
- data/.rvmrc +0 -1
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 41cf9f1df6c11508d36a7dda6cce51a70ba19f30
|
|
4
|
+
data.tar.gz: 8168d00903f7ee8255021d28c251876e56a9787c
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 5f6ea5ea859997e69e646bb65d3505a2d7f65b51a73f22a3613bf91970fbf2890cc9ef99253f482b6ea9c424d6fd85c6ceb3a41ab16d43f3192b5befefb035fc
|
|
7
|
+
data.tar.gz: 9f06de8f00cce839914fdce6ed191e84c447d03ab0687ba948d2fb791943cb2428859efd7a06260ed0638b962f5d85ea45b1e2e2a5679e0af80d03c277e0078a
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.1.2
|
data/.travis.yml
ADDED
data/Gemfile
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
source "
|
|
1
|
+
source "https://rubygems.org"
|
|
2
2
|
|
|
3
3
|
# Specify your gem's dependencies in message_router.gemspec
|
|
4
4
|
gemspec
|
|
5
5
|
|
|
6
|
-
group :test, :development do
|
|
6
|
+
group :ci, :test, :development do
|
|
7
7
|
gem 'rspec'
|
|
8
|
-
gem '
|
|
8
|
+
gem 'rake'
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
group :test, :development do
|
|
12
|
+
gem 'listen'
|
|
9
13
|
gem 'growl'
|
|
10
14
|
gem 'guard'
|
|
11
15
|
gem 'guard-rspec'
|
|
12
|
-
end
|
|
16
|
+
end
|
data/Gemfile.lock
CHANGED
|
@@ -1,27 +1,58 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
message_router (0.
|
|
4
|
+
message_router (0.2.0)
|
|
5
5
|
|
|
6
6
|
GEM
|
|
7
|
-
remote:
|
|
7
|
+
remote: https://rubygems.org/
|
|
8
8
|
specs:
|
|
9
|
-
|
|
9
|
+
celluloid (0.16.0)
|
|
10
|
+
timers (~> 4.0.0)
|
|
11
|
+
coderay (1.1.0)
|
|
12
|
+
diff-lcs (1.2.5)
|
|
13
|
+
ffi (1.9.6)
|
|
14
|
+
formatador (0.2.5)
|
|
10
15
|
growl (1.0.3)
|
|
11
|
-
guard (
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
rspec
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
guard (2.6.1)
|
|
17
|
+
formatador (>= 0.2.4)
|
|
18
|
+
listen (~> 2.7)
|
|
19
|
+
lumberjack (~> 1.0)
|
|
20
|
+
pry (>= 0.9.12)
|
|
21
|
+
thor (>= 0.18.1)
|
|
22
|
+
guard-rspec (4.3.1)
|
|
23
|
+
guard (~> 2.1)
|
|
24
|
+
rspec (>= 2.14, < 4.0)
|
|
25
|
+
hitimes (1.2.2)
|
|
26
|
+
listen (2.7.11)
|
|
27
|
+
celluloid (>= 0.15.2)
|
|
28
|
+
rb-fsevent (>= 0.9.3)
|
|
29
|
+
rb-inotify (>= 0.9)
|
|
30
|
+
lumberjack (1.0.9)
|
|
31
|
+
method_source (0.8.2)
|
|
32
|
+
pry (0.10.1)
|
|
33
|
+
coderay (~> 1.1.0)
|
|
34
|
+
method_source (~> 0.8.1)
|
|
35
|
+
slop (~> 3.4)
|
|
36
|
+
rake (10.3.2)
|
|
37
|
+
rb-fsevent (0.9.4)
|
|
38
|
+
rb-inotify (0.9.5)
|
|
39
|
+
ffi (>= 0.5.0)
|
|
40
|
+
rspec (3.1.0)
|
|
41
|
+
rspec-core (~> 3.1.0)
|
|
42
|
+
rspec-expectations (~> 3.1.0)
|
|
43
|
+
rspec-mocks (~> 3.1.0)
|
|
44
|
+
rspec-core (3.1.7)
|
|
45
|
+
rspec-support (~> 3.1.0)
|
|
46
|
+
rspec-expectations (3.1.2)
|
|
47
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
48
|
+
rspec-support (~> 3.1.0)
|
|
49
|
+
rspec-mocks (3.1.3)
|
|
50
|
+
rspec-support (~> 3.1.0)
|
|
51
|
+
rspec-support (3.1.2)
|
|
52
|
+
slop (3.6.0)
|
|
53
|
+
thor (0.19.1)
|
|
54
|
+
timers (4.0.1)
|
|
55
|
+
hitimes
|
|
25
56
|
|
|
26
57
|
PLATFORMS
|
|
27
58
|
ruby
|
|
@@ -30,6 +61,7 @@ DEPENDENCIES
|
|
|
30
61
|
growl
|
|
31
62
|
guard
|
|
32
63
|
guard-rspec
|
|
64
|
+
listen
|
|
33
65
|
message_router!
|
|
34
|
-
|
|
66
|
+
rake
|
|
35
67
|
rspec
|
data/Guardfile
CHANGED
|
@@ -1,8 +1,32 @@
|
|
|
1
1
|
# A sample Guardfile
|
|
2
2
|
# More info at https://github.com/guard/guard#readme
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
# Note: The cmd option is now required due to the increasing number of ways
|
|
5
|
+
# rspec may be run, below are examples of the most common uses.
|
|
6
|
+
# * bundler: 'bundle exec rspec'
|
|
7
|
+
# * bundler binstubs: 'bin/rspec'
|
|
8
|
+
# * spring: 'bin/rsspec' (This will use spring if running and you have
|
|
9
|
+
# installed the spring binstubs per the docs)
|
|
10
|
+
# * zeus: 'zeus rspec' (requires the server to be started separetly)
|
|
11
|
+
# * 'just' rspec: 'rspec'
|
|
12
|
+
guard :rspec, cmd: 'bundle exec rspec' do
|
|
13
|
+
watch(%r{^spec/.+_spec\.rb$})
|
|
14
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
|
15
|
+
watch('spec/spec_helper.rb') { "spec" }
|
|
16
|
+
|
|
17
|
+
# Rails example
|
|
18
|
+
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
|
19
|
+
watch(%r{^app/(.*)(\.erb|\.haml|\.slim)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
|
|
20
|
+
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
|
|
21
|
+
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
|
22
|
+
watch('config/routes.rb') { "spec/routing" }
|
|
23
|
+
watch('app/controllers/application_controller.rb') { "spec/controllers" }
|
|
24
|
+
watch('spec/rails_helper.rb') { "spec" }
|
|
25
|
+
|
|
26
|
+
# Capybara features specs
|
|
27
|
+
watch(%r{^app/views/(.+)/.*\.(erb|haml|slim)$}) { |m| "spec/features/#{m[1]}_spec.rb" }
|
|
28
|
+
|
|
29
|
+
# Turnip features and steps
|
|
30
|
+
watch(%r{^spec/acceptance/(.+)\.feature$})
|
|
31
|
+
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
|
|
32
|
+
end
|
data/README.rdoc
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
= Message Router
|
|
2
2
|
|
|
3
|
+
{<img src="https://travis-ci.org/polleverywhere/message_router.svg?branch=master" alt="Build Status" />}[https://travis-ci.org/polleverywhere/message_router]
|
|
4
|
+
{<img src="https://codeclimate.com/github/polleverywhere/message_router/badges/gpa.svg" />}[https://codeclimate.com/github/polleverywhere/message_router]
|
|
5
|
+
|
|
3
6
|
Message router is a DSL for routing and processing simple messages, like SMS messages or Tweets.
|
|
4
7
|
|
|
5
8
|
== Installation
|
|
@@ -12,30 +15,30 @@ See rdoc for MessageRouter::Router.build (lib/message_router/router.rb) for exam
|
|
|
12
15
|
|
|
13
16
|
And now some irb action.
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
Hi there. You sent me: {'body'=>"can you say hi to me?"}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
WTF? I don't know how to do that!
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
Hi there. You sent me: {'body'=>"can you say hi to me?", 'to'=>"greeter"}
|
|
38
|
-
|
|
18
|
+
class HelloRouter < MessageRouter::Router
|
|
19
|
+
match /hi/ do
|
|
20
|
+
puts "Hi there. You sent me: #{env.inspect}"
|
|
21
|
+
true # puts returns nil, and that would fail the matcher
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
# => [[/hi/, #<Proc:0x00000000026963b8@(irb):2>]]
|
|
25
|
+
HelloRouter.call({'body' => 'can you say hi to me?'})
|
|
26
|
+
# Hi there. You sent me: {'body'=>"can you say hi to me?"}
|
|
27
|
+
# => true
|
|
28
|
+
class MainRouter < MessageRouter::Router
|
|
29
|
+
match({'to' => 'greeter'}, HelloRouter)
|
|
30
|
+
match(true) do
|
|
31
|
+
puts "WTF? I don't know how to do that!"
|
|
32
|
+
true # puts returns nil, and that would fail the matcher
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
# => [[{"to"=>"greeter"}, HelloRouter], [true, #<Proc:0x007f98c39e5b70@(irb):13>]]
|
|
36
|
+
MainRouter.call({'body' => 'can you say hi to me?'})
|
|
37
|
+
# WTF? I don't know how to do that!
|
|
38
|
+
# => true
|
|
39
|
+
MainRouter.call({'body' => 'can you say hi to me?', 'to' => 'greeter'})
|
|
40
|
+
# Hi there. You sent me: {'body'=>"can you say hi to me?", 'to'=>"greeter"}
|
|
41
|
+
# => true
|
|
39
42
|
|
|
40
43
|
|
|
41
44
|
== TODO
|
|
@@ -57,7 +60,7 @@ Add tests to ensure that instance variables can be shared between initializers a
|
|
|
57
60
|
@sender.puts 'something'
|
|
58
61
|
end
|
|
59
62
|
end
|
|
60
|
-
MyRouter
|
|
63
|
+
MyRouter(:sender => STDOUT).call({}) # prints out 'something' to standard out.
|
|
61
64
|
|
|
62
65
|
Pass Regexp captures on to the proc when there is a match. Examples:
|
|
63
66
|
match /some (cool|awesome) thing/ do |match|
|
|
@@ -106,17 +109,17 @@ Make helper methods defined (or included) in parent routers available in sub rou
|
|
|
106
109
|
Pass around copies of the env Hash instead of modifying the existing Hash in place.
|
|
107
110
|
* This _might_ help with multi-threading
|
|
108
111
|
* Perhaps a parent router wants to delegate to 2 sub-routers which are independent of each other. The current implementation has a shared env hash, so I couldn't use multi-threading, (though forking could work). I would have to trust the user to call #dup on at least one of the env hashes. With this new way, it is safe by default.
|
|
109
|
-
* Convention would be for the '
|
|
112
|
+
* Convention would be for the 'condition' Procs to return a copy of the env hashes, either modified or not, depending on their needs.
|
|
110
113
|
* They would still return nil or false if they don't match.
|
|
111
114
|
* We could also require (by convention only) that sub-routers also return a copy of the env hash (if they succeed) so this (optionally modified) env hash can be used for further routing.
|
|
112
115
|
* This would give the original router access to both the modified env hash and the original env hash.
|
|
113
116
|
|
|
114
|
-
Find a way to allow user-defined '
|
|
117
|
+
Find a way to allow user-defined 'action' procs/blocks to not have to return a true value to be considered to have matched. We still need a way to know if a sub-router matched or not. This _may_require that the code treat sub-routers and user-defined 'action' procs/blocks differently, which could get awkward.
|
|
115
118
|
|
|
116
119
|
Allow routers to accept an optional logger. Depending on the log level, print out info such as:
|
|
117
120
|
* When a matcher is registered
|
|
118
121
|
* Each time a matcher is evaluated, including what the return value was.
|
|
119
|
-
* Each time a '
|
|
122
|
+
* Each time a 'action' block is evaluated, including what the return value was.
|
|
120
123
|
Each time we write to the log include the following (depending on the log level):
|
|
121
124
|
* The value of the env hash
|
|
122
125
|
* The name of the class (so we can tell which subclass we are in)
|
|
@@ -132,21 +135,21 @@ Consider having a class called Run nested within the router's namespace. Instead
|
|
|
132
135
|
end
|
|
133
136
|
Run#run might look something like:
|
|
134
137
|
def run
|
|
135
|
-
router.rules.detect do |
|
|
136
|
-
|
|
137
|
-
self.instance_eval &
|
|
138
|
+
router.rules.detect do |condition, action|
|
|
139
|
+
condition = if condition.kind_of?(Proc)
|
|
140
|
+
self.instance_eval &condition
|
|
138
141
|
else
|
|
139
|
-
|
|
142
|
+
condition.call env
|
|
140
143
|
end
|
|
141
144
|
|
|
142
|
-
if
|
|
143
|
-
|
|
144
|
-
self.instance_eval &
|
|
145
|
+
if condition
|
|
146
|
+
action = if action.kind_of?(Proc)
|
|
147
|
+
self.instance_eval &action
|
|
145
148
|
else
|
|
146
|
-
|
|
149
|
+
action.call env
|
|
147
150
|
end
|
|
148
151
|
|
|
149
|
-
return
|
|
152
|
+
return action if action
|
|
150
153
|
end
|
|
151
154
|
end
|
|
152
155
|
end
|
|
@@ -8,11 +8,11 @@ class MessageRouter
|
|
|
8
8
|
#
|
|
9
9
|
# prerequisite :db_connected?
|
|
10
10
|
#
|
|
11
|
-
# match SomeOtherRouter
|
|
11
|
+
# match SomeOtherRouter
|
|
12
12
|
# # `mount` is an alias of `match`
|
|
13
|
-
# mount AnotherRouter
|
|
13
|
+
# mount AnotherRouter
|
|
14
14
|
#
|
|
15
|
-
# match(
|
|
15
|
+
# match(lambda { env['from'].nil? }) do
|
|
16
16
|
# Logger.error "Can't reply when when don't know who a message is from: #{env.inspect}"
|
|
17
17
|
# end
|
|
18
18
|
#
|
|
@@ -40,13 +40,13 @@ class MessageRouter
|
|
|
40
40
|
# send_reply "Sorry, you are trying to use a deprecated short code. Please try again.", env
|
|
41
41
|
# end
|
|
42
42
|
#
|
|
43
|
-
# match :user_name => PriorityUsernameRouter
|
|
44
|
-
# match :user_name, OldStyleUsernameRouter
|
|
43
|
+
# match :user_name => PriorityUsernameRouter
|
|
44
|
+
# match :user_name, OldStyleUsernameRouter
|
|
45
45
|
# match :user_name do
|
|
46
46
|
# send_reply "I found you! Your name is #{user_name}.", env
|
|
47
47
|
# end
|
|
48
48
|
#
|
|
49
|
-
# match %w(stop end quit), StopRouter
|
|
49
|
+
# match %w(stop end quit), StopRouter
|
|
50
50
|
#
|
|
51
51
|
# # Array elements don't need to be the same type
|
|
52
52
|
# match [
|
|
@@ -54,7 +54,7 @@ class MessageRouter
|
|
|
54
54
|
# {'to' => %w(12345 54321)},
|
|
55
55
|
# {'RAILS_ENV' => 'test'},
|
|
56
56
|
# 'test'
|
|
57
|
-
# ], TestRouter
|
|
57
|
+
# ], TestRouter
|
|
58
58
|
#
|
|
59
59
|
# # Works inside a Hash too
|
|
60
60
|
# match 'from' => ['12345', '54321', /111\d\d/] do
|
|
@@ -80,7 +80,7 @@ class MessageRouter
|
|
|
80
80
|
# end
|
|
81
81
|
# end
|
|
82
82
|
#
|
|
83
|
-
# router = MyApp::Router::Application
|
|
83
|
+
# router = MyApp::Router::Application
|
|
84
84
|
# router.call({}) # Logs an error about not knowing who the message is from
|
|
85
85
|
# router.call({'from' => 'mr-smith', 'body' => 'ping'}) # Sends a 'pong' reply
|
|
86
86
|
# router.call({'from' => 'mr-smith', 'to' => 12345}) # Sends a deprecation warning reply
|
|
@@ -149,28 +149,31 @@ class MessageRouter
|
|
|
149
149
|
when 0
|
|
150
150
|
raise ArgumentError, "You must provide either a block or an argument which responds to call."
|
|
151
151
|
when 1
|
|
152
|
-
if args[0].respond_to?(:
|
|
153
|
-
|
|
154
|
-
|
|
152
|
+
if args[0].respond_to?(:env)
|
|
153
|
+
condition = true
|
|
154
|
+
action = args[0]
|
|
155
|
+
elsif args[0].respond_to?(:call)
|
|
156
|
+
condition = true
|
|
157
|
+
action = args[0]
|
|
155
158
|
elsif args[0].kind_of?(Hash) && args[0].values.size == 1 && args[0].values[0].respond_to?(:call)
|
|
156
159
|
# Syntactical suger to make:
|
|
157
|
-
# match :cool? => OnlyForCoolPeopleRouter
|
|
160
|
+
# match :cool? => OnlyForCoolPeopleRouter
|
|
158
161
|
# work just like:
|
|
159
|
-
# match :cool?, OnlyForCoolPeopleRouter
|
|
160
|
-
|
|
161
|
-
|
|
162
|
+
# match :cool?, OnlyForCoolPeopleRouter
|
|
163
|
+
condition = args[0].keys[0]
|
|
164
|
+
action = args[0].values[0]
|
|
162
165
|
else
|
|
163
166
|
raise ArgumentError, "You must provide either a block or a 2nd argument which responds to call."
|
|
164
167
|
end
|
|
165
168
|
when 2
|
|
166
|
-
|
|
167
|
-
raise ArgumentError, "The 2nd argument must respond to call." unless
|
|
169
|
+
condition, action = args
|
|
170
|
+
raise ArgumentError, "The 2nd argument must respond to call." unless action.respond_to?(:call)
|
|
168
171
|
else
|
|
169
172
|
raise ArgumentError, "Too many arguments. Note: you may not provide a block when a 2nd argument has been provided."
|
|
170
173
|
end
|
|
171
174
|
|
|
172
175
|
# Save the arguments for later.
|
|
173
|
-
rules << [
|
|
176
|
+
rules << [condition, action]
|
|
174
177
|
end
|
|
175
178
|
alias :mount :match
|
|
176
179
|
|
|
@@ -193,13 +196,34 @@ class MessageRouter
|
|
|
193
196
|
def prerequisites
|
|
194
197
|
@prerequisites ||= []
|
|
195
198
|
end
|
|
199
|
+
|
|
200
|
+
# Kicks off the router. 'env' is a Hash. The keys are up to the user;
|
|
201
|
+
# however, the default key (used when a matcher is just a String or Regexp)
|
|
202
|
+
# is 'body'. If you don't specify this key, then String and Regexp matchers
|
|
203
|
+
# will always be false.
|
|
204
|
+
# Returns a new instance of this class, that gets run before being returned
|
|
205
|
+
# A rule "matches" if its condition return true and the action does not
|
|
206
|
+
# explicitly call not_matched. For example:
|
|
207
|
+
# match(true) { }
|
|
208
|
+
# matches. However:
|
|
209
|
+
# match(true) { not_matched }
|
|
210
|
+
# does not count as a match. This allows us to mount sub-routers and
|
|
211
|
+
# continue trying other rules if those subrouters fail to match something.
|
|
212
|
+
def call(env)
|
|
213
|
+
new(env).run
|
|
214
|
+
end
|
|
196
215
|
end
|
|
197
216
|
|
|
198
217
|
|
|
199
218
|
# This method initializes all the rules stored at the class level. When you
|
|
200
219
|
# create your subclass, if you want to add your own initializer, it is very
|
|
201
220
|
# important to call `super` or none of your rules will be matched.
|
|
202
|
-
def initialize
|
|
221
|
+
def initialize(env) #:nodoc:
|
|
222
|
+
@env = env.dup
|
|
223
|
+
# a parent router may be assuming a successful match
|
|
224
|
+
# but this subrouter may not, so we explicitly set it to not matched
|
|
225
|
+
# on creation
|
|
226
|
+
not_matched
|
|
203
227
|
@rules = []
|
|
204
228
|
# Actually create the rules so that the procs we create are in the
|
|
205
229
|
# context of an instance of this object. This is most important when the
|
|
@@ -209,100 +233,96 @@ class MessageRouter
|
|
|
209
233
|
self.class.rules.each {|rule| match *rule }
|
|
210
234
|
|
|
211
235
|
@prerequisites = []
|
|
212
|
-
self.class.prerequisites.each
|
|
236
|
+
self.class.prerequisites.each do |prerequisite|
|
|
237
|
+
@prerequisites << normalize_match_params(prerequisite)
|
|
238
|
+
end
|
|
213
239
|
end
|
|
214
240
|
|
|
215
|
-
|
|
216
|
-
# however, the default key (used when a matcher is just a String or Regexp)
|
|
217
|
-
# is 'body'. If you don't specify this key, then String and Regexp matchers
|
|
218
|
-
# will always be false.
|
|
219
|
-
# Returns nil if no rules match
|
|
220
|
-
# Returns true if a rule matches
|
|
221
|
-
# A rule "matches" if both its procs return true. For example:
|
|
222
|
-
# match(true) { true }
|
|
223
|
-
# matches. However:
|
|
224
|
-
# match(true) { false }
|
|
225
|
-
# does not count as a match. This allows us to mount sub-routers and
|
|
226
|
-
# continue trying other rules if those subrouters fail to match something.
|
|
227
|
-
# However, this does mean you need to be careful when writing the 2nd
|
|
228
|
-
# argument to #match. If you return nil or false, the router will keep
|
|
229
|
-
# looking for another match.
|
|
230
|
-
def call(env)
|
|
231
|
-
# I'm pretty sure this is NOT thread safe. Having two threads use the
|
|
232
|
-
# same router at the same time will almost certainly give you VERY weird
|
|
233
|
-
# and incorrect results. We may want to introduce a RouterRun object to
|
|
234
|
-
# encapsulate one invocation of this #call method.
|
|
235
|
-
@env = env
|
|
236
|
-
|
|
241
|
+
def run #:nodoc:
|
|
237
242
|
# All prerequisites must return true in order to continue.
|
|
238
|
-
return
|
|
239
|
-
|
|
240
|
-
self.instance_eval &should_i
|
|
241
|
-
else
|
|
242
|
-
should_i.call @env
|
|
243
|
-
end
|
|
243
|
+
return self unless @prerequisites.all? do |condition|
|
|
244
|
+
self.instance_eval &condition
|
|
244
245
|
end
|
|
245
246
|
|
|
246
|
-
@rules.detect do |
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
247
|
+
@rules.detect do |condition, action|
|
|
248
|
+
if self.instance_eval &condition
|
|
249
|
+
matched
|
|
250
|
+
r = self.instance_eval &action
|
|
251
|
+
@env = r.respond_to?(:env) ? r.env : r
|
|
252
|
+
return self if matched?
|
|
251
253
|
end
|
|
254
|
+
end
|
|
252
255
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
self.instance_eval &do_this
|
|
256
|
-
else
|
|
257
|
-
do_this.call @env
|
|
258
|
-
end
|
|
256
|
+
self # always return router instance
|
|
257
|
+
end
|
|
259
258
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
259
|
+
def not_matched
|
|
260
|
+
env['_matched'] = false
|
|
261
|
+
end
|
|
262
|
+
def matched
|
|
263
|
+
env['_matched'] = true
|
|
264
|
+
end
|
|
265
|
+
def matched?
|
|
266
|
+
!!env['_matched']
|
|
265
267
|
end
|
|
266
268
|
|
|
267
269
|
|
|
268
|
-
private
|
|
269
270
|
def env; @env; end
|
|
271
|
+
private
|
|
270
272
|
|
|
271
|
-
def match(
|
|
272
|
-
@rules << [normalize_match_params(
|
|
273
|
+
def match(condition, action)
|
|
274
|
+
@rules << [normalize_match_params(condition), normalize_action(action)]
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def normalize_action(action)
|
|
278
|
+
if action.kind_of?(Proc) # This is true for blocks and lamdas too.
|
|
279
|
+
Proc.new do
|
|
280
|
+
self.instance_eval &action
|
|
281
|
+
env
|
|
282
|
+
end
|
|
283
|
+
else
|
|
284
|
+
Proc.new do
|
|
285
|
+
action.call(env)
|
|
286
|
+
end
|
|
287
|
+
end
|
|
273
288
|
end
|
|
274
289
|
|
|
275
|
-
def normalize_match_params(
|
|
276
|
-
|
|
290
|
+
def normalize_match_params(condition=nil, &block)
|
|
291
|
+
condition ||= block if block
|
|
277
292
|
|
|
278
|
-
case
|
|
293
|
+
case condition
|
|
279
294
|
when Regexp, String
|
|
280
|
-
Proc.new { attr_matches? default_attribute,
|
|
295
|
+
Proc.new { attr_matches? default_attribute, condition }
|
|
281
296
|
|
|
282
297
|
when TrueClass, FalseClass, NilClass
|
|
283
|
-
Proc.new {
|
|
298
|
+
Proc.new { condition }
|
|
284
299
|
|
|
285
300
|
when Symbol
|
|
286
301
|
Proc.new do
|
|
287
|
-
self.send
|
|
302
|
+
self.send condition
|
|
288
303
|
end
|
|
289
304
|
|
|
290
305
|
when Array
|
|
291
|
-
|
|
306
|
+
condition = condition.map {|x| normalize_match_params x}
|
|
292
307
|
Proc.new do
|
|
293
|
-
|
|
308
|
+
condition.any? { |x| x.call env }
|
|
294
309
|
end
|
|
295
310
|
|
|
296
311
|
when Hash
|
|
297
312
|
Proc.new do
|
|
298
|
-
|
|
313
|
+
condition.all? do |key, val|
|
|
299
314
|
attr_matches? env[key], val
|
|
300
315
|
end
|
|
301
316
|
end
|
|
302
317
|
|
|
318
|
+
when Proc
|
|
319
|
+
condition
|
|
320
|
+
|
|
303
321
|
else
|
|
304
322
|
# Assume it already responds to #call.
|
|
305
|
-
|
|
323
|
+
Proc.new do
|
|
324
|
+
condition.call env
|
|
325
|
+
end
|
|
306
326
|
end
|
|
307
327
|
end
|
|
308
328
|
|
data/message_router.gemspec
CHANGED
|
@@ -5,8 +5,8 @@ require "message_router/version"
|
|
|
5
5
|
Gem::Specification.new do |s|
|
|
6
6
|
s.name = "message_router"
|
|
7
7
|
s.version = MessageRouter::VERSION
|
|
8
|
-
s.authors = ["Brad Gessler", "Paul Cortens"]
|
|
9
|
-
s.email = ["brad@bradgessler.com", "paul@thoughtless.ca"]
|
|
8
|
+
s.authors = ["Brad Gessler", "Paul Cortens", "Christopher Bertels"]
|
|
9
|
+
s.email = ["brad@bradgessler.com", "paul@thoughtless.ca", "bakkdoor@flasht.de"]
|
|
10
10
|
s.homepage = ""
|
|
11
11
|
s.summary = %q{Route messages}
|
|
12
12
|
s.description = %q{a DSL for routing SMS, Twitter, and other short message formats.}
|
data/spec/message_router_spec.rb
CHANGED
|
@@ -5,11 +5,6 @@ describe MessageRouter::Router do
|
|
|
5
5
|
|
|
6
6
|
describe 'defining matchers' do
|
|
7
7
|
describe '1st argument' do
|
|
8
|
-
before do
|
|
9
|
-
# For use in confirming whether or not a proc was called.
|
|
10
|
-
$thing_to_match = $did_it_run = nil
|
|
11
|
-
end
|
|
12
|
-
|
|
13
8
|
let :env do
|
|
14
9
|
{
|
|
15
10
|
'body' => 'hello world',
|
|
@@ -19,10 +14,10 @@ describe MessageRouter::Router do
|
|
|
19
14
|
end
|
|
20
15
|
|
|
21
16
|
# This needs to be a method (and not memoized by #let) so that
|
|
22
|
-
#
|
|
23
|
-
def router
|
|
17
|
+
# thing_to_match can change within a test.
|
|
18
|
+
def router(thing_to_match)
|
|
24
19
|
Class.new MessageRouter::Router do
|
|
25
|
-
match(
|
|
20
|
+
match(thing_to_match) { env['did_it_run'] = true }
|
|
26
21
|
|
|
27
22
|
# Using these methods also proves that the message is optionally
|
|
28
23
|
# passed to helper methods.
|
|
@@ -32,20 +27,16 @@ describe MessageRouter::Router do
|
|
|
32
27
|
def always_false
|
|
33
28
|
false
|
|
34
29
|
end
|
|
35
|
-
end
|
|
30
|
+
end
|
|
36
31
|
end
|
|
37
32
|
|
|
38
33
|
let :the_test do
|
|
39
34
|
Proc.new do |opts|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
$did_it_run.should == true
|
|
43
|
-
$did_it_run = nil # reset for next time
|
|
35
|
+
r = router(opts[:true]).call(env.dup)
|
|
36
|
+
expect(r.env['did_it_run']).to eq true
|
|
44
37
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
$did_it_run.should == nil
|
|
48
|
-
$did_it_run = nil # reset for next time
|
|
38
|
+
r = router(opts[:false]).call(env.dup)
|
|
39
|
+
expect(r.env['did_it_run']).to eq nil
|
|
49
40
|
end
|
|
50
41
|
end
|
|
51
42
|
|
|
@@ -82,27 +73,27 @@ describe MessageRouter::Router do
|
|
|
82
73
|
def default_attribute
|
|
83
74
|
env['tacos']
|
|
84
75
|
end
|
|
85
|
-
end
|
|
76
|
+
end
|
|
86
77
|
end
|
|
87
78
|
|
|
88
79
|
it 'accepts a string to match against the 1st word in the default attribute' do
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
env['result'].
|
|
80
|
+
r = router.call({ 'tacos' => 'cheese please' })
|
|
81
|
+
expect(r.matched?).to be_truthy
|
|
82
|
+
expect(r.env['result']).to eq 'i found cheese'
|
|
92
83
|
end
|
|
93
84
|
it "does not match strings against the 'body' attribute" do
|
|
94
|
-
|
|
95
|
-
|
|
85
|
+
r = router.call({ 'body' => 'cheese please' })
|
|
86
|
+
expect(r.matched?).to be_falsey
|
|
96
87
|
end
|
|
97
88
|
|
|
98
89
|
it 'accepts a regex to match against the default attribute' do
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
env['result'].
|
|
90
|
+
r = router.call({ 'tacos' => 'i like beans a lot' })
|
|
91
|
+
expect(r.matched?).to be_truthy
|
|
92
|
+
expect(r.env['result']).to eq 'magical fruit'
|
|
102
93
|
end
|
|
103
94
|
it "does not match regex against the 'body' attribute" do
|
|
104
|
-
|
|
105
|
-
|
|
95
|
+
r = router.call({ 'body' => 'i like beans a lot' })
|
|
96
|
+
expect(r.matched?).to be_falsey
|
|
106
97
|
end
|
|
107
98
|
end
|
|
108
99
|
|
|
@@ -115,31 +106,18 @@ describe MessageRouter::Router do
|
|
|
115
106
|
end
|
|
116
107
|
|
|
117
108
|
describe 'matching an Array' do
|
|
118
|
-
it "doesn't run the '
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
match [true, true] do
|
|
122
|
-
$run_count += 1
|
|
123
|
-
nil # Return nil to ensure this matcher failed.
|
|
124
|
-
end
|
|
125
|
-
end.new
|
|
126
|
-
|
|
127
|
-
router.call({})
|
|
128
|
-
$run_count.should == 1
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
it "returns nil if the 'do_this' block returns nil" do
|
|
132
|
-
$run_count = 0
|
|
109
|
+
it "doesn't run the 'action' block multiple times if there are multiple matches" do
|
|
110
|
+
run = double
|
|
111
|
+
expect(run).to receive(:count).once
|
|
133
112
|
router = Class.new(MessageRouter::Router) do
|
|
134
113
|
match [true, true] do
|
|
135
|
-
|
|
136
|
-
|
|
114
|
+
run.count
|
|
115
|
+
not_matched
|
|
137
116
|
end
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
|
|
117
|
+
end
|
|
118
|
+
r = router.call({})
|
|
119
|
+
expect(r.matched?).to be_falsey
|
|
141
120
|
end
|
|
142
|
-
|
|
143
121
|
end
|
|
144
122
|
|
|
145
123
|
describe 'matching a hash' do
|
|
@@ -181,10 +159,8 @@ describe MessageRouter::Router do
|
|
|
181
159
|
end
|
|
182
160
|
|
|
183
161
|
it 'accepts keys that are missing (but is always false)' do
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
$did_it_run.should == nil
|
|
187
|
-
$did_it_run = nil # reset for next time
|
|
162
|
+
r = router({'i dont exist' => /.*/}).call(env)
|
|
163
|
+
expect(r.env['did_it_run']).to eq nil
|
|
188
164
|
end
|
|
189
165
|
end
|
|
190
166
|
end
|
|
@@ -194,34 +170,34 @@ describe MessageRouter::Router do
|
|
|
194
170
|
env = {}
|
|
195
171
|
router = Class.new MessageRouter::Router do
|
|
196
172
|
match(true, Proc.new { env['did_it_run'] = true })
|
|
197
|
-
end
|
|
173
|
+
end
|
|
198
174
|
router.call env
|
|
199
|
-
env['did_it_run'].
|
|
175
|
+
expect(env['did_it_run']).to be_truthy
|
|
200
176
|
end
|
|
201
177
|
|
|
202
178
|
it 'accepts a block' do
|
|
203
179
|
env = {}
|
|
204
180
|
router = Class.new MessageRouter::Router do
|
|
205
181
|
match(true) { env['did_it_run'] = true }
|
|
206
|
-
end
|
|
182
|
+
end
|
|
207
183
|
router.call env
|
|
208
|
-
env['did_it_run'].
|
|
184
|
+
expect(env['did_it_run']).to be_truthy
|
|
209
185
|
end
|
|
210
186
|
|
|
211
187
|
it 'raises an execption when both a Proc and a block are given' do
|
|
212
|
-
|
|
188
|
+
expect {
|
|
213
189
|
router = Class.new MessageRouter::Router do
|
|
214
190
|
match(true, Proc.new { env['did_it_run'] = true }) { env['did_it_run'] = true }
|
|
215
|
-
end
|
|
216
|
-
}.
|
|
191
|
+
end
|
|
192
|
+
}.to raise_error(ArgumentError)
|
|
217
193
|
end
|
|
218
194
|
|
|
219
195
|
it 'raises an execption when neither a Proc nor a block are given' do
|
|
220
|
-
|
|
196
|
+
expect {
|
|
221
197
|
router = Class.new MessageRouter::Router do
|
|
222
198
|
match true
|
|
223
|
-
end
|
|
224
|
-
}.
|
|
199
|
+
end
|
|
200
|
+
}.to raise_error(ArgumentError)
|
|
225
201
|
end
|
|
226
202
|
end
|
|
227
203
|
|
|
@@ -229,18 +205,18 @@ describe MessageRouter::Router do
|
|
|
229
205
|
env = {}
|
|
230
206
|
router = Class.new MessageRouter::Router do
|
|
231
207
|
match { env['did_it_run'] = true }
|
|
232
|
-
end
|
|
208
|
+
end
|
|
233
209
|
router.call env
|
|
234
|
-
env['did_it_run'].
|
|
210
|
+
expect(env['did_it_run']).to be_truthy
|
|
235
211
|
end
|
|
236
212
|
|
|
237
213
|
it 'defaults the 1st argument to true if only a Proc is given' do
|
|
238
214
|
env = {}
|
|
239
215
|
router = Class.new MessageRouter::Router do
|
|
240
216
|
match(Proc.new { env['did_it_run'] = true })
|
|
241
|
-
end
|
|
217
|
+
end
|
|
242
218
|
router.call env
|
|
243
|
-
env['did_it_run'].
|
|
219
|
+
expect(env['did_it_run']).to be_truthy
|
|
244
220
|
end
|
|
245
221
|
|
|
246
222
|
it 'accepts a Hash with a symbol as its only key and a Proc as its only value' do
|
|
@@ -248,41 +224,41 @@ describe MessageRouter::Router do
|
|
|
248
224
|
router = Class.new MessageRouter::Router do
|
|
249
225
|
match :true_method => (Proc.new { env['did_it_run'] = true })
|
|
250
226
|
def true_method; true; end
|
|
251
|
-
end
|
|
227
|
+
end
|
|
252
228
|
router.call env
|
|
253
|
-
env['did_it_run'].
|
|
229
|
+
expect(env['did_it_run']).to be_truthy
|
|
254
230
|
end
|
|
255
231
|
|
|
256
232
|
it 'raises an execption when no arguments and no block is given' do
|
|
257
|
-
|
|
233
|
+
expect {
|
|
258
234
|
router = Class.new MessageRouter::Router do
|
|
259
235
|
match
|
|
260
|
-
end
|
|
261
|
-
}.
|
|
236
|
+
end
|
|
237
|
+
}.to raise_error(ArgumentError)
|
|
262
238
|
end
|
|
263
239
|
end
|
|
264
240
|
|
|
265
241
|
|
|
266
242
|
describe "#call" do
|
|
267
|
-
it "
|
|
268
|
-
|
|
269
|
-
|
|
243
|
+
it "does not match with no rules" do
|
|
244
|
+
router = MessageRouter::Router.call({})
|
|
245
|
+
expect(router.matched?).to be_falsey
|
|
270
246
|
end
|
|
271
247
|
|
|
272
248
|
context 'a rule matches' do
|
|
273
249
|
subject do
|
|
274
250
|
Class.new MessageRouter::Router do
|
|
275
251
|
match(true) { env[:did_it_run] = true }
|
|
276
|
-
end
|
|
252
|
+
end
|
|
277
253
|
end
|
|
278
254
|
|
|
279
255
|
it "returns true" do
|
|
280
|
-
subject.call({}).
|
|
256
|
+
expect(subject.call({})).to be_truthy
|
|
281
257
|
end
|
|
282
258
|
|
|
283
259
|
it "calls the matcher's code" do
|
|
284
|
-
subject.call(env = {})
|
|
285
|
-
env[:did_it_run].
|
|
260
|
+
r = subject.call(env = {})
|
|
261
|
+
expect(r.env[:did_it_run]).to be_truthy
|
|
286
262
|
end
|
|
287
263
|
end
|
|
288
264
|
|
|
@@ -292,16 +268,16 @@ describe MessageRouter::Router do
|
|
|
292
268
|
prerequisite :true_method
|
|
293
269
|
match(true) { env[:did_it_run] = true }
|
|
294
270
|
def true_method; true; end
|
|
295
|
-
end
|
|
271
|
+
end
|
|
296
272
|
end
|
|
297
273
|
|
|
298
|
-
it "
|
|
299
|
-
subject.call({}).
|
|
274
|
+
it "matches" do
|
|
275
|
+
expect(subject.call({}).matched?).to be_truthy
|
|
300
276
|
end
|
|
301
277
|
|
|
302
278
|
it "calls the matcher's code" do
|
|
303
|
-
subject.call(env = {})
|
|
304
|
-
env[:did_it_run].
|
|
279
|
+
r = subject.call(env = {})
|
|
280
|
+
expect(r.env[:did_it_run]).to be_truthy
|
|
305
281
|
end
|
|
306
282
|
end
|
|
307
283
|
|
|
@@ -311,112 +287,126 @@ describe MessageRouter::Router do
|
|
|
311
287
|
prerequisite :false_method
|
|
312
288
|
match(true) { env[:did_it_run] = true }
|
|
313
289
|
def false_method; false; end
|
|
314
|
-
end
|
|
290
|
+
end
|
|
315
291
|
end
|
|
316
292
|
|
|
317
|
-
it "
|
|
318
|
-
subject.call({}).
|
|
293
|
+
it "doesn't match" do
|
|
294
|
+
expect(subject.call({}).matched?).to be_falsey
|
|
319
295
|
end
|
|
320
296
|
|
|
321
297
|
it "doesn't calls the matcher's code" do
|
|
322
298
|
subject.call(env = {})
|
|
323
|
-
env[:did_it_run].
|
|
299
|
+
expect(env[:did_it_run]).to_not be_truthy
|
|
324
300
|
end
|
|
325
301
|
end
|
|
326
302
|
|
|
327
|
-
describe '
|
|
328
|
-
|
|
303
|
+
describe 'mount routers' do
|
|
304
|
+
it "can delegate to other routers" do
|
|
305
|
+
sub_router = Class.new(MessageRouter::Router) do
|
|
306
|
+
match do
|
|
307
|
+
env['result'] = true
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
main_router = Class.new(MessageRouter::Router) do
|
|
312
|
+
mount sub_router
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
r = main_router.call({})
|
|
316
|
+
expect(r.env['result']).to eq true
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
describe "explicitly not matching" do
|
|
321
|
+
let(:router) do
|
|
329
322
|
Class.new(MessageRouter::Router) do
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
end
|
|
323
|
+
match do
|
|
324
|
+
not_matched
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
end
|
|
333
328
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
329
|
+
it "sets matched to false in env" do
|
|
330
|
+
r = router.call({})
|
|
331
|
+
expect(r.matched?).to be_falsey
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
describe "explicitly matching" do
|
|
336
|
+
let(:router) do
|
|
337
|
+
Class.new(MessageRouter::Router) do
|
|
338
|
+
match do
|
|
339
|
+
not_matched
|
|
337
340
|
end
|
|
338
|
-
|
|
341
|
+
match do
|
|
342
|
+
matched
|
|
343
|
+
end
|
|
344
|
+
end
|
|
339
345
|
end
|
|
340
346
|
|
|
341
|
-
|
|
342
|
-
|
|
347
|
+
it "sets matched to true in env" do
|
|
348
|
+
r = router.call({})
|
|
349
|
+
expect(r.matched?).to be_truthy
|
|
343
350
|
end
|
|
351
|
+
end
|
|
344
352
|
|
|
345
|
-
|
|
346
|
-
|
|
353
|
+
describe 'nested routers' do
|
|
354
|
+
def main_router(matcher_opts = {})
|
|
355
|
+
Class.new(MessageRouter::Router) do
|
|
356
|
+
sub_router = Class.new(MessageRouter::Router) do
|
|
357
|
+
match { not_matched; env['did_outer_run'] = true }
|
|
358
|
+
match(matcher_opts[:inner]) { env['did_inner_run'] = true }
|
|
359
|
+
end
|
|
347
360
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
$did_inner_run.should be_true
|
|
361
|
+
match matcher_opts[:outer] => sub_router
|
|
362
|
+
end
|
|
351
363
|
end
|
|
352
364
|
|
|
353
|
-
it
|
|
354
|
-
|
|
355
|
-
|
|
365
|
+
it 'runs both when both match' do
|
|
366
|
+
r = main_router(:outer => true, :inner => true).call({})
|
|
367
|
+
expect(r.env['did_outer_run']).to be_truthy
|
|
368
|
+
expect(r.env['did_inner_run']).to be_truthy
|
|
369
|
+
end
|
|
356
370
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
371
|
+
it "runs outer only when outer matches and inner doesn't" do
|
|
372
|
+
r = main_router(:outer => true, :inner => false).call({})
|
|
373
|
+
expect(r.env['did_outer_run']).to be_truthy
|
|
374
|
+
expect(r.env['did_inner_run']).to be_nil
|
|
360
375
|
end
|
|
361
376
|
|
|
362
377
|
it "runs neither when inner matches and outer doesn't" do
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
main_router.call({}).should be_nil
|
|
367
|
-
$did_outer_run.should be_nil
|
|
368
|
-
$did_inner_run.should be_nil
|
|
378
|
+
r = main_router(:outer => false, :inner => true).call({})
|
|
379
|
+
expect(r.env['did_outer_run']).to be_nil
|
|
380
|
+
expect(r.env['did_inner_run']).to be_nil
|
|
369
381
|
end
|
|
370
382
|
|
|
371
383
|
context 'multiple inner matchers' do
|
|
372
|
-
|
|
373
|
-
$outer_matcher_1 = $outer_matcher_2 = $inner_matcher_1 = $inner_matcher_2 = $did_outer_run_1 = $did_outer_run_2 = $did_inner_run_1 = $did_inner_run_2 = nil
|
|
374
|
-
end
|
|
375
|
-
|
|
376
|
-
def main_router
|
|
384
|
+
def main_router(inner_matcher_1)
|
|
377
385
|
Class.new MessageRouter::Router do
|
|
378
386
|
# Define them
|
|
379
387
|
sub_router_1 = Class.new MessageRouter::Router do
|
|
380
|
-
match(
|
|
381
|
-
end
|
|
388
|
+
match(inner_matcher_1) { env['did_inner_run_1'] = true }
|
|
389
|
+
end
|
|
382
390
|
sub_router_2 = Class.new MessageRouter::Router do
|
|
383
|
-
match(
|
|
384
|
-
end.new
|
|
385
|
-
|
|
386
|
-
# 'mount' them
|
|
387
|
-
match $outer_matcher_1 do
|
|
388
|
-
$did_outer_run_1 = true
|
|
389
|
-
sub_router_1.call(env)
|
|
391
|
+
match(true) { env['did_inner_run_2'] = true }
|
|
390
392
|
end
|
|
391
393
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
end
|
|
396
|
-
end.new
|
|
394
|
+
mount sub_router_1
|
|
395
|
+
mount sub_router_2
|
|
396
|
+
end
|
|
397
397
|
end
|
|
398
398
|
|
|
399
399
|
it "runs only 1st outer and 1st inner when all match" do
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
$did_outer_run_1.should be_true
|
|
404
|
-
$did_outer_run_2.should be_nil
|
|
405
|
-
$did_inner_run_1.should be_true
|
|
406
|
-
$did_inner_run_2.should be_nil
|
|
400
|
+
r = main_router(true).call({})
|
|
401
|
+
expect(r.env['did_inner_run_1']).to eq true
|
|
402
|
+
expect(r.env['did_inner_run_2']).to eq nil
|
|
407
403
|
end
|
|
408
404
|
|
|
409
405
|
it "runs both outers, and 2nd inner when all but 1st inner match" do
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
main_router.call({}).should be_true
|
|
414
|
-
$did_outer_run_1.should be_true
|
|
415
|
-
$did_outer_run_2.should be_true
|
|
416
|
-
$did_inner_run_1.should be_nil
|
|
417
|
-
$did_inner_run_2.should be_true
|
|
406
|
+
r = main_router(false).call({})
|
|
407
|
+
expect(r.env['did_inner_run_1']).to be_nil
|
|
408
|
+
expect(r.env['did_inner_run_2']).to be_truthy
|
|
418
409
|
end
|
|
419
|
-
|
|
420
410
|
end
|
|
421
411
|
end
|
|
422
412
|
|
|
@@ -437,7 +427,7 @@ describe MessageRouter::Router do
|
|
|
437
427
|
Class.new MessageRouter::Router do
|
|
438
428
|
include MyTestHelper
|
|
439
429
|
match :lookup_human_name do
|
|
440
|
-
|
|
430
|
+
env['is_john'] = env['human_name'] == 'John'
|
|
441
431
|
end
|
|
442
432
|
|
|
443
433
|
match 'run_a' => 'block' do
|
|
@@ -448,7 +438,7 @@ describe MessageRouter::Router do
|
|
|
448
438
|
env['id'] = 2
|
|
449
439
|
env['the_name'] = lookup_human_name
|
|
450
440
|
end)
|
|
451
|
-
match({'run_a' => 'lambda'}, lambda do
|
|
441
|
+
match({'run_a' => 'lambda'}, lambda do |args|
|
|
452
442
|
env['id'] = 2
|
|
453
443
|
env['the_name'] = lookup_human_name
|
|
454
444
|
end)
|
|
@@ -459,34 +449,53 @@ describe MessageRouter::Router do
|
|
|
459
449
|
lookup_human_name
|
|
460
450
|
end
|
|
461
451
|
) { true }
|
|
462
|
-
end
|
|
452
|
+
end
|
|
463
453
|
end
|
|
464
454
|
|
|
465
455
|
it 'can access/modify the env via #env' do
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
env['human_name'].
|
|
470
|
-
end
|
|
471
|
-
|
|
472
|
-
it '#env is reset after #call has finished' do
|
|
473
|
-
router.call({'id' => 1}).should be_true
|
|
474
|
-
router.send(:env).should be_nil
|
|
456
|
+
r = router.call({'id' => 1})
|
|
457
|
+
expect(r.matched?).to be_truthy
|
|
458
|
+
expect(r.env['is_john']).to be_truthy # Prove the inner matcher can see the new value
|
|
459
|
+
expect(r.env['human_name']).to eq 'John' # Prove we can get at the value after the router has finished.
|
|
475
460
|
end
|
|
476
461
|
|
|
477
462
|
%w(block proc lambda).each do |type|
|
|
478
463
|
it "can be accessed from a #{type} that is the 2nd argument" do
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
env['the_name'].
|
|
464
|
+
r = router.call({'run_a' => type})
|
|
465
|
+
expect(r.matched?).to be_truthy
|
|
466
|
+
expect(r.env['the_name']).to eq 'Jim'
|
|
482
467
|
end
|
|
483
468
|
end
|
|
484
469
|
|
|
485
470
|
%w(proc lambda).each do |type|
|
|
486
471
|
it "can be accessed from a #{type} that is the 1st argument" do
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
env['human_name'].
|
|
472
|
+
r = router.call({'match_with' => type})
|
|
473
|
+
expect(r.matched?).to be_truthy
|
|
474
|
+
expect(r.env['human_name']).to eq 'Jules'
|
|
475
|
+
end
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
describe "instance variables" do
|
|
479
|
+
let :router do
|
|
480
|
+
Class.new MessageRouter::Router do
|
|
481
|
+
prerequisite :helper_method
|
|
482
|
+
|
|
483
|
+
match do
|
|
484
|
+
env['result'] = @helper_method
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
private
|
|
488
|
+
def helper_method
|
|
489
|
+
@helper_method ||= 0
|
|
490
|
+
@helper_method += 1
|
|
491
|
+
end
|
|
492
|
+
end
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
it "doesn't leak state to a 2nd run" do
|
|
496
|
+
router.call({})
|
|
497
|
+
r = router.call({})
|
|
498
|
+
expect(r.env['result']).to eq 1
|
|
490
499
|
end
|
|
491
500
|
end
|
|
492
501
|
end
|
metadata
CHANGED
|
@@ -1,37 +1,29 @@
|
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: message_router
|
|
3
|
-
version: !ruby/object:Gem::Version
|
|
4
|
-
|
|
5
|
-
prerelease:
|
|
6
|
-
segments:
|
|
7
|
-
- 0
|
|
8
|
-
- 1
|
|
9
|
-
- 3
|
|
10
|
-
version: 0.1.3
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.2.0
|
|
11
5
|
platform: ruby
|
|
12
|
-
authors:
|
|
6
|
+
authors:
|
|
13
7
|
- Brad Gessler
|
|
14
8
|
- Paul Cortens
|
|
9
|
+
- Christopher Bertels
|
|
15
10
|
autorequire:
|
|
16
11
|
bindir: bin
|
|
17
12
|
cert_chain: []
|
|
18
|
-
|
|
19
|
-
date: 2012-09-10 00:00:00 Z
|
|
13
|
+
date: 2014-10-21 00:00:00.000000000 Z
|
|
20
14
|
dependencies: []
|
|
21
|
-
|
|
22
15
|
description: a DSL for routing SMS, Twitter, and other short message formats.
|
|
23
|
-
email:
|
|
16
|
+
email:
|
|
24
17
|
- brad@bradgessler.com
|
|
25
18
|
- paul@thoughtless.ca
|
|
19
|
+
- bakkdoor@flasht.de
|
|
26
20
|
executables: []
|
|
27
|
-
|
|
28
21
|
extensions: []
|
|
29
|
-
|
|
30
22
|
extra_rdoc_files: []
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
- .
|
|
34
|
-
- .
|
|
23
|
+
files:
|
|
24
|
+
- ".gitignore"
|
|
25
|
+
- ".ruby-version"
|
|
26
|
+
- ".travis.yml"
|
|
35
27
|
- Gemfile
|
|
36
28
|
- Gemfile.lock
|
|
37
29
|
- Guardfile
|
|
@@ -45,38 +37,30 @@ files:
|
|
|
45
37
|
- spec/message_router_spec.rb
|
|
46
38
|
- spec/spec.opts
|
|
47
39
|
- spec/spec_helper.rb
|
|
48
|
-
homepage:
|
|
40
|
+
homepage: ''
|
|
49
41
|
licenses: []
|
|
50
|
-
|
|
42
|
+
metadata: {}
|
|
51
43
|
post_install_message:
|
|
52
44
|
rdoc_options: []
|
|
53
|
-
|
|
54
|
-
require_paths:
|
|
45
|
+
require_paths:
|
|
55
46
|
- lib
|
|
56
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
|
57
|
-
|
|
58
|
-
requirements:
|
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
48
|
+
requirements:
|
|
59
49
|
- - ">="
|
|
60
|
-
- !ruby/object:Gem::Version
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
version: "0"
|
|
65
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
66
|
-
none: false
|
|
67
|
-
requirements:
|
|
50
|
+
- !ruby/object:Gem::Version
|
|
51
|
+
version: '0'
|
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
53
|
+
requirements:
|
|
68
54
|
- - ">="
|
|
69
|
-
- !ruby/object:Gem::Version
|
|
70
|
-
|
|
71
|
-
segments:
|
|
72
|
-
- 0
|
|
73
|
-
version: "0"
|
|
55
|
+
- !ruby/object:Gem::Version
|
|
56
|
+
version: '0'
|
|
74
57
|
requirements: []
|
|
75
|
-
|
|
76
58
|
rubyforge_project: message_router
|
|
77
|
-
rubygems_version:
|
|
59
|
+
rubygems_version: 2.2.2
|
|
78
60
|
signing_key:
|
|
79
|
-
specification_version:
|
|
61
|
+
specification_version: 4
|
|
80
62
|
summary: Route messages
|
|
81
|
-
test_files:
|
|
82
|
-
|
|
63
|
+
test_files:
|
|
64
|
+
- spec/message_router_spec.rb
|
|
65
|
+
- spec/spec.opts
|
|
66
|
+
- spec/spec_helper.rb
|
data/.rvmrc
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
rvm use --install ree
|