cache_json 1.0.0 → 1.1.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
  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