async_cache 1.1.0 → 1.2.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: f27471956b05c3bb40ca9741a98a95134308fc84
4
- data.tar.gz: 0cc68507dc421ff154b487d1db2fa2db56f4dc07
3
+ metadata.gz: f5e390b11f0a6f259d8b5d8fed3ba2ce042045c4
4
+ data.tar.gz: bce164634a83146a3e385d33cb79b74e69c4222d
5
5
  SHA512:
6
- metadata.gz: edacd1a9fff44a91e2791d8bcf27b9511bd2d9806d475637d93a874d51c715a11d48959138c8509d91f6a9b4f4de8b35cab6827821a61bdcc96478fd8c244df4
7
- data.tar.gz: 88a1777e3e4e4bca3fcc51695c670e5160f1380da356ae9417e49c36aa02877b0663f6679243cf8c26623b3f4e3e5f0b92ec2c8566533c0664825b635fea35c6
6
+ metadata.gz: bd4e66433f1c2e5a2c36a33573fcee5e2f228743056d12a885d65efe0d31bbf811471fed0c593ee06866a2014f0000ad4c7c8584e7ca2bbf330a8ff51d6df494
7
+ data.tar.gz: 63bc29c179cca10113d65bef8649cf3e1137af95b0448d767200edd2c37cdd4cd9932bde75f95938d29ab5e2eb8abdc9265ce421cdd6b7e2518547810520e1a9
data/.gitignore CHANGED
@@ -1,3 +1,5 @@
1
+ *.gemfile.lock
2
+ .bundle
1
3
  .DS_Store
2
4
  .yardoc
3
5
  coverage
@@ -1,14 +1,18 @@
1
1
  sudo: false
2
2
  language: ruby
3
3
  cache: bundler
4
- rvm:
5
- - '2.2.1'
6
- - '2.2.3'
7
- - '2.3.0'
8
4
 
9
5
  services:
10
6
  - redis-server
11
7
 
8
+ gemfile:
9
+ - gemfiles/rails_4.gemfile
10
+ - gemfiles/rails_5.gemfile
11
+
12
+ rvm:
13
+ - '2.5.1'
14
+ - '2.4.4'
15
+
12
16
  before_script:
13
17
  - bundle exec ruby spec/integration/support/run_all.rb &
14
18
  - sleep 2
@@ -0,0 +1,10 @@
1
+ appraise 'rails-4' do
2
+ gem 'rails', '~> 4.2.10'
3
+ gem 'sidekiq', '~> 3.5.4'
4
+ gem 'sinatra', '~> 1.4.8'
5
+ gem 'redis-activesupport', '~> 4.1.5'
6
+ end
7
+
8
+ appraise 'rails-5' do
9
+ # Default configuration from Gemfile's development dependencies.
10
+ end
@@ -1,136 +1,129 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- async_cache (1.1.0)
4
+ async_cache (1.2.0)
5
5
  sourcify (~> 0.5.0)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- actionmailer (4.2.4)
11
- actionpack (= 4.2.4)
12
- actionview (= 4.2.4)
13
- activejob (= 4.2.4)
10
+ actioncable (5.0.7)
11
+ actionpack (= 5.0.7)
12
+ nio4r (>= 1.2, < 3.0)
13
+ websocket-driver (~> 0.6.1)
14
+ actionmailer (5.0.7)
15
+ actionpack (= 5.0.7)
16
+ actionview (= 5.0.7)
17
+ activejob (= 5.0.7)
14
18
  mail (~> 2.5, >= 2.5.4)
15
- rails-dom-testing (~> 1.0, >= 1.0.5)
16
- actionpack (4.2.4)
17
- actionview (= 4.2.4)
18
- activesupport (= 4.2.4)
19
- rack (~> 1.6)
20
- rack-test (~> 0.6.2)
21
- rails-dom-testing (~> 1.0, >= 1.0.5)
19
+ rails-dom-testing (~> 2.0)
20
+ actionpack (5.0.7)
21
+ actionview (= 5.0.7)
22
+ activesupport (= 5.0.7)
23
+ rack (~> 2.0)
24
+ rack-test (~> 0.6.3)
25
+ rails-dom-testing (~> 2.0)
22
26
  rails-html-sanitizer (~> 1.0, >= 1.0.2)
23
- actionview (4.2.4)
24
- activesupport (= 4.2.4)
27
+ actionview (5.0.7)
28
+ activesupport (= 5.0.7)
25
29
  builder (~> 3.1)
26
30
  erubis (~> 2.7.0)
