async_cache 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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: