cache_json 1.0.0 → 1.1.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
  SHA256:
3
- metadata.gz: 5447969541ae8237cf12f05f7cd4f4d9765ecf698d18987557dc3c38d4226e98
4
- data.tar.gz: 6623c07f0e8b5adcb2db3c1689ef8f3cbde3cf3836a6e520a08c460877f825c6
3
+ metadata.gz: 4c98d70e39deec46936d9103a02a7e79abd094b829f53f7a1e9b203862b398ff
4
+ data.tar.gz: a6e727b179329cc1dd68b8a15b2246c2028cef24f8148bba111b82bf84411d83
5
5
  SHA512:
6
- metadata.gz: 46e08e0520e82b99a050e57d8478a8d87350240cbf2725e8679aac7d51e3142fcdf08109597d6f69dc0ed9f41c4513373e0be28ca1d905bd69e8ba5971281a6f
7
- data.tar.gz: 0445a7014e507e7af83e3023460e1670adb53e965492810bfe0e438b33b6b46069808f16f23a59e16d3da4e69a3a470397c88ba80bc58bcacfc98ebaeb2c29f8
6
+ metadata.gz: 5aae4fbabd515f77747561abe2a427f2265cacb14376d085801bab836897fb79bc016be8ec1d4c4613c20af7705b8ef8cc1ed2d891db161008bb5feaca910f2d
7
+ data.tar.gz: eac08115326cdbe40c10936a6d83e79df496ab4693d2fa414535b27217214fa3b1d79c2c0e7b9fb9e56abaf61329cb0a77b6226bf8d35f12ead8bcc6da6d8829
data/.rubocop.yml ADDED
@@ -0,0 +1,14 @@
1
+ Style/GlobalVars:
2
+ Enabled: false
3
+
4
+ Style/Documentation:
5
+ Enabled: false
6
+
7
+ Metrics/LineLength:
8
+ Enabled: true
9
+ Max: 100
10
+
11
+ Metrics/BlockLength:
12
+ Enabled: true
13
+ Exclude:
14
+ - "spec/**/*"
data/Gemfile CHANGED
@@ -1,4 +1,6 @@
1
- source "https://rubygems.org"
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in cache_json.gemspec
4
6
  gemspec
data/Gemfile.lock CHANGED
@@ -1,14 +1,24 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cache_json (1.0.0)
4
+ cache_json (1.1.0)
5
5
  redis (>= 3.3.5, < 5)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
+ ast (2.4.0)
11
+ connection_pool (2.2.2)
10
12
  diff-lcs (1.3)
13
+ jaro_winkler (1.5.3)
11
14
  mock_redis (0.21.0)
15
+ parallel (1.17.0)
16
+ parser (2.6.3.0)
17
+ ast (~> 2.4.0)
18
+ rack (2.0.7)
19
+ rack-protection (2.0.5)
20
+ rack
21
+ rainbow (3.0.0)
12
22
  rake (10.5.0)
13
23
  redis (4.1.2)
14
24
  rspec (3.8.0)
@@ -24,7 +34,21 @@ GEM
24
34
  diff-lcs (>= 1.2.0, < 2.0)
25
35
  rspec-support (~> 3.8.0)
26
36
  rspec-support (3.8.2)
37
+ rubocop (0.72.0)
38
+ jaro_winkler (~> 1.5.1)
39
+ parallel (~> 1.10)
40
+ parser (>= 2.6)
41
+ rainbow (>= 2.2.2, < 4.0)
42
+ ruby-progressbar (~> 1.7)
43
+ unicode-display_width (>= 1.4.0, < 1.7)
44
+ ruby-progressbar (1.10.1)
45
+ sidekiq (5.2.7)
46
+ connection_pool (~> 2.2, >= 2.2.2)
47
+ rack (>= 1.5.0)
48
+ rack-protection (>= 1.5.0)
49
+ redis (>= 3.3.5, < 5)
27
50
  timecop (0.9.1)
51
+ unicode-display_width (1.6.0)
28
52
 
29
53
  PLATFORMS
30
54
  ruby
@@ -35,6 +59,8 @@ DEPENDENCIES
35
59
  mock_redis (~> 0.21.0)