27
- rails-dom-testing (~> 1.0, >= 1.0.5)
28
- rails-html-sanitizer (~> 1.0, >= 1.0.2)
29
- activejob (4.2.4)
30
- activesupport (= 4.2.4)
31
- globalid (>= 0.3.0)
32
- activemodel (4.2.4)
33
- activesupport (= 4.2.4)
34
- builder (~> 3.1)
35
- activerecord (4.2.4)
36
- activemodel (= 4.2.4)
37
- activesupport (= 4.2.4)
38
- arel (~> 6.0)
39
- activesupport (4.2.4)
40
- i18n (~> 0.7)
41
- json (~> 1.7, >= 1.7.7)
31
+ rails-dom-testing (~> 2.0)
32
+ rails-html-sanitizer (~> 1.0, >= 1.0.3)
33
+ activejob (5.0.7)
34
+ activesupport (= 5.0.7)
35
+ globalid (>= 0.3.6)
36
+ activemodel (5.0.7)
37
+ activesupport (= 5.0.7)
38
+ activerecord (5.0.7)
39
+ activemodel (= 5.0.7)
40
+ activesupport (= 5.0.7)
41
+ arel (~> 7.0)
42
+ activesupport (5.0.7)
43
+ concurrent-ruby (~> 1.0, >= 1.0.2)
44
+ i18n (>= 0.7, < 2)
42
45
  minitest (~> 5.1)
43
- thread_safe (~> 0.3, >= 0.3.4)
44
46
  tzinfo (~> 1.1)
45
- arel (6.0.3)
46
- builder (3.2.2)
47
- celluloid (0.17.2)
48
- celluloid-essentials
49
- celluloid-extras
50
- celluloid-fsm
51
- celluloid-pool
52
- celluloid-supervision
53
- timers (>= 4.1.1)
54
- celluloid-essentials (0.20.5)
55
- timers (>= 4.1.1)
56
- celluloid-extras (0.20.5)
57
- timers (>= 4.1.1)
58
- celluloid-fsm (0.20.5)
59
- timers (>= 4.1.1)
60
- celluloid-pool (0.20.5)
61
- timers (>= 4.1.1)
62
- celluloid-supervision (0.20.5)
63
- timers (>= 4.1.1)
47
+ appraisal (2.2.0)
48
+ bundler
49
+ rake
50
+ thor (>= 0.14.0)
51
+ arel (7.1.4)
52
+ builder (3.2.3)
64
53
  coderay (1.1.0)
65
- connection_pool (2.2.0)
54
+ concurrent-ruby (1.0.5)
55
+ connection_pool (2.2.2)
56
+ crass (1.0.4)
66
57
  diff-lcs (1.2.5)
67
58
  docile (1.1.5)
68
59
  erubis (2.7.0)
69
60
  file-tail (1.1.0)
70
61
  tins (~> 1.0)
71
- globalid (0.3.6)
72
- activesupport (>= 4.1.0)
73
- hitimes (1.2.3)
62
+ globalid (0.4.1)
63
+ activesupport (>= 4.2.0)
74
64
  httparty (0.13.7)
75
65
  json (~> 1.8)
76
66
  multi_xml (>= 0.5.2)
77
- i18n (0.7.0)
78
- json (1.8.3)
79
- loofah (2.0.3)
67
+ i18n (1.1.0)
68
+ concurrent-ruby (~> 1.0)
69
+ json (1.8.6)
70
+ loofah (2.2.2)
71
+ crass (~> 1.0.2)
80
72
  nokogiri (>= 1.5.9)
81
- mail (2.6.3)
82
- mime-types (>= 1.16, < 3)
73
+ mail (2.7.0)
74
+ mini_mime (>= 0.1.1)
83
75
  method_source (0.8.2)
84
- mime-types (2.6.2)
85
- mini_portile (0.6.2)
86
- minitest (5.8.2)
76
+ mini_mime (1.0.1)
77
+ mini_portile2 (2.3.0)
78
+ minitest (5.11.3)
87
79
  multi_json (1.11.2)
88
80
  multi_xml (0.5.5)
89
- nokogiri (1.6.6.2)
90
- mini_portile (~> 0.6.0)
81
+ mustermann (1.0.3)
82
+ nio4r (2.3.1)
83
+ nokogiri (1.8.5)
84
+ mini_portile2 (~> 2.3.0)
91
85
  pry (0.10.1)
92
86
  coderay (~> 1.1.0)
93
87
  method_source (~> 0.8.1)
94
88
  slop (~> 3.4)
95
- rack (1.6.4)
96
- rack-protection (1.5.3)
89
+ rack (2.0.5)
90
+ rack-protection (2.0.4)
97
91
  rack
98
92
  rack-test (0.6.3)
99
93
  rack (>= 1.0)
