routemaster-drain 2.4.4 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cbb67c5fb3fc8aec8fe43fb6035f99672eb3ce87
4
- data.tar.gz: bb35752a729017d5e15d3189ce60f93ef60abd60
3
+ metadata.gz: 174d544b122fdee4b52eb706ce2d1324e416eea4
4
+ data.tar.gz: 2d33ba614a48f0e613adc1941a2126d17354b2a0
5
5
  SHA512:
6
- metadata.gz: b611d4238abac43c10ccd52413541cf272edd293694b42618084bc7393b63f2ca4e27d78e33cf71f4193369c54ae3916a0fca49b376eb99b86d220d7b8dae008
7
- data.tar.gz: 004e39f85620152ae1c6275e1edbaa4867badf20b5adda669207079740bc2da746531c86eaf43d0e83da11765b6a5803aff1b4da1fd09cd8bc3ff5a8883446ed
6
+ metadata.gz: 0b3cc42bfe4e62e6ecc2b60e71c69525d669447c0601ea5bf06a1b79c1fdab7b6c1e20e1494acb980ac5097615231b2b22fda210cca1fc43b5fd87875667d453
7
+ data.tar.gz: 4688fb67eb80241cf5fe28e5eed11be1617d083f8625fe378de960ee2506d45f105666ab556e69adb0944d592755d2ec9b302c31b6c8864ebdcc23070e02734d
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ### 2.5.0 (2017-04-11)
2
+
3
+ Features:
4
+
5
+ - Allow template urls to be defined in services (#38)
6
+ - Adds the `Siphon` middleware (#39, #44)
7
+ - Adds `CacheBusting` middleware (#40)
8
+
9
+ Bug fixes:
10
+
11
+ - Caching middleware always busts the cache on events - preventing stale events being cached in some circumstances (#40)
12
+
1
13
  ### 2.4.4 (2017-03-27)
2
14
 
3
15
  Features:
data/Gemfile.lock CHANGED
@@ -1,7 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- routemaster-drain (2.4.4)
4
+ routemaster-drain (2.5.0)
5
+ addressable
5
6
  concurrent-ruby
6
7
  faraday (>= 0.9.0)
7
8
  faraday_middleware
@@ -36,7 +37,7 @@ GEM
36
37
  dotenv (2.1.1)
37
38
  ethon (0.10.1)
38
39
  ffi (>= 1.3.0)
39
- faraday (0.11.0)
40
+ faraday (0.12.0.1)
40
41
  multipart-post (>= 1.2, < 3)
41
42
  faraday_middleware (0.11.0.1)
42
43
  faraday (>= 0.7.4, < 1.0)
data/README.md CHANGED
@@ -199,7 +199,7 @@ response = @cache.fget('https://example.com/widgets/123')
199
199
  puts response.body.id
200
200
  ```
201
201
 
202
- In this example, is your app was notified by Routemaster about Widget #123, the
202
+ In this example, if your app was notified by Routemaster about Widget #123, the
203
203
  cache will be very likely to be hit; and it will be invalidated automatically
204
204
  whenever the drain gets notified about a change on that widget.
205
205
 
@@ -210,6 +210,17 @@ See
210
210
  [rubydoc](http://rubydoc.info/github/deliveroo/routemaster-drain/Routemaster/Cache)
211
211
  for more details on `Cache`.
212
212
 
213
+ ### Expire Cache data for all notified resources
214
+
215
+ You may wish to maintain a coherent cache but don't need the cache to be warmed
216
+ before you process incoming events. In that case, use the cache as detailed above
217
+ but swap the `Caching` drain out for `CachingBusting`
218
+
219
+ ```ruby
220
+ require 'routemaster/drain/machine'
221
+ $app = Routemaster::Drain::CachingBusting.new
222
+ ```
223
+
213
224
  ## HTTP Client
214
225
  The Drain is using a Faraday http client for communication between services. The client
215
226
  comes with a convenient caching mechanism as a default and supports custom response materialization.
@@ -277,9 +288,10 @@ response.user.show(1)
277
288
 
278
289
  The more elaborate drains are built with two components which can also be used
279
290
  independently,
280
- [`Dirty::Map`](http://rubydoc.info/github/deliveroo/routemaster-drain/Routemaster/Dirty/Map)
281
- and
282
- [`Dirty::Filter`](http://rubydoc.info/github/deliveroo/routemaster-drain/Routemaster/Dirty/Filter).
291
+ [`Dirty::Map`](http://rubydoc.info/github/deliveroo/routemaster-drain/Routemaster/Dirty/Map),
292
+ [`Dirty::Filter`](http://rubydoc.info/github/deliveroo/routemaster-drain/Routemaster/Dirty/Filter) and
293
+ [`Middleware::Siphon`](http://www.rubydoc.info/github/deliveroo/routemaster-drain/master/Routemaster/Middleware/Siphon).
294
+
283
295
 
284
296
  ### Dirty map
285
297
 
@@ -314,6 +326,11 @@ It stores transient state in Redis and will emit `:entity_changed` events
314
326
  whenever an entity has changed. This event can usefully be fed into a dirty map,
315
327
  as in `Receiver::Filter` for instance.
316
328
 
329
+ ### Siphon
330
+
331
+ [`Middleware::Siphon`](http://www.rubydoc.info/github/deliveroo/routemaster-drain/master/Routemaster/Middleware/Siphon) extracts
332
+ payloads from the middleware chain, allowing them to be processed separately. This is useful for event topics where the update frequency is not well suited
333
+ to frequent caching. For example, a location update event which you'd expect to receive every few seconds.
317
334
 
318
335
  ## Contributing
319
336
 
@@ -327,3 +344,5 @@ Do not bump version numbers on branches (a maintainer will do this when cutting
327
344
  a release); but please do describe your changes in the `CHANGELOG` (at the top,
328
345
  without a version number).
329
346
 
347
+ If you have changed dependencies, you need to run `appraisal update` to make
348
+ sure the various version specific gemfiles are updated.
@@ -1,7 +1,8 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- routemaster-drain (2.4.4)
4
+ routemaster-drain (2.5.0)
5
+ addressable
5
6
  concurrent-ruby
6
7
  faraday (>= 0.9.0)
7
8
  faraday_middleware
@@ -41,7 +42,7 @@ GEM
41
42
  activesupport (3.2.22.5)
42
43
  i18n (~> 0.6, >= 0.6.4)
43
44
  multi_json (~> 1.0)
44
- addressable (2.5.0)
45
+ addressable (2.5.1)
45
46
  public_suffix (~> 2.0, >= 2.0.2)
46
47
  appraisal (2.1.0)
47
48
  bundler
@@ -50,7 +51,7 @@ GEM
50
51
  arel (3.0.3)
51
52
  builder (3.0.4)
52
53
  byebug (9.0.6)
53
- codecov (0.1.9)
54
+ codecov (0.1.10)
54
55
  json
55
56
  simplecov
56
57
  url
@@ -66,11 +67,11 @@ GEM
66
67
  erubis (2.7.0)
67
68
  ethon (0.10.1)
68
69
  ffi (>= 1.3.0)
69
- faraday (0.11.0)
70
+ faraday (0.12.0.1)
70
71
  multipart-post (>= 1.2, < 3)
71
72
  faraday_middleware (0.11.0.1)
72
73
  faraday (>= 0.7.4, < 1.0)
73
- ffi (1.9.17)
74
+ ffi (1.9.18)
74
75
  fork (1.0.1)
75
76
  fork_break (0.1.4)
76
77
  fork (= 1.0.1)
@@ -174,12 +175,12 @@ GEM
174
175
  ruby_dep (1.5.0)
175
176
  safe_yaml (1.0.4)
176
177
  shellany (0.0.1)
177
- sidekiq (4.2.9)
178
+ sidekiq (4.2.10)
178
179
  concurrent-ruby (~> 1.0)
179
180
  connection_pool (~> 2.2, >= 2.2.0)
180
181
  rack-protection (>= 1.5.0)
181
182
  redis (~> 3.2, >= 3.2.1)
182
- simplecov (0.13.0)
183
+ simplecov (0.14.1)
183
184
  docile (~> 1.1.0)
184
185
  json (>= 1.8, < 3)
185
186
  simplecov-html (~> 0.10.0)
@@ -201,7 +202,7 @@ GEM
201
202
  polyglot (>= 0.3.1)
202
203
  typhoeus (1.1.2)
203
204
  ethon (>= 0.9.0)
204
- tzinfo (0.3.52)
205
+ tzinfo (0.3.53)
205
206
  url (0.3.2)
206
207
  vegas (0.1.11)
207
208
  rack (>= 1.0.0)
@@ -1,7 +1,8 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- routemaster-drain (2.4.4)
4
+ routemaster-drain (2.5.0)
5
+ addressable
5
6
  concurrent-ruby
6
7
  faraday (>= 0.9.0)
7
8
  faraday_middleware
@@ -48,7 +49,7 @@ GEM
48
49
  minitest (~> 5.1)
49
50
  thread_safe (~> 0.3, >= 0.3.4)
50
51
  tzinfo (~> 1.1)
51
- addressable (2.5.0)
52
+ addressable (2.5.1)
52
53
  public_suffix (~> 2.0, >= 2.0.2)
53
54
  appraisal (2.1.0)
54
55
  bundler
@@ -57,7 +58,7 @@ GEM
57
58
  arel (6.0.4)
58
59
  builder (3.2.3)
59
60
  byebug (9.0.6)
60
- codecov (0.1.9)
61
+ codecov (0.1.10)
61
62
  json
62
63
  simplecov
63
64
  url
@@ -73,11 +74,11 @@ GEM
73
74
  erubis (2.7.0)
74
75
  ethon (0.10.1)
75
76
  ffi (>= 1.3.0)
76
- faraday (0.11.0)
77
+ faraday (0.12.0.1)
77
78
  multipart-post (>= 1.2, < 3)
78
79
  faraday_middleware (0.11.0.1)
79
80
  faraday (>= 0.7.4, < 1.0)
80
- ffi (1.9.17)
81
+ ffi (1.9.18)
81
82
  fork (1.0.1)
82
83
  fork_break (0.1.4)
83
84
  fork (= 1.0.1)
@@ -121,7 +122,7 @@ GEM
121
122
  multi_json (1.12.1)
122
123
  multipart-post (2.0.0)
123
124
  nenv (0.3.0)
124
- nokogiri (1.7.0.1)
125
+ nokogiri (1.7.1)
125
126
  mini_portile2 (~> 2.1.0)
126
127
  notiffany (0.1.1)
127
128
  nenv (~> 0.1)
@@ -190,12 +191,12 @@ GEM
190
191
  ruby_dep (1.5.0)
191
192
  safe_yaml (1.0.4)
192
193
  shellany (0.0.1)
193
- sidekiq (4.2.9)
194
+ sidekiq (4.2.10)
194
195
  concurrent-ruby (~> 1.0)
195
196
  connection_pool (~> 2.2, >= 2.2.0)
196
197
  rack-protection (>= 1.5.0)
197
198
  redis (~> 3.2, >= 3.2.1)
198
- simplecov (0.13.0)
199
+ simplecov (0.14.1)
199
200
  docile (~> 1.1.0)
200
201
  json (>= 1.8, < 3)
201
202
  simplecov-html (~> 0.10.0)
@@ -214,10 +215,10 @@ GEM
214
215
  sprockets (>= 3.0.0)
215
216
  thor (0.19.4)
216
217
  thread_safe (0.3.6)
217
- tilt (2.0.6)
218
+ tilt (2.0.7)
218
219
  typhoeus (1.1.2)
219
220
  ethon (>= 0.9.0)
220
- tzinfo (1.2.2)
221
+ tzinfo (1.2.3)
221
222
  thread_safe (~> 0.1)
222
223
  url (0.3.2)
223
224
  vegas (0.1.11)
@@ -1,7 +1,8 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- routemaster-drain (2.4.4)
4
+ routemaster-drain (2.5.0)
5
+ addressable
5
6
  concurrent-ruby
6
7
  faraday (>= 0.9.0)
7
8
  faraday_middleware
@@ -14,44 +15,44 @@ PATH
14
15
  GEM
15
16
  remote: https://rubygems.org/
16
17
  specs:
17
- actioncable (5.0.1)
18
- actionpack (= 5.0.1)
19
- nio4r (~> 1.2)
18
+ actioncable (5.0.2)
19
+ actionpack (= 5.0.2)
20
+ nio4r (>= 1.2, < 3.0)
20
21
  websocket-driver (~> 0.6.1)
21
- actionmailer (5.0.1)
22
- actionpack (= 5.0.1)
23
- actionview (= 5.0.1)
24
- activejob (= 5.0.1)
22
+ actionmailer (5.0.2)
23
+ actionpack (= 5.0.2)
24
+ actionview (= 5.0.2)
25
+ activejob (= 5.0.2)
25
26
  mail (~> 2.5, >= 2.5.4)
26
27
  rails-dom-testing (~> 2.0)
27
- actionpack (5.0.1)
28
- actionview (= 5.0.1)
29
- activesupport (= 5.0.1)
28
+ actionpack (5.0.2)
29
+ actionview (= 5.0.2)
30
+ activesupport (= 5.0.2)
30
31
  rack (~> 2.0)
31
32
  rack-test (~> 0.6.3)
32
33
  rails-dom-testing (~> 2.0)
33
34
  rails-html-sanitizer (~> 1.0, >= 1.0.2)
34
- actionview (5.0.1)
35
- activesupport (= 5.0.1)
35
+ actionview (5.0.2)
36
+ activesupport (= 5.0.2)
36
37
  builder (~> 3.1)
37
38
  erubis (~> 2.7.0)
38
39
  rails-dom-testing (~> 2.0)
39
- rails-html-sanitizer (~> 1.0, >= 1.0.2)
40
- activejob (5.0.1)
41
- activesupport (= 5.0.1)
40
+ rails-html-sanitizer (~> 1.0, >= 1.0.3)
41
+ activejob (5.0.2)
42
+ activesupport (= 5.0.2)
42
43
  globalid (>= 0.3.6)
43
- activemodel (5.0.1)
44
- activesupport (= 5.0.1)
45
- activerecord (5.0.1)
46
- activemodel (= 5.0.1)
47
- activesupport (= 5.0.1)
44
+ activemodel (5.0.2)
45
+ activesupport (= 5.0.2)
46
+ activerecord (5.0.2)
47
+ activemodel (= 5.0.2)
48
+ activesupport (= 5.0.2)
48
49
  arel (~> 7.0)
49
- activesupport (5.0.1)
50
+ activesupport (5.0.2)
50
51
  concurrent-ruby (~> 1.0, >= 1.0.2)
51
52
  i18n (~> 0.7)
52
53
  minitest (~> 5.1)
53
54
  tzinfo (~> 1.1)
54
- addressable (2.5.0)
55
+ addressable (2.5.1)
55
56
  public_suffix (~> 2.0, >= 2.0.2)
56
57
  appraisal (2.1.0)
57
58
  bundler
@@ -60,7 +61,7 @@ GEM
60
61
  arel (7.1.4)
61
62
  builder (3.2.3)
62
63
  byebug (9.0.6)
63
- codecov (0.1.9)
64
+ codecov (0.1.10)
64
65
  json
65
66
  simplecov
66
67
  url
@@ -76,11 +77,11 @@ GEM
76
77
  erubis (2.7.0)
77
78
  ethon (0.10.1)
78
79
  ffi (>= 1.3.0)
79
- faraday (0.11.0)
80
+ faraday (0.12.0.1)
80
81
  multipart-post (>= 1.2, < 3)
81
82
  faraday_middleware (0.11.0.1)
82
83
  faraday (>= 0.7.4, < 1.0)
83
- ffi (1.9.17)
84
+ ffi (1.9.18)
84
85
  fork (1.0.1)
85
86
  fork_break (0.1.4)
86
87
  fork (= 1.0.1)
@@ -124,8 +125,8 @@ GEM
124
125
  multi_json (1.12.1)
125
126
  multipart-post (2.0.0)
126
127
  nenv (0.3.0)
127
- nio4r (1.2.1)
128
- nokogiri (1.7.0.1)
128
+ nio4r (2.0.0)
129
+ nokogiri (1.7.1)
129
130
  mini_portile2 (~> 2.1.0)
130
131
  notiffany (0.1.1)
131
132
  nenv (~> 0.1)
@@ -141,26 +142,26 @@ GEM
141
142
  rack
142
143
  rack-test (0.6.3)
143
144
  rack (>= 1.0)
144
- rails (5.0.1)
145
- actioncable (= 5.0.1)
146
- actionmailer (= 5.0.1)
147
- actionpack (= 5.0.1)
148
- actionview (= 5.0.1)
149
- activejob (= 5.0.1)
150
- activemodel (= 5.0.1)
151
- activerecord (= 5.0.1)
152
- activesupport (= 5.0.1)
145
+ rails (5.0.2)
146
+ actioncable (= 5.0.2)
147
+ actionmailer (= 5.0.2)
148
+ actionpack (= 5.0.2)
149
+ actionview (= 5.0.2)
150
+ activejob (= 5.0.2)
151
+ activemodel (= 5.0.2)
152
+ activerecord (= 5.0.2)
153
+ activesupport (= 5.0.2)
153
154
  bundler (>= 1.3.0, < 2.0)
154
- railties (= 5.0.1)
155
+ railties (= 5.0.2)
155
156
  sprockets-rails (>= 2.0.0)
156
157
  rails-dom-testing (2.0.2)
157
158
  activesupport (>= 4.2.0, < 6.0)
158
159
  nokogiri (~> 1.6)
159
160
  rails-html-sanitizer (1.0.3)
160
161
  loofah (~> 2.0)
161
- railties (5.0.1)
162
- actionpack (= 5.0.1)
163
- activesupport (= 5.0.1)
162
+ railties (5.0.2)
163
+ actionpack (= 5.0.2)
164
+ activesupport (= 5.0.2)
164
165
  method_source
165
166
  rake (>= 0.8.7)
166
167
  thor (>= 0.18.1, < 2.0)
@@ -193,12 +194,12 @@ GEM
193
194
  ruby_dep (1.5.0)
194
195
  safe_yaml (1.0.4)
195
196
  shellany (0.0.1)
196
- sidekiq (4.2.9)
197
+ sidekiq (4.2.10)
197
198
  concurrent-ruby (~> 1.0)
198
199
  connection_pool (~> 2.2, >= 2.2.0)
199
200
  rack-protection (>= 1.5.0)
200
201
  redis (~> 3.2, >= 3.2.1)
201
- simplecov (0.13.0)
202
+ simplecov (0.14.1)
202
203
  docile (~> 1.1.0)
203
204
  json (>= 1.8, < 3)
204
205
  simplecov-html (~> 0.10.0)
@@ -217,7 +218,7 @@ GEM
217
218
  thread_safe (0.3.6)
218
219
  typhoeus (1.1.2)
219
220
  ethon (>= 0.9.0)
220
- tzinfo (1.2.2)
221
+ tzinfo (1.2.3)
221
222
  thread_safe (~> 0.1)
222
223
  url (0.3.2)
223
224
  vegas (0.1.11)
@@ -37,4 +37,3 @@ module Routemaster
37
37
  end
38
38
  end
39
39
  end
40
-
@@ -0,0 +1,38 @@
1
+ require 'routemaster/middleware/root_post_only'
2
+ require 'routemaster/middleware/authenticate'
3
+ require 'routemaster/middleware/parse'
4
+ require 'routemaster/middleware/expire_cache'
5
+ require 'routemaster/middleware/payload_filter'
6
+ require 'routemaster/middleware/filter'
7
+ require 'routemaster/drain/terminator'
8
+ require 'rack/builder'
9
+ require 'delegate'
10
+
11
+ module Routemaster
12
+ module Drain
13
+ # Rack application which authenticates, parses, filters duplicates in this request
14
+ # and invalidates the cache for all updated or new items.
15
+ #
16
+ # See the various corresponding middleware for details on operation:
17
+ # {Middleware::RootPostOnly}, {Middleware::Authenticate},
18
+ # {Middleware::Parse}, {Middleware::ExpireCache} and {Terminator}.
19
+ class CacheBusting
20
+ extend Forwardable
21
+
22
+ def initialize(options = {})
23
+ @terminator = terminator = Terminator.new
24
+ @app = ::Rack::Builder.new do
25
+ use Middleware::RootPostOnly
26
+ use Middleware::Authenticate, options
27
+ use Middleware::Parse
28
+ use Middleware::Filter, { filter: Routemaster::Middleware::PayloadFilter.new }.merge(options)
29
+ use Middleware::ExpireCache
30
+ run terminator
31
+ end
32
+ end
33
+
34
+ delegate :call => :@app
35
+ delegate [:on, :subscribe] => :@terminator
36
+ end
37
+ end
38
+ end
@@ -1,9 +1,11 @@
1
1
  require 'routemaster/middleware/root_post_only'
2
2
  require 'routemaster/middleware/authenticate'
3
3
  require 'routemaster/middleware/parse'
4
+ require 'routemaster/middleware/siphon'
4
5
  require 'routemaster/middleware/filter'
5
6
  require 'routemaster/middleware/dirty'
6
7
  require 'routemaster/middleware/cache'
8
+ require 'routemaster/middleware/expire_cache'
7
9
  require 'routemaster/drain/terminator'
8
10
  require 'rack/builder'
9
11
  require 'delegate'
@@ -28,6 +30,8 @@ module Routemaster
28
30
  use Middleware::RootPostOnly
29
31
  use Middleware::Authenticate, options
30
32
  use Middleware::Parse
33
+ use Middleware::ExpireCache, options
34
+ use Middleware::Siphon, options
31
35
  use Middleware::Filter, options
32
36
  use Middleware::Dirty, options
33
37
  use Middleware::Cache, options
@@ -40,4 +44,3 @@ module Routemaster
40
44
  end
41
45
  end
42
46
  end
43
-
@@ -1,6 +1,7 @@
1
1
  require 'routemaster/middleware/root_post_only'
2
2
  require 'routemaster/middleware/authenticate'
3
3
  require 'routemaster/middleware/parse'
4
+ require 'routemaster/middleware/siphon'
4
5
  require 'routemaster/middleware/filter'
5
6
  require 'routemaster/middleware/dirty'
6
7
  require 'routemaster/drain/terminator'
@@ -29,6 +30,7 @@ module Routemaster
29
30
  use Middleware::RootPostOnly
30
31
  use Middleware::Authenticate, options
31
32
  use Middleware::Parse
33
+ use Middleware::Siphon, options
32
34
  use Middleware::Filter, options
33
35
  use Middleware::Dirty, options
34
36
  run terminator
@@ -40,4 +42,3 @@ module Routemaster
40
42
  end
41
43
  end
42
44
  end
43
-
@@ -1,5 +1,5 @@
1
1
  module Routemaster
2
2
  module Drain
3
- VERSION = '2.4.4'
3
+ VERSION = '2.5.0'
4
4
  end
5
5
  end
@@ -17,7 +17,7 @@ module Routemaster
17
17
  include Wisper::Publisher
18
18
 
19
19
  # @param uuid [Enumerable] a set of accepted authentication tokens
20
- def initialize(app, uuid: nil)
20
+ def initialize(app, uuid: nil, **_)
21
21
  @app = app
22
22
  @uuid = uuid || Config.drain_tokens
23
23
 
@@ -56,4 +56,3 @@ module Routemaster
56
56
  end
57
57
  end
58
58
  end
59
-
@@ -7,7 +7,7 @@ require 'routemaster/event_index'
7
7
  module Routemaster
8
8
  module Middleware
9
9
  class Cache
10
- def initialize(app, cache:nil, client:nil, queue:nil)
10
+ def initialize(app, cache:nil, client:nil, queue:nil, **_)
11
11
  @app = app
12
12
  @cache = cache || Routemaster::Cache.new
13
13
  @client = client || Routemaster::Jobs::Client.new
@@ -16,7 +16,6 @@ module Routemaster
16
16
 
17
17
  def call(env)
18
18
  env.fetch('routemaster.dirty', []).each do |url|
19
- @cache.invalidate(url)
20
19
  @client.enqueue(@queue, Routemaster::Jobs::CacheAndSweep, url)
21
20
  end
22
21
  @app.call(env)
@@ -11,7 +11,7 @@ module Routemaster
11
11
  # The dirty map is passed as `:map` to the constructor and must respond to
12
12
  # `#mark` (like `Routemaster::Dirty::Map`).
13
13
  class Dirty
14
- def initialize(app, dirty_map:nil)
14
+ def initialize(app, dirty_map: nil, **_)
15
15
  @app = app
16
16
  @map = dirty_map || Routemaster::Dirty::Map.new
17
17
  end
@@ -28,6 +28,3 @@ module Routemaster
28
28
  end
29
29
  end
30
30
  end
31
-
32
-
33
-
@@ -0,0 +1,20 @@
1
+ require 'routemaster/cache'
2
+
3
+ module Routemaster
4
+ module Middleware
5
+ class ExpireCache
6
+ def initialize(app, cache:nil, **_)
7
+ @app = app
8
+ @cache = cache || Routemaster::Cache.new
9
+ end
10
+
11
+ def call(env)
12
+ env.fetch('routemaster.payload', []).each do |event|
13
+ next if event['type'] == 'noop'
14
+ @cache.invalidate(event['url'])
15
+ end
16
+ @app.call(env)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -9,7 +9,7 @@ module Routemaster
9
9
  class Filter
10
10
  # @param filter [Routemaster::Dirty::Filter] an event filter (optional;
11
11
  # will be created using the `redis` and `expiry` options if not provided)
12
- def initialize(app, filter:nil)
12
+ def initialize(app, filter:nil, **_)
13
13
  @app = app
14
14
  @filter = filter || Routemaster::Dirty::Filter.new
15
15
  end
@@ -19,7 +19,7 @@ module Routemaster
19
19
  if payload && payload.any?
20
20
  env['routemaster.payload'] = @filter.run(payload)
21
21
  end
22
- @app.call(env)
22
+ @app.call(env)
23
23
  end
24
24
  end
25
25
  end
@@ -0,0 +1,10 @@
1
+ module Routemaster
2
+ module Middleware
3
+ class PayloadFilter
4
+ # Filters duplicate events by url and type in a single payload.
5
+ def run(payload)
6
+ payload.group_by { |event| [event['url'], event['type']] }.map { |_, events| events.last }
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,32 @@
1
+ module Routemaster
2
+ module Middleware
3
+ # Filters out events based on their topic and passes them to a handling class
4
+ #
5
+ # `use Middleware::Siphon, 'some_topic' => SomeTopicHandler`
6
+ #
7
+ # Topic handlers are initialized with the full event payload and must respond to `#call`
8
+ class Siphon
9
+ def initialize(app, siphon_events: nil)
10
+ @app = app
11
+ @processors = siphon_events || {}
12
+ end
13
+
14
+ def call(env)
15
+ siphoned, non_siphoned = env.fetch('routemaster.payload', []).partition do |event|
16
+ topics_to_siphon.include? event['topic']
17
+ end
18
+ siphoned.each do |event|
19
+ @processors[event['topic']].new(event).call
20
+ end
21
+ env['routemaster.payload'] = non_siphoned
22
+ @app.call(env)
23
+ end
24
+
25
+ private
26
+
27
+ def topics_to_siphon
28
+ @topics_to_siphon ||= @processors.keys.map(&:to_s)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -1,3 +1,5 @@
1
+ require 'addressable/template'
2
+
1
3
  require 'routemaster/api_client'
2
4
  require 'routemaster/responses/hateoas_enumerable_response'
3
5
  require 'routemaster/responses/hateoas_response'
@@ -5,40 +7,48 @@ require 'routemaster/responses/hateoas_response'
5
7
  module Routemaster
6
8
  module Resources
7
9
  class RestResource
8
- attr_reader :url
9
-
10
10
  def initialize(url, client: nil)
11
- @url = url
11
+ @url_template = Addressable::Template.new(url)
12
12
  @client = client || Routemaster::APIClient.new
13
13
  end
14
14
 
15
15
  def create(params)
16
16
  @client.with_response(Responses::HateoasResponse) do
17
- @client.post(@url, body: params)
17
+ @client.post(expanded_url, body: params)
18
18
  end
19
19
  end
20
20
 
21
21
  def show(id=nil, enable_caching: true)
22
22
  @client.with_response(Responses::HateoasResponse) do
23
- @client.get(@url.gsub('{id}', id.to_s), options: { enable_caching: enable_caching })
23
+ @client.get(expanded_url(id: id), options: { enable_caching: enable_caching })
24
24
  end
25
25
  end
26
26
 
27
27
  def index(params: {}, filters: {}, enable_caching: false)
28
28
  @client.with_response(Responses::HateoasEnumerableResponse) do
29
- @client.get(@url, params: params.merge(filters), options: { enable_caching: enable_caching })
29
+ @client.get(expanded_url, params: params.merge(filters), options: { enable_caching: enable_caching })
30
30
  end
31
31
  end
32
32
 
33
33
  def update(id=nil, params)
34
34
  @client.with_response(Responses::HateoasResponse) do
35
- @client.patch(@url.gsub('{id}', id.to_s), body: params)
35
+ @client.patch(expanded_url(id: id), body: params)
36
36
  end
37
37
  end
38
38
 
39
39
  def destroy(id=nil)
40
40
  # no response wrapping as DELETE is supposed to 204.
41
- @client.delete(@url.gsub('{id}', id.to_s))
41
+ @client.delete(expanded_url(id: id))
42
+ end
43
+
44
+ def url
45
+ @url_template.pattern
46
+ end
47
+
48
+ private
49
+
50
+ def expanded_url(**params)
51
+ @url_template.expand(params).to_s
42
52
  end
43
53
  end
44
54
  end
@@ -16,6 +16,7 @@ Gem::Specification.new do |spec|
16
16
  spec.test_files = spec.files.grep(%r{^spec/})
17
17
  spec.require_paths = %w(lib)
18
18
 
19
+ spec.add_runtime_dependency 'addressable'
19
20
  spec.add_runtime_dependency 'faraday', '>= 0.9.0'
20
21
  spec.add_runtime_dependency 'faraday_middleware'
21
22
  spec.add_runtime_dependency 'typhoeus', '~> 1.1'
@@ -111,12 +111,7 @@ describe Routemaster::APIClient do
111
111
  end
112
112
 
113
113
  let(:callback){
114
- subject.on_success do
115
- callback_spy.success
116
- end
117
- subject.on_error do
118
- callback_spy.error
119
- end
114
+ subject.on_success { callback_spy.success }.zip(subject.on_error { callback_spy.error })
120
115
  }
121
116
 
122
117
  context "when successful" do
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+ require 'spec/support/rack_test'
3
+ require 'spec/support/uses_redis'
4
+ require 'spec/support/uses_dotenv'
5
+ require 'spec/support/events'
6
+ require 'routemaster/drain/cache_busting'
7
+ require 'json'
8
+
9
+ describe Routemaster::Drain::CacheBusting do
10
+ uses_dotenv
11
+ uses_redis
12
+
13
+ let(:app) { described_class.new }
14
+ let(:listener) { double 'listener' }
15
+
16
+ before { app.subscribe(listener, prefix: true) }
17
+
18
+ let(:path) { '/' }
19
+ let(:payload) { [1,2,3,1].map { |idx| make_event(idx) }.to_json }
20
+ let(:environment) {{ 'CONTENT_TYPE' => 'application/json' }}
21
+ let(:perform) { post path, payload, environment }
22
+
23
+ before { authorize 'd3m0', 'x' }
24
+
25
+ it 'succeeds' do
26
+ perform
27
+ expect(last_response.status).to eq(204)
28
+ end
29
+
30
+ it 'emits events' do
31
+ expect(listener).to receive(:on_events_received) do |payload|
32
+ expect(payload.size).to eq(3)
33
+ end
34
+ perform
35
+ end
36
+
37
+ it 'increments the event index' do
38
+ ei_double = double(increment: 1)
39
+ allow(Routemaster::EventIndex).to receive(:new).and_return(ei_double)
40
+ expect(ei_double).to receive(:increment).exactly(3).times
41
+ perform
42
+ end
43
+ end
@@ -3,25 +3,31 @@ require 'spec/support/rack_test'
3
3
  require 'spec/support/uses_redis'
4
4
  require 'spec/support/uses_dotenv'
5
5
  require 'spec/support/events'
6
+ require 'spec/support/siphon'
7
+
6
8
  require 'routemaster/drain/caching'
9
+
7
10
  require 'json'
8
11
 
9
12
  describe Routemaster::Drain::Caching do
10
13
  uses_dotenv
11
14
  uses_redis
12
15
 
13
- let(:app) { described_class.new }
16
+ let(:app) { described_class.new options}
14
17
  let(:listener) { double 'listener' }
18
+ let(:options) { {} }
15
19
 
16
20
  before { app.subscribe(listener, prefix: true) }
17
21
 
18
22
  let(:path) { '/' }
19
- let(:payload) { [1,2,3,1].map { |idx| make_event(idx) }.to_json }
23
+ let(:payload) { [1,2,3,1].map { |idx| make_event(idx) } }
20
24
  let(:environment) {{ 'CONTENT_TYPE' => 'application/json' }}
21
- let(:perform) { post path, payload, environment }
25
+ let(:perform) { post path, payload.to_json, environment }
22
26
 
23
27
  before { authorize 'd3m0', 'x' }
24
28
 
29
+ include_examples 'supports siphon'
30
+
25
31
  it 'succeeds' do
26
32
  perform
27
33
  expect(last_response.status).to eq(204)
@@ -37,7 +43,7 @@ describe Routemaster::Drain::Caching do
37
43
  it 'increments the event index' do
38
44
  ei_double = double(increment: 1)
39
45
  allow(Routemaster::EventIndex).to receive(:new).and_return(ei_double)
40
- expect(ei_double).to receive(:increment).exactly(3).times
46
+ expect(ei_double).to receive(:increment).exactly(4).times
41
47
  perform
42
48
  end
43
49
 
@@ -3,6 +3,7 @@ require 'spec/support/rack_test'
3
3
  require 'spec/support/uses_redis'
4
4
  require 'spec/support/uses_dotenv'
5
5
  require 'spec/support/events'
6
+ require 'spec/support/siphon'
6
7
  require 'routemaster/drain/mapping'
7
8
  require 'json'
8
9
 
@@ -10,18 +11,21 @@ describe Routemaster::Drain::Mapping do
10
11
  uses_dotenv
11
12
  uses_redis
12
13
 
13
- let(:app) { described_class.new }
14
+ let(:app) { described_class.new(options) }
15
+ let(:options) { {} }
14
16
  let(:listener) { double 'listener' }
15
-
17
+
16
18
  before { app.subscribe(listener, prefix: true) }
17
19
 
18
20
  let(:path) { '/' }
19
- let(:payload) { [1,2,3,1].map { |idx| make_event(idx) }.to_json }
21
+ let(:payload) { [1,2,3,1].map { |idx| make_event(idx) } }
20
22
  let(:environment) {{ 'CONTENT_TYPE' => 'application/json' }}
21
- let(:perform) { post path, payload, environment }
23
+ let(:perform) { post path, payload.to_json, environment }
22
24
 
23
25
  before { authorize 'd3m0', 'x' }
24
26
 
27
+ include_examples 'supports siphon'
28
+
25
29
  it 'succeeds' do
26
30
  perform
27
31
  expect(last_response.status).to eq(204)
@@ -35,11 +39,11 @@ describe Routemaster::Drain::Mapping do
35
39
  end
36
40
 
37
41
  let(:map) { Routemaster::Dirty::Map.new }
38
-
42
+
39
43
  it 'uses dirty map' do
40
44
  payload1 = [1,2,3,1].map { |idx| make_event(idx) }.to_json
41
45
  payload2 = [1,4,5,2].map { |idx| make_event(idx) }.to_json
42
-
46
+
43
47
  post path, payload1, environment
44
48
  expect(map.count).to eq(3)
45
49
  map.sweep_one("https://example.com/stuff/1") { true }
@@ -48,4 +52,3 @@ describe Routemaster::Drain::Mapping do
48
52
  expect(map.count).to eq(4) # 2, 3, 4, 5 (1 is a repeat event, filtered out)
49
53
  end
50
54
  end
51
-
@@ -19,11 +19,6 @@ RSpec.describe Routemaster::Middleware::Cache do
19
19
  describe '#call' do
20
20
  let(:payload) { ['https://example.com/1'] }
21
21
 
22
- it 'increments the event_index' do
23
- expect(cache).to receive(:invalidate)
24
- perform
25
- end
26
-
27
22
  it 'queues a fetch job' do
28
23
  expect(client).to receive(:enqueue).with('routemaster', Routemaster::Jobs::CacheAndSweep, 'https://example.com/1')
29
24
  perform
@@ -40,7 +40,3 @@ describe Routemaster::Middleware::RootPostOnly do
40
40
  end
41
41
  end
42
42
  end
43
-
44
-
45
-
46
-
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+ require 'spec/support/rack_test'
3
+ require 'spec/support/events'
4
+ require 'routemaster/middleware/siphon'
5
+ require 'json'
6
+
7
+ describe Routemaster::Middleware::Siphon do
8
+ let(:terminator) { ErrorRackApp.new }
9
+ let(:app) { described_class.new(terminator, options) }
10
+
11
+ let(:perform) do
12
+ post '/whatever', '', 'routemaster.payload' => payload
13
+ end
14
+
15
+ describe '#call' do
16
+
17
+ let(:options) { { filter:filter } }
18
+ let(:payload) { [ make_event(1), make_event(1).merge({'topic' => 'notstuff'})] }
19
+
20
+ context "if no siphon is defined" do
21
+ let(:options) { {} }
22
+
23
+ it "passes all to the terminator" do
24
+ perform
25
+ expect(terminator.last_env['routemaster.payload']).to eq payload
26
+ end
27
+ end
28
+
29
+ context "if a 'stuff' siphon is defined" do
30
+ let(:siphon_double) { double(new: siphon_instance) }
31
+ let(:siphon_instance) { double(call: nil) }
32
+ let(:options) { { siphon_events: { 'stuff' => siphon_double } } }
33
+
34
+ it "calls the siphon with the event" do
35
+ perform
36
+ expect(siphon_double).to have_received(:new).with(payload[0])
37
+ expect(siphon_instance).to have_received(:call)
38
+ end
39
+
40
+ it "passes 'notstuff' to the terminator" do
41
+ perform
42
+ expect(terminator.last_env['routemaster.payload']).to eq [payload[1]]
43
+ end
44
+ end
45
+ end
46
+ end
@@ -4,46 +4,55 @@ require 'routemaster/resources/rest_resource'
4
4
  module Routemaster
5
5
  module Resources
6
6
  RSpec.describe RestResource do
7
- let(:url) { 'test_url' }
8
7
  let(:client) { double('Client') }
9
8
  let(:params) { {} }
10
9
 
11
- subject { described_class.new(url, client: client) }
12
-
13
10
  before { allow(client).to receive(:with_response).and_yield }
14
11
 
15
- describe '#create' do
16
- it 'posts to the given url' do
17
- expect(client).to receive(:post).with(url, body: params)
18
- subject.create(params)
12
+ describe "singular resource" do
13
+ let(:url) { '/resources/1' }
14
+
15
+ subject { described_class.new('/resources/{id}', client: client) }
16
+
17
+ describe '#show' do
18
+ it 'gets to the given url' do
19
+ expect(client).to receive(:get).with(url, options: { enable_caching: true })
20
+ subject.show(1)
21
+ end
19
22
  end
20
- end
21
23
 
22
- describe '#show' do
23
- it 'gets to the given url' do
24
- expect(client).to receive(:get).with(url, options: { enable_caching: true })
25
- subject.show(1)
24
+ describe '#update' do
25
+ it 'updates the given resource' do
26
+ expect(client).to receive(:patch).with(url, body: params)
27
+ subject.update(1, params)
28
+ end
26
29
  end
27
- end
28
30
 
29
- describe '#index' do
30
- it 'gets to the given url' do
31
- expect(client).to receive(:get).with(url, params: {}, options: { enable_caching: false })
32
- subject.index
31
+ describe '#destroy' do
32
+ it 'destroys the given resource' do
33
+ expect(client).to receive(:delete).with(url)
34
+ subject.destroy(1)
35
+ end
33
36
  end
34
37
  end
35
38
 
36
- describe '#update' do
37
- it 'updates the given resource' do
38
- expect(client).to receive(:patch).with(url, body: params)
39
- subject.update(1, params)
39
+ describe "collection resource" do
40
+ let(:url) { '/resources' }
41
+
42
+ subject { described_class.new('/resources{?page,per_page}', client: client) }
43
+
44
+ describe '#create' do
45
+ it 'posts to the given url' do
46
+ expect(client).to receive(:post).with(url, body: params)
47
+ subject.create(params)
48
+ end
40
49
  end
41
- end
42
50
 
43
- describe '#destroy' do
44
- it 'destroys the given resource' do
45
- expect(client).to receive(:delete).with(url)
46
- subject.destroy(1)
51
+ describe '#index' do
52
+ it 'gets to the given url' do
53
+ expect(client).to receive(:get).with(url, params: {}, options: { enable_caching: false })
54
+ subject.index
55
+ end
47
56
  end
48
57
  end
49
58
  end
@@ -0,0 +1,13 @@
1
+ RSpec.shared_examples 'supports siphon' do
2
+ context "if a siphon is defined" do
3
+ let(:siphon_double) { double(new: siphon_instance) }
4
+ let(:siphon_instance) { double(call: nil) }
5
+ let(:options){ { siphon_events: { payload[0]['topic'] => siphon_double } } }
6
+
7
+ it "calls the siphon with the event" do
8
+ perform
9
+ expect(siphon_double).to have_received(:new).with(payload[0]).at_least(:once)
10
+ expect(siphon_instance).to have_received(:call).at_least(:once)
11
+ end
12
+ end
13
+ end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: routemaster-drain
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.4
4
+ version: 2.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julien Letessier
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-03-27 00:00:00.000000000 Z
11
+ date: 2017-04-11 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: addressable
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: faraday
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -164,6 +178,7 @@ files:
164
178
  - lib/routemaster/dirty/state.rb
165
179
  - lib/routemaster/drain.rb
166
180
  - lib/routemaster/drain/basic.rb
181
+ - lib/routemaster/drain/cache_busting.rb
167
182
  - lib/routemaster/drain/caching.rb
168
183
  - lib/routemaster/drain/mapping.rb
169
184
  - lib/routemaster/drain/terminator.rb
@@ -180,11 +195,14 @@ files:
180
195
  - lib/routemaster/middleware/cache.rb
181
196
  - lib/routemaster/middleware/dirty.rb
182
197
  - lib/routemaster/middleware/error_handling.rb
198
+ - lib/routemaster/middleware/expire_cache.rb
183
199
  - lib/routemaster/middleware/filter.rb
184
200
  - lib/routemaster/middleware/metrics.rb
185
201
  - lib/routemaster/middleware/parse.rb
202
+ - lib/routemaster/middleware/payload_filter.rb
186
203
  - lib/routemaster/middleware/response_caching.rb
187
204
  - lib/routemaster/middleware/root_post_only.rb
205
+ - lib/routemaster/middleware/siphon.rb
188
206
  - lib/routemaster/null_logger.rb
189
207
  - lib/routemaster/redis_broker.rb
190
208
  - lib/routemaster/resources/rest_resource.rb
@@ -198,6 +216,7 @@ files:
198
216
  - spec/routemaster/dirty/map_spec.rb
199
217
  - spec/routemaster/dirty/state_spec.rb
200
218
  - spec/routemaster/drain/basic_spec.rb
219
+ - spec/routemaster/drain/cache_busting_spec.rb
201
220
  - spec/routemaster/drain/caching_spec.rb
202
221
  - spec/routemaster/drain/mapping_spec.rb
203
222
  - spec/routemaster/drain/terminator_spec.rb
@@ -214,6 +233,7 @@ files:
214
233
  - spec/routemaster/middleware/filter_spec.rb
215
234
  - spec/routemaster/middleware/parse_spec.rb
216
235
  - spec/routemaster/middleware/root_post_only_spec.rb
236
+ - spec/routemaster/middleware/siphon_spec.rb
217
237
  - spec/routemaster/redis_broker_spec.rb
218
238
  - spec/routemaster/resources/rest_resource_spec.rb
219
239
  - spec/routemaster/responses/hateoas_enumerable_response_spec.rb
@@ -224,6 +244,7 @@ files:
224
244
  - spec/support/events.rb
225
245
  - spec/support/rack_test.rb
226
246
  - spec/support/server.rb
247
+ - spec/support/siphon.rb
227
248
  - spec/support/uses_dotenv.rb
228
249
  - spec/support/uses_redis.rb
229
250
  - spec/support/uses_webmock.rb
@@ -258,6 +279,7 @@ test_files:
258
279
  - spec/routemaster/dirty/map_spec.rb
259
280
  - spec/routemaster/dirty/state_spec.rb
260
281
  - spec/routemaster/drain/basic_spec.rb
282
+ - spec/routemaster/drain/cache_busting_spec.rb
261
283
  - spec/routemaster/drain/caching_spec.rb
262
284
  - spec/routemaster/drain/mapping_spec.rb
263
285
  - spec/routemaster/drain/terminator_spec.rb
@@ -274,6 +296,7 @@ test_files:
274
296
  - spec/routemaster/middleware/filter_spec.rb
275
297
  - spec/routemaster/middleware/parse_spec.rb
276
298
  - spec/routemaster/middleware/root_post_only_spec.rb
299
+ - spec/routemaster/middleware/siphon_spec.rb
277
300
  - spec/routemaster/redis_broker_spec.rb
278
301
  - spec/routemaster/resources/rest_resource_spec.rb
279
302
  - spec/routemaster/responses/hateoas_enumerable_response_spec.rb
@@ -284,6 +307,7 @@ test_files:
284
307
  - spec/support/events.rb
285
308
  - spec/support/rack_test.rb
286
309
  - spec/support/server.rb
310
+ - spec/support/siphon.rb
287
311
  - spec/support/uses_dotenv.rb
288
312
  - spec/support/uses_redis.rb
289
313
  - spec/support/uses_webmock.rb