cache_json 1.2.0 → 1.2.5

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
  SHA256:
3
- metadata.gz: fe1d01d761ef7ff3aff1777d3a019704095b1f50a9ba2ab57fd5a5ba72e02816
4
- data.tar.gz: 64b4ac0d7350fbb3125c11c16c9d952e847efa8d230cd54d024f56cfb7ede509
3
+ metadata.gz: 5df5d41a7515bef89f1d04f0368a8e4704a2ecf7ee46e8c43fa4a39b76850a37
4
+ data.tar.gz: 11baaf8f7055c0dfb5ae0928e47f85d881e0dc83ffb368c8744d2573c42ba5d4
5
5
  SHA512:
6
- metadata.gz: ad7911c949d27461f994f16bfcb19e93a5c0830e7b606ccf87e4362042d888056d9a9e029608381550a997297691b34e5570827eb2da64666b0b8d407647fb8e
7
- data.tar.gz: 1cfde86c126160747298a70ef8ebc07dcfb0be8e6e66945c464262a99de4a227ef4e264a0d4e265ba5724afd1a5cee5e94dca78eea53e757b3cb0fdfa4c94bf7
6
+ metadata.gz: 70db145dc314055f2f5200a1e51cd05ba807ac2489765f22275f3ee747a15dac358907c2597b888a918c771d6396c200a52c9d7113424b3504fe2ce07addc8e5
7
+ data.tar.gz: a99dc593f2e01d575516489dd8321d7d2969b6b05b723f740fc02bd1752d62cedda91d37a5cee4763b6bafa6e29799d8d33570ff01af7f0b56f81f52ac89c7a5
data/.rubocop.yml CHANGED
@@ -1,14 +1,41 @@
1
- Style/GlobalVars:
2
- Enabled: false
3
-
4
- Style/Documentation:
5
- Enabled: false
1
+ Metrics/BlockLength:
2
+ Enabled: true
3
+ Exclude:
4
+ - "spec/**/*"
6
5
 
7
- Metrics/LineLength:
6
+ Layout/LineLength:
8
7
  Enabled: true
9
8
  Max: 100
10
9
 
11
- Metrics/BlockLength:
10
+ Layout/EmptyLinesAroundAttributeAccessor:
12
11
  Enabled: true
13
- Exclude:
14
- - "spec/**/*"
12
+
13
+ Layout/SpaceAroundMethodCallOperator:
14
+ Enabled: true
15
+
16
+ Lint/RaiseException:
17
+ Enabled: true
18
+
19
+ Lint/StructNewOverride:
20
+ Enabled: true
21
+
22
+ Style/ExponentialNotation:
23
+ Enabled: true
24
+
25
+ Style/HashEachMethods:
26
+ Enabled: true
27
+
28
+ Style/HashTransformKeys:
29
+ Enabled: true
30
+
31
+ Style/HashTransformValues:
32
+ Enabled: true
33
+
34
+ Style/SlicingWithRange:
35
+ Enabled: true
36
+
37
+ Style/GlobalVars:
38
+ Enabled: false
39
+
40
+ Style/Documentation:
41
+ Enabled: false
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cache_json (1.2.0)
4
+ cache_json (1.2.5)
5
5
  redis (>= 3.3.5, < 5)
6
6
 
7
7
  GEM
@@ -15,7 +15,7 @@ GEM
15
15
  parallel (1.19.1)
16
16
  parser (2.7.0.3)
17
17
  ast (~> 2.4.0)
18
- rack (2.2.2)
18
+ rack (2.2.3)
19
19
  rack-protection (2.0.8.1)
20
20
  rack
21
21
  rainbow (3.0.0)
@@ -66,4 +66,4 @@ DEPENDENCIES
66
66
  timecop (~> 0.9.1)
67
67
 
68
68
  BUNDLED WITH
69
- 2.0.2
69
+ 2.2.3
data/README.md CHANGED
@@ -54,6 +54,44 @@ CacheJSON.configure do |config|
54
54
  end
55
55
  ```
56
56
 
57
+ ## Automatic refreshing (Sidekiq)
58
+
59
+ There is a simple Sidekiq job that lets you pre-compute selected classes with specified ranges of arguments. All you have to do is add a `refresh` option:
60
+
61
+ ```ruby
62
+ class ExpensiveJob
63
+
64
+ include CacheJSON::Base
65
+
66
+
67
+ cache_json_options(
68
+ time_to_expire: 1.hour,
69
+ refresh: {
70
+ buffer: 5.minutes,
71
+ arguments: {
72
+ first: (5..10),
73
+ second: ['one option', 'another option'],
74
+ third: 'the only option',
75
+ fourth: -> { ['proc result'] }
76
+ }
77
+ }
78
+ )
79
+ ...
80
+ end
81
+ ```
82
+
83
+ The Sidekiq job will take the Cartesian product of all the argument ranges/arrays (all the combinations).
84
+
85
+ We leave it to you to schedule the job. If you're using https://github.com/moove-it/sidekiq-scheduler, you can do something like this:
86
+
87
+ ```yml
88
+ cache_json_worker:
89
+ every: "20s"
90
+ class: CacheJSON::Worker
91
+ ```
92
+
93
+ Whenever the worker runs, it checks which results have expired, and refreshes only those. If you pass in the `buffer` option, it will actually refresh keys that are that far away from expiring. In the example above, the worker will refresh the cache 5 minutes before it expires. This is good if you want to avoid cache misses altogether.
94
+
57
95
  ## Development
58
96
 
59
97
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -31,6 +31,10 @@ module CacheJSON
31
31
  $redis.del(class_key)
32
32
  end
33
33
 
34
+ def cache_expiring_soon?
35
+ $redis.ttl(full_key) < options[:refresh][:buffer].to_i
36
+ end
37
+
34
38
  private
35
39
 
36
40
  def full_key
@@ -8,7 +8,7 @@ module CacheJSON
8
8
  base.extend(ClassMethods)
9
9
  end
10
10
 
11
- def results(args)
11
+ def results(args = {})
12
12
  raise ArgumentError, 'Must use keyword arguments' unless args.is_a?(Hash)
13
13
 
14
14
  options = self.class.cache_json_full_options
@@ -17,13 +17,18 @@ module CacheJSON
17
17
  JSON.parse(refresh_cache!(args: args, cache: cache).to_json) # stringify keys
18
18
  end
19
19
 
20
+ def cache_expiring_soon?(args:, cache: nil)
21
+ cache ||= Cache.new(args: args, options: self.class.cache_json_full_options)
22
+ cache.cache_expiring_soon?
23
+ end
24
+
20
25
  def clear_cache!
21
26
  Cache.new(options: self.class.cache_json_full_options).clear_cache!
22
27
  end
23
28
 
24
29
  def refresh_cache!(args:, cache: nil)
25
30
  cache ||= Cache.new(args: args, options: self.class.cache_json_full_options)
26
- results = compute_results(args)
31
+ results = compute_results(**args)
27
32
  cache.cached_results = results
28
33
  results
29
34
  end
@@ -54,6 +59,10 @@ module CacheJSON
54
59
  adapter.clear_cache!
55
60
  end
56
61
 
62
+ def cache_expiring_soon?
63
+ adapter.cache_expiring_soon?
64
+ end
65
+
57
66
  def adapter
58
67
  @adapter ||= CacheJSON::Adapters::Redis.new(args: args, options: options)
59
68
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CacheJSON
4
- VERSION = '1.2.0'
4
+ VERSION = '1.2.5'
5
5
  end
@@ -5,7 +5,6 @@ begin
5
5
  rescue LoadError
6
6
  nil
7
7
  end
8
-
9
8
  module CacheJSON
10
9
  class Worker
11
10
  if defined?(Sidekiq)
@@ -18,66 +17,79 @@ module CacheJSON
18
17
  end
19
18
  end
20
19
 
21
- def perform(klass: nil, args: {})
20
+ def perform(params="{}")
21
+ parsed_params = JSON.parse(params)
22
+ klass = parsed_params["klass"]
23
+ args = (parsed_params["args"] || {}).transform_keys(&:to_sym)
22
24
  if klass
23
- klass.new.refresh_cache!(args: args)
25
+ klass = Object.const_get(klass)
26
+ klass.new.refresh_cache!(args: args) if should_refresh?(klass, args)
24
27
  else
25
- generate_workers
28
+ AllPermutations.new.results.each do |perm|
29
+ if should_refresh?(perm[:klass], perm[:args])
30
+ CacheJSON::Worker.perform_async(perm.to_json)
31
+ end
32
+ end
26
33
  end
27
34
  end
28
35
 
29
36
  private
30
37
 
31
- def generate_workers
32
- all_cache_classes.each do |klass|
33
- all_argument_permutations(klass).each do |args|
34
- CacheJSON::Worker.new.perform(klass: klass, args: args) if should_refresh?(klass, args)
35
- end
36
- end
38
+ def should_refresh?(klass, args)
39
+ !klass.new.check_cache(args: args) || klass.new.cache_expiring_soon?(args: args)
37
40
  end
38
41
 
39
- def all_cache_classes
40
- # TODO: make this more efficient
41
- ObjectSpace.each_object(Class).select do |klass|
42
- klass.included_modules.include? CacheJSON::Base
42
+ class AllPermutations
43
+ def results
44
+ all_cache_classes.flat_map do |klass|
45
+ all_argument_permutations(klass).map do |args|
46
+ {
47
+ klass: klass,
48
+ args: args
49
+ }
50
+ end
51
+ end
43
52
  end
44
- end
45
53
 
46
- def all_argument_permutations(klass)
47
- refresh_options = klass.cache_json_full_options[:refresh]
48
- if refresh_options
49
- all_combinations_with_fixed_points({}, refresh_options[:arguments])
50
- else
51
- []
54
+ def all_cache_classes
55
+ # TODO: make this more efficient
56
+ ObjectSpace.each_object(Class).select do |klass|
57
+ klass.included_modules.include? CacheJSON::Base
58
+ end
52
59
  end
53
- end
54
60
 
55
- def all_combinations_with_fixed_points(fixed_points, full_hash)
56
- non_fixed_points = full_hash.dup.delete_if { |k, _| fixed_points.key?(k) }
57
- if non_fixed_points.empty?
58
- [fixed_points]
59
- else
60
- pivot_key = non_fixed_points.keys.first
61
- values_for_key(non_fixed_points, pivot_key).flat_map do |pivot_key_value|
62
- new_fixed_points = fixed_points.merge(Hash[pivot_key, pivot_key_value])
63
- all_combinations_with_fixed_points(new_fixed_points, full_hash)
61
+ def all_argument_permutations(klass)
62
+ refresh_options = klass.cache_json_full_options[:refresh]
63
+ if refresh_options
64
+ all_combinations_with_fixed_points({}, refresh_options[:arguments])
65
+ else
66
+ []
64
67
  end
65
68
  end
66
- end
67
69
 
68
- def values_for_key(hsh, key)
69
- pivot_key_values = hsh[key]
70
- if pivot_key_values.is_a?(Proc)
71
- pivot_key_values.call
72
- elsif pivot_key_values.is_a?(Range) || pivot_key_values.is_a?(Array)
73
- pivot_key_values
74
- else
75
- [pivot_key_values]
70
+ def all_combinations_with_fixed_points(fixed_points, full_hash)
71
+ non_fixed_points = full_hash.dup.delete_if { |k, _| fixed_points.key?(k) }
72
+ if non_fixed_points.empty?
73
+ [fixed_points]
74
+ else
75
+ pivot_key = non_fixed_points.keys.first
76
+ values_for_key(non_fixed_points, pivot_key).flat_map do |pivot_key_value|
77
+ new_fixed_points = fixed_points.merge(Hash[pivot_key, pivot_key_value])
78
+ all_combinations_with_fixed_points(new_fixed_points, full_hash)
79
+ end
80
+ end
76
81
  end
77
- end
78
82
 
79
- def should_refresh?(klass, args)
80
- !klass.new.check_cache(args: args)
83
+ def values_for_key(hsh, key)
84
+ pivot_key_values = hsh[key]
85
+ if pivot_key_values.is_a?(Proc)
86
+ pivot_key_values.call
87
+ elsif pivot_key_values.is_a?(Range) || pivot_key_values.is_a?(Array)
88
+ pivot_key_values
89
+ else
90
+ [pivot_key_values]
91
+ end
92
+ end
81
93
  end
82
94
  end
83
95
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cache_json
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul Gut
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-03-02 00:00:00.000000000 Z
11
+ date: 2021-04-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -166,7 +166,7 @@ licenses:
166
166
  metadata:
167
167
  homepage_uri: https://github.com/loopsupport/cache_json
168
168
  source_code_uri: https://github.com/loopsupport/cache_json
169
- post_install_message:
169
+ post_install_message:
170
170
  rdoc_options: []
171
171
  require_paths:
172
172
  - lib
@@ -181,8 +181,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
181
181
  - !ruby/object:Gem::Version
182
182
  version: '0'
183
183
  requirements: []
184
- rubygems_version: 3.0.3
185
- signing_key:
184
+ rubygems_version: 3.2.3
185
+ signing_key:
186
186
  specification_version: 4
187
187
  summary: Extremely simple Redis caching for any Ruby class
188
188
  test_files: []