100
- rails (4.2.4)
101
- actionmailer (= 4.2.4)
102
- actionpack (= 4.2.4)
103
- actionview (= 4.2.4)
104
- activejob (= 4.2.4)
105
- activemodel (= 4.2.4)
106
- activerecord (= 4.2.4)
107
- activesupport (= 4.2.4)
108
- bundler (>= 1.3.0, < 2.0)
109
- railties (= 4.2.4)
110
- sprockets-rails
111
- rails-deprecated_sanitizer (1.0.3)
112
- activesupport (>= 4.2.0.alpha)
113
- rails-dom-testing (1.0.7)
114
- activesupport (>= 4.2.0.beta, < 5.0)
115
- nokogiri (~> 1.6.0)
116
- rails-deprecated_sanitizer (>= 1.0.1)
117
- rails-html-sanitizer (1.0.2)
118
- loofah (~> 2.0)
119
- railties (4.2.4)
120
- actionpack (= 4.2.4)
121
- activesupport (= 4.2.4)
94
+ rails (5.0.7)
95
+ actioncable (= 5.0.7)
96
+ actionmailer (= 5.0.7)
97
+ actionpack (= 5.0.7)
98
+ actionview (= 5.0.7)
99
+ activejob (= 5.0.7)
100
+ activemodel (= 5.0.7)
101
+ activerecord (= 5.0.7)
102
+ activesupport (= 5.0.7)
103
+ bundler (>= 1.3.0)
104
+ railties (= 5.0.7)
105
+ sprockets-rails (>= 2.0.0)
106
+ rails-dom-testing (2.0.3)
107
+ activesupport (>= 4.2.0)
108
+ nokogiri (>= 1.6)
109
+ rails-html-sanitizer (1.0.4)
110
+ loofah (~> 2.2, >= 2.2.2)
111
+ railties (5.0.7)
112
+ actionpack (= 5.0.7)
113
+ activesupport (= 5.0.7)
114
+ method_source
122
115
  rake (>= 0.8.7)
123
116
  thor (>= 0.18.1, < 2.0)
124
- rake (10.4.2)
117
+ rake (12.3.1)
125
118
  redcarpet (3.3.4)
126
- redis (3.2.1)
127
- redis-activesupport (4.1.5)
128
- activesupport (>= 3, < 5)
129
- redis-store (~> 1.1.0)
130
- redis-namespace (1.5.2)
131
- redis (~> 3.0, >= 3.0.4)
132
- redis-store (1.1.7)
133
- redis (>= 2.2)
119
+ redis (4.0.2)
120
+ redis-activesupport (5.0.7)
121
+ activesupport (>= 3, < 6)
122
+ redis-store (>= 1.3, < 2)
123
+ redis-namespace (1.6.0)
124
+ redis (>= 3.0.4)
125
+ redis-store (1.6.0)
126
+ redis (>= 2.2, < 5)
134
127
  rspec (3.4.0)
135
128
  rspec-core (~> 3.4.0)
136
129
  rspec-expectations (~> 3.4.0)
@@ -150,58 +143,62 @@ GEM
150
143
  ruby_parser (3.7.2)
151
144
  sexp_processor (~> 4.1)
152
145
  sexp_processor (4.6.0)
153
- sidekiq (3.5.2)
154
- celluloid (~> 0.17.2)
146
+ sidekiq (5.0.5)
147
+ concurrent-ruby (~> 1.0)
155
148
  connection_pool (~> 2.2, >= 2.2.0)
156
- json (~> 1.0)
157
- redis (~> 3.2, >= 3.2.1)
158
- redis-namespace (~> 1.5, >= 1.5.2)
149
+ rack-protection (>= 1.5.0)
150
+ redis (>= 3.3.4, < 5)
159
151
  simplecov (0.9.2)
160
152
  docile (~> 1.1.0)
161
153
  multi_json (~> 1.0)
162
154
  simplecov-html (~> 0.9.0)
163
155
  simplecov-html (0.9.0)
164
- sinatra (1.4.6)
165
- rack (~> 1.4)
166
- rack-protection (~> 1.4)
167
- tilt (>= 1.3, < 3)
156
+ sinatra (2.0.4)
157
+ mustermann (~> 1.0)
158
+ rack (~> 2.0)
159
+ rack-protection (= 2.0.4)
160
+ tilt (~> 2.0)
168
161
  slop (3.6.0)
169
162
  sourcify (0.5.0)
170
163
  file-tail (>= 1.0.5)
171
164
  ruby2ruby (>= 1.2.5)
172
165
  ruby_parser (>= 2.0.5)
173
166
  sexp_processor (>= 3.0.5)
174
- sprockets (3.4.0)
167
+ sprockets (3.7.2)
168
+ concurrent-ruby (~> 1.0)
175
169
  rack (> 1, < 3)
