routemaster-drain 2.4.4 → 2.5.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 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