36
60
  rake (~> 10.0)
37
61
  rspec (~> 3.0)
62
+ rubocop (~> 0.72)
63
+ sidekiq (>= 3.0, < 7)
38
64
  timecop (~> 0.9.1)
39
65
 
40
66
  BUNDLED WITH
data/Rakefile CHANGED
@@ -1,6 +1,8 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
3
5
 
4
6
  RSpec::Core::RakeTask.new(:spec)
5
7
 
6
- task :default => :spec
8
+ task default: :spec
data/bin/console CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- require "bundler/setup"
4
- require "cache_json"
4
+ require 'bundler/setup'
5
+ require 'cache_json'
5
6
 
6
7
  # You can add fixtures and/or initialization code here to make experimenting
7
8
  # with your gem easier. You can also use a different console, if you like.
@@ -10,5 +11,5 @@ require "cache_json"
10
11
  # require "pry"
11
12
  # Pry.start
12
13
 
13
- require "irb"
14
+ require 'irb'
14
15
  IRB.start(__FILE__)
data/cache_json.gemspec CHANGED
@@ -1,34 +1,38 @@
1
- lib = File.expand_path("lib", __dir__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
2
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
- require "cache_json/version"
5
+ require 'cache_json/version'
4
6
 
5
7
  Gem::Specification.new do |spec|
6
- spec.name = "cache_json"
8
+ spec.name = 'cache_json'
7
9
  spec.version = CacheJSON::VERSION
8
- spec.authors = ["Paul Gut"]
9
- spec.email = ["paul@loopsupport.com"]
10
+ spec.authors = ['Paul Gut']
11
+ spec.email = ['paul@loopsupport.com']
10
12
 
11
- spec.summary = "Extremely simple Redis caching for any Ruby class"
12
- spec.description = "Extremely simple Redis caching for any Ruby class"
13
- spec.homepage = "https://github.com/loopsupport/cache_json"
14
- spec.license = "MIT"
13
+ spec.summary = 'Extremely simple Redis caching for any Ruby class'
14
+ spec.description = 'Extremely simple Redis caching for any Ruby class'
15
+ spec.homepage = 'https://github.com/loopsupport/cache_json'
16
+ spec.license = 'MIT'
15
17
 
16
- spec.metadata["homepage_uri"] = spec.homepage
17
- spec.metadata["source_code_uri"] = "https://github.com/loopsupport/cache_json"
18
+ spec.metadata['homepage_uri'] = spec.homepage
19
+ spec.metadata['source_code_uri'] = 'https://github.com/loopsupport/cache_json'
18
20
 
19
21
  # Specify which files should be added to the gem when it is released.
20
22
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
23
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
22
24
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
23
25
  end
24
- spec.bindir = "exe"
26
+ spec.bindir = 'exe'
25
27
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
- spec.require_paths = ["lib"]
28
+ spec.require_paths = ['lib']
27
29
 
28
30
  spec.add_dependency 'redis', '>= 3.3.5', '< 5'
29
- spec.add_development_dependency 'timecop', '~> 0.9.1'
31
+ spec.add_development_dependency 'bundler', '~> 2.0'
30
32
  spec.add_development_dependency 'mock_redis', '~> 0.21.0'
31
- spec.add_development_dependency "bundler", "~> 2.0"
32
- spec.add_development_dependency "rake", "~> 10.0"
33
- spec.add_development_dependency "rspec", "~> 3.0"
33
+ spec.add_development_dependency 'rake', '~> 10.0'
34
+ spec.add_development_dependency 'rspec', '~> 3.0'
35
+ spec.add_development_dependency 'rubocop', '~> 0.72'
36
+ spec.add_development_dependency 'sidekiq', '>= 3.0', '< 7'
37
+ spec.add_development_dependency 'timecop', '~> 0.9.1'
34
38
  end
data/lib/cache_json.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "json"
4
- require "cache_json/version"
5
- require "cache_json/base"
6
- require "cache_json/configure"
3
+ require 'json'
4
+ require 'cache_json/version'
5
+ require 'cache_json/base'
6
+ require 'cache_json/configure'
7
+ require 'cache_json/worker'
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CacheJSON
4
+ module Adapters
5
+ class Redis
6
+ attr_accessor :args
7
+ attr_accessor :options
8
+
9
+ def initialize(args:, options:)
10
+ @args = args
11
+ @options = options
12
+ end
13
+
14
+ def cached_results
15
+ return @cached_results if @cached_results
16
+
17
+ results_json = $redis.get(full_key)
18
+ @cached_results = JSON.parse(results_json) if results_json
19
+ end
20
+
21
+ def cached_results=(results)
22
+ $redis.sadd(class_key, full_key)
23
+ $redis.set(full_key, results.to_json)
24
+ $redis.expire(full_key, options[:time_to_expire].to_i)
25
+ end
26
+
27
+ def clear_cache!
28
+ $redis.smembers(class_key).each do |key|
29
+ $redis.del(key)
30
+ end
31
+ $redis.del(class_key)
32
+ end
33
+
34
+ private
35
+
36
+ def full_key
37
+ @full_key ||= "#{class_key}-#{arguments_signature}"
38
+ end
39
+
40
+ def class_key
41
+ @class_key ||= "CacheJSON-#{options[:class_name]}"
42
+ end
43
+
44
+ def arguments_signature
45
+ args.to_a.map do |row|
46
+ "#{row[0]}:#{row[1]}"
47
+ end.sort.join('-')
48
+ end
49
+ end
50
+ end
51
+ end
@@ -1,89 +1,79 @@
1
- module CacheJSON::Base
2
- def self.included(base)
3
- base.extend(ClassMethods)
4
- end
1
+ # frozen_string_literal: true
5
2
 
6
- def results(args)
7
- options = self.class.cache_json_full_options
8
- cache = Cache.new(args: args, options: options)
9
- if cache.get_cached_results
10
- # Rails.logger.info "Got #{self.class} results from cache" if options[:logging_enabled]
11
- cache.get_cached_results
12
- else
13
- # Rails.logger.info "Generating #{self.class} cached results" if options[:logging_enabled]
14
- results = compute_results(args)
15
- cache.set_cached_results(results)
16
- JSON.parse(results.to_json) # stringify keys
17
- end
18
- end
3
+ require 'cache_json/adapters/redis'
19
4
 
20
- def clear_cache!
21
- Cache.new(options: self.class.cache_json_full_options).clear_cache!
22
- end
5
+ module CacheJSON
6
+ module Base
7
+ def self.included(base)
8
+ base.extend(ClassMethods)
9
+ end
23
10
 
24
- class Cache
25
- attr_accessor :args
26
- attr_accessor :options
11
+ def results(args)
12
+ raise ArgumentError, 'Must use keyword arguments' unless args.is_a?(Hash)
27
13
 
28
- def initialize(args: {}, options:)
29
- @args = args
30
- @options = options
14
+ options = self.class.cache_json_full_options
15
+ cache = Cache.new(args: args, options: options)
16
+ check_cache(args: args, cache: cache) ||
17
+ JSON.parse(refresh_cache!(args: args, cache: cache).to_json) # stringify keys
31
18
  end
32
19
 
33
- def get_cached_results
34
- return @get_cached_results if @get_cached_results
20
+ def clear_cache!
21
+ Cache.new(options: self.class.cache_json_full_options).clear_cache!
22
+ end
35
23
 
36
- results_json = $redis.get(full_key)
37
- if results_json
38
- @get_cached_results = JSON.parse(results_json)
39
- end
24
+ def refresh_cache!(args:, cache: nil)
25
+ cache ||= Cache.new(args: args, options: self.class.cache_json_full_options)
26
+ results = compute_results(args)
27
+ cache.cached_results = results
28
+ results
40
29
  end
41
30
 
42
- def set_cached_results(results)
43
- $redis.set(full_key, results.to_json)
44
- $redis.expire(full_key, options[:time_to_expire].to_i)
31
+ def check_cache(args:, cache: nil)
32
+ cache ||= Cache.new(args: args, options: self.class.cache_json_full_options)
33
+ cache.cached_results
45
34
  end
46
35
 
47
- def clear_cache!
48
- $redis.keys.select do |key|
49
- key.include?(base_key)
50
- end.each do |key|
51
- # Rails.logger.info "Deleting #{self.class} cached results" if options[:logging_enabled]
52
- $redis.del(key)
36
+ class Cache
37
+ attr_accessor :args
38
+ attr_accessor :options
39
+
40
+ def initialize(args: {}, options:)
41
+ @args = args
42
+ @options = options
53
43
  end
54
- end
55
44
 
56
- def full_key
57
- @full_key ||= "#{base_key}-#{arguments_signature}"
58
- end
45
+ def cached_results
46
+ adapter.cached_results
47
+ end
59
48
 
60
- def base_key
61
- @base_key ||= "CacheJSON-#{options[:class_name]}"
62
- end
49
+ def cached_results=(results)
50
+ adapter.cached_results = results
51
+ end
63
52
 
64
- def arguments_signature
65
- args.to_a.sort_by do |row|
66
- row[0]
67
- end.map do |row|
68
- "#{row[0]}:#{row[1]}"
69
- end.join("-")
70
- end
71
- end
53
+ def clear_cache!
54
+ adapter.clear_cache!
55
+ end
72
56
 
73
- module ClassMethods
74
- # Setter
75
- def cache_json_options(options_hash)
76
- cache_json_class_specific_overrides.merge!(options_hash)
57
+ def adapter
58
+ @adapter ||= CacheJSON::Adapters::Redis.new(args: args, options: options)
59
+ end
77
60
  end
78
61
 
79
- # Just the class-specific overrides
80
- def cache_json_class_specific_overrides
81
- @cache_json_class_specific_overrides ||= {}
82
- end
62
+ module ClassMethods
63
+ # Setter
64
+ def cache_json_options(options_hash)
65
+ cache_json_class_specific_overrides.merge!(options_hash)
66
+ end
83
67
 
84
- # Getter (inherits from global defaults)
85
- def cache_json_full_options
86
- CacheJSON::Config.options.merge(cache_json_class_specific_overrides).merge(class_name: self.to_s)
68
+ # Just the class-specific overrides
69
+ def cache_json_class_specific_overrides
70
+ @cache_json_class_specific_overrides ||= {}
71
+ end
72
+
73
+ # Getter (inherits from global defaults)
74
+ def cache_json_full_options
75
+ CacheJSON::Config.options.merge(cache_json_class_specific_overrides).merge(class_name: to_s)
76
+ end
87
77
  end
88
78
  end
89
- end
79
+ end
@@ -1,5 +1,6 @@
1
- module CacheJSON
1
+ # frozen_string_literal: true
2
2
 
3
+ module CacheJSON
3
4
  class Config
4
5
  @time_to_expire = 3600 # 1 hour
5
6
 
@@ -17,4 +18,4 @@ module CacheJSON
17
18
  def self.configure
18
19
  yield(CacheJSON::Config) if block_given?
19
20
  end
20
- end
21
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CacheJSON
2
- VERSION = "1.0.0"
4
+ VERSION = '1.1.0'
3
5
  end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require 'sidekiq'
5
+ rescue LoadError
6
+ nil
7
+ end
8
+
9
+ module CacheJSON
10
+ class Worker
11
+ if defined?(Sidekiq)
12
+ include Sidekiq::Worker
13
+ sidekiq_options queue: 'cache_json'
14
+ else
15
+ def perform_async(*)
16
+ raise 'Sidekiq gem not found. ' \
17
+ 'You can still call this worker manually via CacheJSON::Worker.new.perform'
18
+ end
19
+ end
20
+
21
+ def perform(klass: nil, args: {})
22
+ if klass
23
+ klass.new.refresh_cache!(args: args)
24
+ else
25
+ generate_workers
26
+ end
27
+ end
28
+
29
+ private
30
+
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
37
+ end
38
+
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
43
+ end
44
+ end
45
+
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
+ []
52
+ end
53
+ end
54
+
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)
64
+ end
65
+ end
66
+ end
67
+
68
+ def values_for_key(hsh, key)
69
+ pivot_key_values = hsh[key]
70
+ if pivot_key_values.is_a?(Range) || pivot_key_values.is_a?(Array)
71
+ pivot_key_values
72
+ else
73
+ [pivot_key_values]
74
+ end
75
+ end
76
+
77
+ def should_refresh?(klass, args)
78
+ !klass.new.check_cache(args: args)
79
+ end
80
+ end
81
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cache_json
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul Gut
@@ -31,19 +31,19 @@ dependencies:
31
31
  - !ruby/object:Gem::Version
32
32
  version: '5'
33
33
  - !ruby/object:Gem::Dependency
34
- name: timecop
34
+ name: bundler
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: 0.9.1
39
+ version: '2.0'
40
40
  type: :development
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: 0.9.1
46
+ version: '2.0'
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: mock_redis
49
49
  requirement: !ruby/object:Gem::Requirement
@@ -59,47 +59,81 @@ dependencies:
59
59
  - !ruby/object:Gem::Version
60
60
  version: 0.21.0
61
61
  - !ruby/object:Gem::Dependency
62
- name: bundler
62
+ name: rake
63
63
  requirement: !ruby/object:Gem::Requirement
64
64
  requirements:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
- version: '2.0'
67
+ version: '10.0'
68
68
  type: :development
69
69
  prerelease: false
70
70
  version_requirements: !ruby/object:Gem::Requirement
71
71
  requirements:
72
72
  - - "~>"
73
73
  - !ruby/object:Gem::Version
74
- version: '2.0'
74
+ version: '10.0'
75
75
  - !ruby/object:Gem::Dependency
76
- name: rake
76
+ name: rspec
77
77
  requirement: !ruby/object:Gem::Requirement
78
78
  requirements:
79
79
  - - "~>"
80
80
  - !ruby/object:Gem::Version
81
- version: '10.0'
81
+ version: '3.0'
82
82
  type: :development