176
- sprockets-rails (2.3.3)
177
- actionpack (>= 3.0)
178
- activesupport (>= 3.0)
179
- sprockets (>= 2.8, < 4.0)
180
- thor (0.19.1)
181
- thread_safe (0.3.5)
182
- tilt (1.4.1)
183
- timers (4.1.1)
184
- hitimes
170
+ sprockets-rails (3.2.1)
171
+ actionpack (>= 4.0)
172
+ activesupport (>= 4.0)
173
+ sprockets (>= 3.0.0)
174
+ thor (0.20.0)
175
+ thread_safe (0.3.6)
176
+ tilt (2.0.8)
185
177
  tins (1.8.1)
186
- tzinfo (1.2.2)
178
+ tzinfo (1.2.5)
187
179
  thread_safe (~> 0.1)
188
- yard (0.8.7.6)
180
+ websocket-driver (0.6.5)
181
+ websocket-extensions (>= 0.1.0)
182
+ websocket-extensions (0.1.3)
183
+ yard (0.9.16)
189
184
 
190
185
  PLATFORMS
191
186
  ruby
192
187
 
193
188
  DEPENDENCIES
189
+ appraisal (~> 2.2.0)
194
190
  async_cache!
195
191
  httparty (~> 0.13.7)
196
192
  pry (~> 0.10.1)
197
- rails (~> 4.2.4)
193
+ rails (~> 5.0.1)
198
194
  redcarpet (~> 3.3.4)
199
- redis-activesupport (~> 4.1.5)
195
+ redis-activesupport (~> 5.0.7)
196
+ redis-namespace (~> 1.6.0)
200
197
  rspec (~> 3.4.0)
201
- sidekiq (~> 3.5.2)
198
+ sidekiq (~> 5.0.5)
202
199
  simplecov (~> 0.9.2)
203
- sinatra (~> 1.4.6)
204
- yard (~> 0.8)
200
+ sinatra (~> 2.0.4)
201
+ yard (~> 0.9.11)
205
202
 
206
203
  BUNDLED WITH
207
- 1.11.2
204
+ 1.16.1
data/LICENSE CHANGED
@@ -1,6 +1,7 @@
1
1
  The MIT License (MIT)
2
2
 
3
3
  Copyright (c) 2015 Everlane Inc.
4
+ Copyright (c) 2017-2018 Brandless Inc.
4
5
 
5
6
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
7
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # async_cache
2
2
 
3
- Caching is great, but having to block your app while you refresh the cache isn't so great, especially when it takes a long time to regenerate that cache entry.
3
+ Async caching lets you serve slightly-stale data immediately while generating an up-to-date version in the background.
4
4
 
5
- This outlines a strategy and provides a Rails-focused implementation for asynchronously refreshing caches while serving the stale version to users to maintain responsiveness.
5
+ This provides a Ruby implementation of the async caching strategy (detailed below). It works with both Sidekiq (recommended) and ActiveJob systems. Usage examples are provided below and in the [`examples`](examples/) directory.
6
6
 
7
7
  ## Usage
8
8
 
@@ -15,15 +15,17 @@ Gem::Specification.new do |s|
15
15
 
16
16
  s.add_dependency 'sourcify', '~> 0.5.0'
17
17
 
18
- s.add_development_dependency 'rails', '~> 4.2.4'
19
- s.add_development_dependency 'sidekiq', '~> 3.5.2'
20
- s.add_development_dependency 'sinatra', '~> 1.4.6'
18
+ s.add_development_dependency 'appraisal', '~> 2.2.0'
19
+ s.add_development_dependency 'rails', '~> 5.0.1'
20
+ s.add_development_dependency 'sidekiq', '~> 5.0.5'
21
+ s.add_development_dependency 'redis-namespace', '~> 1.6.0'
22
+ s.add_development_dependency 'sinatra', '~> 2.0.4'
21
23
  s.add_development_dependency 'rspec', '~> 3.4.0'
22
24
  s.add_development_dependency 'pry', '~> 0.10.1'
23
25
  s.add_development_dependency 'simplecov', '~> 0.9.2'
24
- s.add_development_dependency 'redis-activesupport', '~> 4.1.5'
26
+ s.add_development_dependency 'redis-activesupport', '~> 5.0.7'
25
27
  s.add_development_dependency 'httparty', '~> 0.13.7'
26
- s.add_development_dependency 'yard', '~> 0.8'
28
+ s.add_development_dependency 'yard', '~> 0.9.11'
27
29
  s.add_development_dependency 'redcarpet', '~> 3.3.4'
28
30
 
29
31
  s.files = `git ls-files`.split("\n")
@@ -0,0 +1,10 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 4.2.10"
6
+ gem "sidekiq", "~> 3.5.4"
7
+ gem "sinatra", "~> 1.4.8"
8
+ gem "redis-activesupport", "~> 4.1.5"
9
+
10
+ gemspec path: "../"
@@ -0,0 +1,5 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec path: "../"
@@ -1,20 +1,31 @@
1
1
  require 'sourcify'
2
2
 
3
3
  module AsyncCache
4
- class << self
5
- def backend
6
- @backend || Rails.cache
7
- end
8
- def backend=(backend)
9
- @backend = backend
10
- end
4
+ DEFAULT_OPTIONS = {
5
+ # How long Sidekiq Enterprise should hold a uniqueness lock. The default
6
+ # is 10 minutes.
7
+ uniqueness_timeout: 600,
8
+ }
11
9
 
12
- def logger
13
- @logger || Rails.logger
14
- end
15
- def logger=(logger)
16
- @logger = logger
17
- end
10
+ def self.options
11
+ @options ||= DEFAULT_OPTIONS.dup
12
+ end
13
+ def self.options=(options)
14
+ @options = options
15
+ end
16
+
17
+ def self.backend
18
+ @backend ||= Rails.cache
19
+ end
20
+ def self.backend=(backend)
21
+ @backend = backend
22
+ end
23
+
24
+ def self.logger
25
+ @logger ||= Rails.logger
26
+ end
27
+ def self.logger=(logger)
28
+ @logger = logger
18
29
  end
19
30
  end
20
31
 
@@ -1,3 +1,5 @@
1
+ require 'digest/md5'
2
+
1
3
  module AsyncCache
2
4
  class Store
3
5
  attr_accessor :backend, :worker_klass
@@ -34,43 +36,45 @@ module AsyncCache
34
36
  # @param [Hash] options
35
37
  # @yield [*arguments in options[:arguments]] Called if entry out-of-date
36
38
  def fetch(locator, version, options = {}, &block)
37
- options = options.dup # Duplicate to avoid side effects
39
+ options = options.dup # Duplicate to avoid side effects
38
40
  version = version.to_i # Versions must *always* be convertible to integers
39
41
 
40
42
  # Expires-in must be an integer if present, nil if not
41
43
  expires_in = options[:expires_in] ? options[:expires_in].to_i : nil
42
44
 
45
+ block_source = block.to_source
43
46
  block_arguments = check_arguments(options.delete(:arguments) || [])
44
47
 
45
48
  # Serialize arguments into the full cache key
46
- key = ActiveSupport::Cache.expand_cache_key Array.wrap(locator) + block_arguments
49
+ key = ActiveSupport::Cache.expand_cache_key([
50
+ Store.base_cache_key(locator, block_source),
51
+ block_arguments
52
+ ].flatten)
47
53
 
48
54
  cached_data, cached_version = @backend.read key
49
55
 
50
56
  strategy = determine_strategy(
51
- :has_cached_data => !!cached_data,
52
- :needs_regen => version > (cached_version || 0),
53
- :synchronous_regen => options[:synchronous_regen]
57
+ has_cached_data: !!cached_data,
58
+ needs_regen: version > (cached_version || 0),
59
+ synchronous_regen: options[:synchronous_regen]
54
60
  )
55
61
 
62
+ return cached_data if strategy == :current
63
+
56
64
  context = {
57
- :key => key,
58
- :version => version,
59
- :expires_in => expires_in,
60
- :block => block,
61
- :arguments => block_arguments
65
+ key: key,
66
+ version: version,
67
+ expires_in: expires_in,
68
+ block_source: block_source,
69
+ arguments: block_arguments
62
70
  }
63
71
 
64
72
  case strategy
65
73
  when :generate
66
74
  return generate_and_cache context
67
-
68
75
  when :enqueue
69
76
  enqueue_generation context
70
77
  return cached_data
71
-
72
- when :current
73
- return cached_data
74
78
  end
75
79
  end
76
80
 
@@ -98,9 +102,7 @@ module AsyncCache
98
102
  end
99
103
  end
100
104
 
101
- def generate_and_cache(key:, version:, expires_in:, block:, arguments:)
102
- block_source = block.to_source
103
-
105
+ def generate_and_cache(key:, version:, expires_in:, block_source:, arguments:)
104
106
  # Mimic the destruction-of-scope behavior of the worker in development
105
107
  # so it will *fail* for developers if they try to depend upon scope
106
108
  block = eval(block_source)
@@ -113,19 +115,19 @@ module AsyncCache
113
115
  return data
114
116
  end
115
117
 
116
- def enqueue_generation(key:, version:, expires_in:, block:, arguments:)
118
+ def enqueue_generation(key:, version:, expires_in:, block_source:, arguments:)
117
119
  worker_klass.enqueue_async_job(
118
- key: key,
119
- version: version,
120
+ key: key,
121
+ version: version,
120
122
  expires_in: expires_in,
121
- block: block.to_source,
122
- arguments: arguments
123
+ block: block_source,
124
+ arguments: arguments
123
125
  )