83
83
  prerelease: false
84
84
  version_requirements: !ruby/object:Gem::Requirement
85
85
  requirements:
86
86
  - - "~>"
87
87
  - !ruby/object:Gem::Version
88
- version: '10.0'
88
+ version: '3.0'
89
89
  - !ruby/object:Gem::Dependency
90
- name: rspec
90
+ name: rubocop
91
91
  requirement: !ruby/object:Gem::Requirement
92
92
  requirements:
93
93
  - - "~>"
94
94
  - !ruby/object:Gem::Version
95
- version: '3.0'
95
+ version: '0.72'
96
96
  type: :development
97
97
  prerelease: false
98
98
  version_requirements: !ruby/object:Gem::Requirement
99
99
  requirements:
100
100
  - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '0.72'
103
+ - !ruby/object:Gem::Dependency
104
+ name: sidekiq
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
101
108
  - !ruby/object:Gem::Version
102
109
  version: '3.0'
110
+ - - "<"
111
+ - !ruby/object:Gem::Version
112
+ version: '7'
113
+ type: :development
114
+ prerelease: false
115
+ version_requirements: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: '3.0'
120
+ - - "<"
121
+ - !ruby/object:Gem::Version
122
+ version: '7'
123
+ - !ruby/object:Gem::Dependency
124
+ name: timecop
125
+ requirement: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - "~>"
128
+ - !ruby/object:Gem::Version
129
+ version: 0.9.1
130
+ type: :development
131
+ prerelease: false
132
+ version_requirements: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - "~>"
135
+ - !ruby/object:Gem::Version
136
+ version: 0.9.1
103
137
  description: Extremely simple Redis caching for any Ruby class
104
138
  email:
105
139
  - paul@loopsupport.com
@@ -109,6 +143,7 @@ extra_rdoc_files: []
109
143
  files:
110
144
  - ".gitignore"
111
145
  - ".rspec"
146
+ - ".rubocop.yml"
112
147
  - ".travis.yml"
113
148
  - CODE_OF_CONDUCT.md
114
149
  - Gemfile
@@ -120,9 +155,11 @@ files:
120
155
  - bin/setup
121
156
  - cache_json.gemspec
122
157
  - lib/cache_json.rb
158
+ - lib/cache_json/adapters/redis.rb
123
159
  - lib/cache_json/base.rb
124
160
  - lib/cache_json/configure.rb
125
161
  - lib/cache_json/version.rb
162
+ - lib/cache_json/worker.rb
126
163
  homepage: https://github.com/loopsupport/cache_json
127
164
  licenses:
128
165
  - MIT