124
126
  end
125
127
 
126
128
  def inspect
127
- pointer_format = '0x%014x'
128
- pointer = Kernel.sprintf pointer_format, self.object_id * 2
129
+ pointer_format = '0x%014x'
130
+ pointer = Kernel.sprintf pointer_format, self.object_id * 2
129
131
  backend_pointer = Kernel.sprintf pointer_format, @backend.object_id * 2
130
132
 
131
133
  '#<' + [
@@ -135,6 +137,16 @@ module AsyncCache
135
137
  ].join('') + '>'
136
138
  end
137
139
 
140
+ # Build the base part of the cache key with the locator and the digest
141
+ # of the block source. This ensures that if the implementation (block)
142
+ # changes then the cache key will also change.
143
+ def self.base_cache_key(locator, block_source)
144
+ ActiveSupport::Cache.expand_cache_key([
145
+ locator,
146
+ Digest::MD5.hexdigest(block_source)
147
+ ])
148
+ end
149
+
138
150
  private
139
151
 
140
152
  # Ensures the arguments are primitives.
@@ -1,3 +1,3 @@
1
1
  module AsyncCache
2
- VERSION = '1.1.0'
2
+ VERSION = '1.2.0'
3
3
  end
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/array/wrap'
2
+
1
3
  module AsyncCache
2
4
  module Workers
3
5
  def self.worker_for_name(name)
@@ -41,8 +43,6 @@ module AsyncCache
41
43
  # @param [Array] block_arguments Arguments with which to call the block
42
44
  # @param [String] block_source Ruby source to evaluate to produce the value
43
45
  def perform key, version, expires_in, block_arguments, block_source
44
- t0 = Time.now
45
-
46
46
  _cached_data, cached_version = backend.read key
47
47
  return unless version > (cached_version || 0)
48
48
 
@@ -7,8 +7,19 @@ module AsyncCache
7
7
  include Base
8
8
  include Sidekiq::Worker
9
9
 
10
- # Only allow one job per set of arguments to ever be in the queue
11
- sidekiq_options :unique => :until_executed
10
+ # Pulled out into a module so it can be tested.
11
+ module Options
12
+ def self.included(mod)
13
+ if defined?(Sidekiq::Enterprise)
14
+ mod.sidekiq_options unique_for: AsyncCache.options[:uniqueness_timeout]
15
+ elsif defined?(SidekiqUniqueJobs)
16
+ # Only allow one job per set of arguments to ever be in the queue
17
+ mod.sidekiq_options unique: :until_executed
18
+ end
19
+ end
20
+ end
21
+
22
+ include Options
12
23
 
13
24
  # Use the Sidekiq API to see if there are worker processes available to
14
25
  # handle the async cache jobs queue.
File without changes
@@ -11,4 +11,8 @@ SimpleCov.start
11
11
  Rails.cache = ActiveSupport::Cache::MemoryStore.new
12
12
  Rails.logger = Logger.new($stdout).tap { |log| log.level = Logger::ERROR }
13
13
 
14
+ # Fake the `sidekiq-unique-jobs` gem being loaded.
15
+ module SidekiqUniqueJobs
16
+ end
17
+
14
18
  require 'async_cache'
@@ -2,9 +2,10 @@ require 'spec_helper'
2
2
  require 'async_cache/workers/sidekiq'
3
3
 
4
4
  describe AsyncCache::Store do
5
+ Store = AsyncCache::Store
5
6
 
6
7
  subject do
7
- AsyncCache::Store.new(
8
+ Store.new(
8
9
  backend: Rails.cache,
9
10
  worker: :sidekiq
10
11
  )
@@ -12,7 +13,7 @@ describe AsyncCache::Store do
12
13
 
13
14
  it "raises if it doesn't receive a worker class" do
14
15
  expect {
15
- AsyncCache::Store.new backend: Rails.cache
16
+ Store.new backend: Rails.cache
16
17
  }.to raise_error(ArgumentError)
17
18
  end
18
19
 
@@ -32,20 +33,26 @@ describe AsyncCache::Store do
32
33
  end
33
34
 
34
35
  it "synchronously calls #fetch if entry isn't present" do
35
- stub_not_present @key
36
+ block = proc { 'something' }
37
+ cache_key = Store.base_cache_key @key, block.to_source
38
+
39
+ stub_not_present cache_key
36
40
 
37
41
  version = Time.now.to_i
38
42
  expires_in = 1.minute
39
43
 
40
44
  # Expect another synchronous call with a block to compute the value
41
- expect(Rails.cache).to receive(:write).with(@key, ['something', version], {:expires_in => expires_in}).and_call_original
45
+ expect(Rails.cache).to receive(:write).with(cache_key, ['something', version], {:expires_in => expires_in}).and_call_original
42
46
 
43
- fetched_value = subject.fetch(@key, version, :expires_in => expires_in) { 'something' }
47
+ fetched_value = subject.fetch(@key, version, :expires_in => expires_in, &block)
44
48
 
45
49
  expect(fetched_value).to eql 'something'
46
50
  end
47
51
 
48
52
  it 'returns the stale value and enqueues the worker if entry is present and timestamp is changed' do
53
+ block = proc { |private_argument| private_argument * 2 }
54
+ base_key = Store.base_cache_key @key, block.to_source
55
+
49
56
  # It will try to check that workers are present, so we need to make that
50
57
  # check be a no-op
51
58
  allow(subject.worker_klass).to receive(:has_workers?).and_return(true)
@@ -57,7 +64,7 @@ describe AsyncCache::Store do
57
64
 
58
65
  # Cache key is composed of *both* the key and the arguments given to the
59
66
  # block since those arguments determine the output of the block
60
- cache_key = ActiveSupport::Cache.expand_cache_key([@key] + arguments)
67
+ cache_key = ActiveSupport::Cache.expand_cache_key([base_key] + arguments)
61
68
 
62
69
  stub_present cache_key, 'old!'
63
70
 
@@ -76,20 +83,21 @@ describe AsyncCache::Store do
76
83
  expect(eval(block_source).call(*block_arguments)).to eql 2
77
84
  end
78
85
 
79
- fetched_value = subject.fetch(@key, timestamp, :expires_in => expires_in, :arguments => arguments) do |private_argument|
80
- private_argument * 2
81
- end
86
+ fetched_value = subject.fetch(@key, timestamp, :expires_in => expires_in, :arguments => arguments, &block)
82
87
 
83
88
  # Check that it immediately returns the stale value
84
89
  expect(fetched_value).to eql old_value
85
90
  end
86
91
 
87
92
  it "returns the current value if timestamp isn't changed" do
88
- stub_present @key, 'value'
93
+ block = proc { 'bad!' }
94
+ cache_key = Store.base_cache_key @key, block.to_source
95
+
96
+ stub_present cache_key, 'value'
89
97
 
90
98
  timestamp = 0 # `stub_present` returns a timestamp of 0
91
99
 
92
- expect(subject.fetch(@key, timestamp, :expires_in => 1.minute) { 'bad!' }).to eql 'value'
100
+ expect(subject.fetch(@key, timestamp, :expires_in => 1.minute, &block)).to eql 'value'
93
101
  end
94
102
 
95
103
  end # context caching
@@ -6,6 +6,13 @@ describe AsyncCache::Workers::SidekiqWorker do
6
6
  AsyncCache::Workers::SidekiqWorker
7
7
  end
8
8
 
9
+ describe '.sidekiq_options' do
10
+ # See `spec_helper.rb` which makes it think the gem is loaded.
11
+ it 'has the uniqueness option for `sidekiq-unique-jobs`' do
12
+ expect(subject.sidekiq_options_hash).to include 'unique'
13
+ end
14
+ end
15
+
9
16
  describe '::has_workers?' do
10
17
  it 'returns false if no Sidekiq queues are available' do
11
18
  allow(subject).to receive(:sidekiq_options).and_return({'queue' => 'good_queue'})
@@ -37,4 +44,34 @@ describe AsyncCache::Workers::SidekiqWorker do
37
44
  )
38
45
  end
39
46
  end
47
+
48
+ describe AsyncCache::Workers::SidekiqWorker::Options do
49
+ subject do
50
+ AsyncCache::Workers::SidekiqWorker::Options
51
+ end
52
+
53
+ before do
54
+ # Set by `spec_helper.rb`.
55
+ hide_const 'SidekiqUniqueJobs'
56
+
57
+ class Worker
58
+ end
59
+ end
60
+
61
+ it 'sets correct option for `sidekiq-unique-jobs`' do
62
+ stub_const 'SidekiqUniqueJobs', Module.new
63
+
64
+ expect(Worker).to receive(:sidekiq_options).with(unique: :until_executed)
65
+
66
+ Worker.include subject
67
+ end
68
+
69
+ it 'sets correct options for Sidekiq Enterprise' do
70
+ stub_const 'Sidekiq::Enterprise', Module.new
71
+
72
+ expect(Worker).to receive(:sidekiq_options).with(unique_for: AsyncCache.options[:uniqueness_timeout])
73
+
74
+ Worker.include subject
75
+ end
76
+ end
40
77
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: async_cache
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Derewecki
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-01-21 00:00:00.000000000 Z
12
+ date: 2018-10-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sourcify
@@ -25,48 +25,76 @@ dependencies:
25
25
  - - "~>"
26
26
  - !ruby/object:Gem::Version
27
27
  version: 0.5.0
28
+ - !ruby/object:Gem::Dependency
29
+ name: appraisal
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: 2.2.0
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: 2.2.0
28
42
  - !ruby/object:Gem::Dependency
29
43
  name: rails
30
44
  requirement: !ruby/object:Gem::Requirement
31
45
  requirements:
32
46
  - - "~>"
33
47
  - !ruby/object:Gem::Version
34
- version: 4.2.4
48
+ version: 5.0.1
35
49
  type: :development
36
50
  prerelease: false
37
51
  version_requirements: !ruby/object:Gem::Requirement
38
52
  requirements:
39
53
  - - "~>"
40
54
  - !ruby/object:Gem::Version
41
- version: 4.2.4
55
+ version: 5.0.1
42
56
  - !ruby/object:Gem::Dependency
43
57
  name: sidekiq
44
58
  requirement: !ruby/object:Gem::Requirement
45
59
  requirements:
46
60
  - - "~>"
47
61
  - !ruby/object:Gem::Version
48
- version: 3.5.2
62
+ version: 5.0.5
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: 5.0.5
70
+ - !ruby/object:Gem::Dependency
71
+ name: redis-namespace
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: 1.6.0
49
77
  type: :development
50
78
  prerelease: false
51
79
  version_requirements: !ruby/object:Gem::Requirement
52
80
  requirements:
53
81
  - - "~>"
54
82
  - !ruby/object:Gem::Version
55
- version: 3.5.2
83
+ version: 1.6.0
56
84
  - !ruby/object:Gem::Dependency
57
85
  name: sinatra
58
86
  requirement: !ruby/object:Gem::Requirement
59
87
  requirements:
60
88
  - - "~>"
61
89
  - !ruby/object:Gem::Version
62
- version: 1.4.6
90
+ version: 2.0.4
63
91
  type: :development
64
92
  prerelease: false
65
93
  version_requirements: !ruby/object:Gem::Requirement
66
94
  requirements:
67
95
  - - "~>"
68
96
  - !ruby/object:Gem::Version
69
- version: 1.4.6
97
+ version: 2.0.4
70
98
  - !ruby/object:Gem::Dependency
71
99
  name: rspec
72
100
  requirement: !ruby/object:Gem::Requirement
@@ -115,14 +143,14 @@ dependencies:
115
143
  requirements:
116
144
  - - "~>"
117
145
  - !ruby/object:Gem::Version
118
- version: 4.1.5
146
+ version: 5.0.7
119
147
  type: :development
120
148
  prerelease: false
121
149
  version_requirements: !ruby/object:Gem::Requirement
122
150
  requirements:
123
151
  - - "~>"
124
152
  - !ruby/object:Gem::Version
125
- version: 4.1.5
153
+ version: 5.0.7
126
154
  - !ruby/object:Gem::Dependency
127
155
  name: httparty
128
156
  requirement: !ruby/object:Gem::Requirement
@@ -143,14 +171,14 @@ dependencies:
143
171
  requirements:
144
172
  - - "~>"
145
173
  - !ruby/object:Gem::Version
146
- version: '0.8'
174
+ version: 0.9.11
147
175
  type: :development
148
176
  prerelease: false
149
177
  version_requirements: !ruby/object:Gem::Requirement
150
178
  requirements:
151
179
  - - "~>"
152
180
  - !ruby/object:Gem::Version
153
- version: '0.8'
181
+ version: 0.9.11
154
182
  - !ruby/object:Gem::Dependency
155
183
  name: redcarpet
156
184
  requirement: !ruby/object:Gem::Requirement
@@ -177,6 +205,7 @@ files:
177
205
  - ".rspec"
178
206
  - ".travis.yml"
179
207
  - ".yardopts"
208
+ - Appraisals
180
209
  - Gemfile
181
210
  - Gemfile.lock
182
211
  - LICENSE
@@ -184,6 +213,8 @@ files:
184
213
  - Rakefile
185
214
  - async_cache.gemspec
186
215
  - examples/examples_controller.rb
216
+ - gemfiles/rails_4.gemfile
217
+ - gemfiles/rails_5.gemfile
187
218
  - lib/async_cache.rb
188
219
  - lib/async_cache/railtie.rb
189
220
  - lib/async_cache/store.rb
@@ -224,9 +255,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
224
255
  version: '0'
225
256
  requirements: []
226
257
  rubyforge_project:
227
- rubygems_version: 2.5.1
258
+ rubygems_version: 2.6.8
228
259
  signing_key:
229
260
  specification_version: 4
230
261
  summary: Pattern and library for implementing asynchronous caching
231
262
  test_files: []
232
- has_rdoc: