cache_keeper 0.2.1 → 0.4.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: d50a51737d307549be39ed49fb7edcfe391a270c0b55d25b52e70092276c548c
4
- data.tar.gz: 5f2d9f844f4eb18bd41eb27a51558d4dd4aec13672abaf13cdcf2598cdfe8263
3
+ metadata.gz: 04c55e34ac5e0676e81456a04f7edae71cf0c79c6cd5be9e135c56561f6439be
4
+ data.tar.gz: 6f9d9bc0bd404cd350f1cf7ff48b8c59bbff60dbeeda754c052ca22457720b8a
5
5
  SHA512:
6
- metadata.gz: '0519ed2a41f0c66ffcd0e8911f22d87f7e62b4b0fee2d3c1d1f9f9c567f9ebcebfa196b7253eb89b1534e04f5dbec873c94715ebc39fcb0e7836d4c5927b84d8'
7
- data.tar.gz: 6b341290d842a7c6ec3c04170989428ea52bbfbd5a6f916a649aa0617326fb42766111f4dedbfcf714b86d403d26f3f5b57635c4cc3e9a34630ab130111da278
6
+ metadata.gz: 1eec12fc16cf629cbe1b3502918b67d6d420bb812c1ed88088a809b07faf31ec981dfddb0d094507f0cd9522238134b1bc60ff011f0d99a12339144a5ba0a3d2
7
+ data.tar.gz: 6a93852da524a5e478166bc24d0bee53973a3343a8b55c2574ed67259f3e5ffc7f927b31bd35e257fe0107073e69fc32a99e608614b07ab736fa103e8ce33adc
@@ -4,13 +4,20 @@ jobs:
4
4
  tests:
5
5
  strategy:
6
6
  matrix:
7
- ruby-version:
8
- - "2.7"
9
- - "3.0"
10
7
  rails-version:
11
8
  - "6.1"
12
9
  - "7.0"
10
+ - "7.1"
13
11
  - "main"
12
+ include:
13
+ - rails-version: "6.1"
14
+ ruby-version: "2.5"
15
+ - rails-version: "7.0"
16
+ ruby-version: "2.7"
17
+ - rails-version: "7.1"
18
+ ruby-version: "2.7"
19
+ - rails-version: "main"
20
+ ruby-version: "3.2"
14
21
 
15
22
  env:
16
23
  RAILS_VERSION: "${{ matrix.rails-version }}"
data/.gitignore CHANGED
@@ -1,5 +1,7 @@
1
1
  *.gem
2
+ Gemfile.lock
2
3
  .byebug_history
4
+ TAGS
3
5
 
4
6
  test/dummy/db/*.sqlite3
5
7
  test/dummy/db/*.sqlite3*
data/.rubocop.yml ADDED
@@ -0,0 +1,122 @@
1
+ require:
2
+ - rubocop-rails
3
+ - rubocop-performance
4
+
5
+ AllCops:
6
+ Exclude:
7
+ - '**/db/**/*'
8
+ - '**/config/**/*'
9
+ - '**/tmp/**/*'
10
+ - '**/templates/**/*'
11
+ - '**/vendor/**/*'
12
+ - '**/node_modules/**/*'
13
+ TargetRubyVersion: 3.2.2
14
+
15
+ # Relaxed Ruby Style
16
+ Style/Alias:
17
+ Enabled: false
18
+ StyleGuide: https://relaxed.ruby.style/#stylealias
19
+
20
+ Style/AsciiComments:
21
+ Enabled: false
22
+ StyleGuide: https://relaxed.ruby.style/#styleasciicomments
23
+
24
+ Style/BeginBlock:
25
+ Enabled: false
26
+ StyleGuide: https://relaxed.ruby.style/#stylebeginblock
27
+
28
+ Style/BlockDelimiters:
29
+ Enabled: false
30
+ StyleGuide: https://relaxed.ruby.style/#styleblockdelimiters
31
+
32
+ Style/Documentation:
33
+ Enabled: false
34
+ StyleGuide: https://relaxed.ruby.style/#styledocumentation
35
+
36
+ Style/EndBlock:
37
+ Enabled: false
38
+ StyleGuide: https://relaxed.ruby.style/#styleendblock
39
+
40
+ Style/GuardClause:
41
+ Enabled: false
42
+
43
+ Style/IfUnlessModifier:
44
+ Enabled: false
45
+ StyleGuide: https://relaxed.ruby.style/#styleifunlessmodifier
46
+
47
+ Style/ModuleFunction:
48
+ Enabled: false
49
+ StyleGuide: https://relaxed.ruby.style/#stylemodulefunction
50
+
51
+ Style/MultilineBlockChain:
52
+ Enabled: false
53
+ StyleGuide: https://relaxed.ruby.style/#stylemultilineblockchain
54
+
55
+ Style/NegatedIf:
56
+ Enabled: false
57
+ StyleGuide: https://relaxed.ruby.style/#stylenegatedif
58
+
59
+ Style/NegatedWhile:
60
+ Enabled: false
61
+ StyleGuide: https://relaxed.ruby.style/#stylenegatedwhile
62
+
63
+ Style/NumericPredicate:
64
+ Enabled: false
65
+ StyleGuide: https://relaxed.ruby.style/#stylenumericpredicate
66
+
67
+ Style/PercentLiteralDelimiters:
68
+ Enabled: false
69
+ StyleGuide: https://relaxed.ruby.style/#stylepercentliteraldelimiters
70
+
71
+ Style/WhileUntilModifier:
72
+ Enabled: false
73
+ StyleGuide: https://relaxed.ruby.style/#stylewhileuntilmodifier
74
+
75
+ Style/WordArray:
76
+ Enabled: false
77
+ StyleGuide: https://relaxed.ruby.style/#stylewordarray
78
+
79
+ Lint/AmbiguousRegexpLiteral:
80
+ Enabled: false
81
+ StyleGuide: https://relaxed.ruby.style/#lintambiguousregexpliteral
82
+
83
+ Lint/AssignmentInCondition:
84
+ Enabled: false
85
+ StyleGuide: https://relaxed.ruby.style/#lintassignmentincondition
86
+
87
+ Layout/LineLength:
88
+ Enabled: false
89
+
90
+ Metrics:
91
+ Enabled: false
92
+
93
+ # Custom
94
+ Performance:
95
+ Exclude:
96
+ - '**/spec/**/*'
97
+
98
+ Style/FrozenStringLiteralComment:
99
+ Enabled: false
100
+
101
+ Style/AndOr:
102
+ Enabled: true
103
+ EnforcedStyle: conditionals
104
+
105
+ Style/ClassAndModuleChildren:
106
+ Enabled: false
107
+
108
+ Style/HashSyntax:
109
+ Enabled: false
110
+
111
+ Style/MixinGrouping:
112
+ Enabled: false
113
+
114
+ Style/RegexpLiteral:
115
+ Enabled: false
116
+
117
+ Style/StringConcatenation:
118
+ Enabled: false
119
+
120
+ Style/StringLiterals:
121
+ Enabled: true
122
+ EnforcedStyle: double_quotes
data/Gemfile CHANGED
@@ -2,8 +2,22 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
- gem "rake"
6
- gem "byebug"
5
+ rails_version = ENV.fetch("RAILS_VERSION", "7.1")
7
6
 
8
- gem "rails", ">= 6.1"
7
+ rails_constraint =
8
+ if rails_version == "main"
9
+ { github: "rails/rails" }
10
+ else
11
+ "~> #{rails_version}.0"
12
+ end
13
+
14
+ gem "rails", rails_constraint
9
15
  gem "sqlite3"
16
+
17
+ group :debug do
18
+ gem "byebug"
19
+
20
+ gem "rubocop"
21
+ gem "rubocop-performance"
22
+ gem "rubocop-rails"
23
+ end
data/README.md CHANGED
@@ -29,25 +29,49 @@ bundle add cache_keeper
29
29
  CacheKeeper provides a `caches` method that will cache the result of the methods you give it:
30
30
 
31
31
  ```ruby
32
- caches :slow_method, :really_slow_method, expires_in: 1.hour
33
- caches :incredibly_slow_method, expires_in: 2.hours, must_revalidate: true
32
+ class Recording < ApplicationRecord
33
+ caches :slow_method, :really_slow_method, expires_in: 1.hour, must_revalidate: true
34
+ caches :incredibly_slow_method, expires_in: 2.hours, key: -> { "custom-key/#{id}" }
35
+
36
+ def slow_method
37
+ ...
38
+ end
39
+
40
+ def really_slow_method
41
+ ...
42
+ end
43
+
44
+ def incredibly_slow_method
45
+ ...
46
+ end
47
+ end
34
48
  ```
35
49
 
36
50
  It's automatically available in your ActiveRecord models and in your controllers. You can also use it in any other class by including `CacheKeeper::Caching`.
37
51
 
38
52
  By default, it will immediately run the method call if it hasn't been cached before. The next time it is called, it will return the cached value if it hasn't expired yet. If it has expired, it will enqueue a job to refresh the cache in the background and return the stale value in the meantime. You can avoid returning stale values by setting `must_revalidate: true` in the options.
39
53
 
54
+ CacheKeeper will compose cache keys from the name of the method and the instance's `cache_key` if it's defined or the name of the class otherwise. You can pass a `key` option to customize the cache key if you need it. It accepts [the same values](https://guides.rubyonrails.org/caching_with_rails.html#cache-keys) as `Rails.cache.fetch`, as well as procs or lambdas in case you need access to the instance.
55
+
40
56
  It's important to note that it will only work with methods that don't take any arguments.
41
57
 
42
- ### Autorefresh
58
+ ### Serialization
43
59
 
44
- You can tell CacheKeeper to automatically refresh the cache after a certain amount of time by setting the `autorefresh` option:
60
+ CacheKeeper needs to pass the instance on which the cached method is called along to the refresh job. As any other job argument, ActiveJob requires it to be serializable. ActiveRecord instances are serializable by default, but controllers, POROs and other classes are not. CacheKeeper provides a `serializer` option that will work in most cases:
45
61
 
46
62
  ```ruby
47
- caches :i_cant_even, expires_in: 2.hours, autorefresh: true
63
+ class Example
64
+ # Generate a new instance using an empty initializer (Example.new)
65
+ # Useful for controllers and for POROs with no arguments
66
+ caches :slow_method, serializer: :new_instance
67
+
68
+ # Replicate the old instance using Marshal.dump and Marshal.load
69
+ # Useful in most other cases, but make sure the dump is not too big
70
+ caches :slow_method, serializer: :marshal
71
+ end
48
72
  ```
49
73
 
50
- This works by running a job in cron mode that will periodically check for stale entries and enqueue a job to refresh them. You need to specify an adapter as explained in the configuration section below.
74
+ If those options don't work for you, you can always [write custom serializers](https://guides.rubyonrails.org/active_job_basics.html#serializers) for your classes.
51
75
 
52
76
 
53
77
  ## Configuration
@@ -63,15 +87,6 @@ Rails.application.configure do
63
87
  # The queue to use for the refresh jobs.
64
88
  # Default: nil (uses the default queue)
65
89
  config.cache_keeper.queues.refresh = :low_priority
66
-
67
- # The adapter to use for the autorefresh cron job.
68
- # Options: :good_job
69
- # Default: nil
70
- config.cache_keeper.cron.adapter = :good_job
71
-
72
- # The cron expression to use for the autorefresh cron job.
73
- # Default: "*/15 * * * *" (every 15 minutes)
74
- config.cache_keeper.cron.expression = "0 * * * *"
75
90
  end
76
91
  ```
77
92
 
@@ -3,14 +3,18 @@ class CacheKeeper::BaseJob < ActiveJob::Base
3
3
 
4
4
  private
5
5
 
6
- # Monkey patch ActiveJob::Core#serialize_arguments to use CacheKeeper::WhateverSerializer
7
- # in case there's no serializer for the argument. I'm doing it this way because I don't
6
+ # Monkey patch ActiveJob::Core#serialize_arguments to use our custom serializers
7
+ # in case the `serializer` option is present. I'm doing it this way because I don't
8
8
  # want to register the serializer as it would affect the whole application.
9
9
  def serialize_arguments(arguments)
10
10
  arguments.map do |argument|
11
11
  ActiveJob::Arguments.send :serialize_argument, argument
12
- rescue ActiveJob::SerializationError
13
- CacheKeeper::WhateverSerializer.serialize argument
12
+ rescue ActiveJob::SerializationError => e
13
+ if arguments.first.serialize_target?
14
+ arguments.first.serialize_target argument
15
+ else
16
+ raise e
17
+ end
14
18
  end
15
19
  end
16
20
  end
@@ -1,7 +1,7 @@
1
1
  class CacheKeeper::RefreshJob < CacheKeeper::BaseJob
2
2
  queue_as { CacheKeeper.configuration.queues[:refresh] }
3
3
 
4
- def perform(cached_method, instance)
5
- cached_method.refresh instance
4
+ def perform(cached_method, target)
5
+ cached_method.refresh target
6
6
  end
7
7
  end
@@ -1,13 +1,11 @@
1
1
  module CacheKeeper::CachedMethod::Refreshable
2
- def refresh(instance)
3
- Rails.cache.fetch(cache_key, expires_in: expires_in) do
4
- instance.send alias_for_original_method
2
+ def refresh(target)
3
+ Rails.cache.fetch(cache_key(target), expires_in: expires_in) do
4
+ target.send alias_for_original_method
5
5
  end
6
6
  end
7
7
 
8
- def refresh_later(instance = nil)
9
- instance ||= klass.new
10
-
11
- CacheKeeper::RefreshJob.perform_later self, instance
8
+ def refresh_later(target)
9
+ CacheKeeper::RefreshJob.perform_later self, target
12
10
  end
13
11
  end
@@ -0,0 +1,18 @@
1
+ module CacheKeeper::CachedMethod::SerializableTarget
2
+ def serialize_target?
3
+ options[:serializer].present?
4
+ end
5
+
6
+ def serialize_target(target)
7
+ case options[:serializer]
8
+ when :new_instance
9
+ CacheKeeper::NewInstanceSerializer.serialize target
10
+ when :marshal
11
+ CacheKeeper::MarshalSerializer.serialize target
12
+ else
13
+ raise "Unknown serializer: #{options[:serializer]}"
14
+ end
15
+ rescue StandardError => e
16
+ raise "Error serializing target using #{options[:serializer]}: #{e}"
17
+ end
18
+ end
@@ -1,5 +1,6 @@
1
1
  class CacheKeeper::CachedMethod
2
2
  include Refreshable
3
+ include SerializableTarget
3
4
 
4
5
  attr_accessor :klass, :method_name, :options
5
6
 
@@ -13,18 +14,16 @@ class CacheKeeper::CachedMethod
13
14
  :"__#{method_name}__hooked__"
14
15
  end
15
16
 
16
- def stale?
17
- cache_entry.blank? || cache_entry.expired?
18
- end
17
+ def call(target)
18
+ cache_entry = cache_entry(target)
19
19
 
20
- def call(instance)
21
20
  if cache_entry.blank?
22
- refresh instance
21
+ refresh target
23
22
  elsif cache_entry.expired?
24
23
  if must_revalidate?
25
- refresh instance
24
+ refresh target
26
25
  else
27
- refresh_later instance
26
+ refresh_later target
28
27
 
29
28
  cache_entry.value
30
29
  end
@@ -35,12 +34,16 @@ class CacheKeeper::CachedMethod
35
34
 
36
35
  private
37
36
 
38
- def cache_entry
39
- Rails.cache.send :read_entry, Rails.cache.send(:normalize_key, cache_key, {})
37
+ def cache_entry(target)
38
+ Rails.cache.send :read_entry, Rails.cache.send(:normalize_key, cache_key(target), {})
40
39
  end
41
40
 
42
- def cache_key
43
- ["CacheKeeper", klass, method_name]
41
+ def cache_key(target)
42
+ if options[:key].present?
43
+ options[:key].is_a?(Proc) ? target.instance_exec(&options[:key]) : options[:key]
44
+ else
45
+ target.respond_to?(:cache_key) ? ["CacheKeeper", target, method_name] : ["CacheKeeper", klass, method_name]
46
+ end
44
47
  end
45
48
 
46
49
  def expires_in
@@ -0,0 +1,9 @@
1
+ class CacheKeeper::MarshalSerializer < ActiveJob::Serializers::ObjectSerializer
2
+ def serialize(target)
3
+ super("dump" => Marshal.dump(target).force_encoding("ISO-8859-1").encode("UTF-8"))
4
+ end
5
+
6
+ def deserialize(json)
7
+ Marshal.load(json["dump"].encode("ISO-8859-1").force_encoding("ASCII-8BIT"))
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class CacheKeeper::NewInstanceSerializer < ActiveJob::Serializers::ObjectSerializer
2
+ def serialize(target)
3
+ super("klass" => target.class.to_s)
4
+ end
5
+
6
+ def deserialize(json)
7
+ json["klass"].constantize.new
8
+ end
9
+ end
data/bin/rails CHANGED
@@ -2,12 +2,12 @@
2
2
  # This command will automatically be run when you run "rails" with Rails gems
3
3
  # installed from the root of your application.
4
4
 
5
- ENGINE_ROOT = File.expand_path('..', __dir__)
6
- APP_PATH = File.expand_path('../test/dummy/config/application', __dir__)
5
+ ENGINE_ROOT = File.expand_path("..", __dir__)
6
+ APP_PATH = File.expand_path("../test/dummy/config/application", __dir__)
7
7
 
8
8
  # Set up gems listed in the Gemfile.
9
- ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
10
- require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
9
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
10
+ require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"])
11
11
 
12
- require 'rails/all'
13
- require 'rails/engine/commands'
12
+ require "rails/all"
13
+ require "rails/engine/commands"
@@ -1,8 +1,7 @@
1
1
  module CacheKeeper
2
2
  class Configuration
3
3
  DEFAULT_MUST_REVALIDATE = false
4
- DEFAULT_QUEUES = {}
5
- DEFAULT_CRON_EXPRESSION = "*/15 * * * *" # Every 15 minutes, every day
4
+ DEFAULT_QUEUES = {}.freeze
6
5
 
7
6
  def must_revalidate
8
7
  return rails_config.must_revalidate unless rails_config.must_revalidate.nil?
@@ -14,21 +13,6 @@ module CacheKeeper
14
13
  rails_config.queues || DEFAULT_QUEUES
15
14
  end
16
15
 
17
- def cron_adapter
18
- return if rails_config.cron.adapter.nil?
19
-
20
- case rails_config.cron.adapter
21
- when :good_job
22
- CacheKeeper::Cron::GoodJobAdapter
23
- else
24
- raise "Unknown cron adapter: #{rails_config.cron.adapter}"
25
- end
26
- end
27
-
28
- def cron_expression
29
- rails_config.cron.expression || DEFAULT_CRON_EXPRESSION
30
- end
31
-
32
16
  private
33
17
 
34
18
  def rails_config
@@ -2,30 +2,18 @@ module CacheKeeper
2
2
  class Engine < ::Rails::Engine
3
3
  isolate_namespace CacheKeeper
4
4
 
5
+ config.eager_load_namespaces << CacheKeeper
6
+
5
7
  config.cache_keeper = ActiveSupport::OrderedOptions.new
6
8
  config.cache_keeper.queues = ActiveSupport::OrderedOptions.new
7
- config.cache_keeper.cron = ActiveSupport::OrderedOptions.new
8
-
9
- config.eager_load_namespaces << CacheKeeper
10
- config.autoload_once_paths = %W(
11
- #{root}/app/jobs
12
- #{root}/app/models
13
- #{root}/app/serializers
14
- )
15
9
 
16
- initializer "cache_keeper.active_job_serializer" do |app|
10
+ initializer "cache_keeper.active_job_serializer" do
17
11
  config.to_prepare do
18
12
  Rails.application.config.active_job.custom_serializers << CacheKeeper::CachedMethodSerializer
19
13
  end
20
14
  end
21
15
 
22
- initializer "cache_keeper.cron_adapter" do |app|
23
- config.to_prepare do
24
- CacheKeeper.configuration.cron_adapter&.setup
25
- end
26
- end
27
-
28
- initializer "cache_keeper.caching_methods" do |app|
16
+ initializer "cache_keeper.caching_methods" do
29
17
  ActiveSupport.on_load :action_controller do
30
18
  ActionController::Base.send :include, CacheKeeper::Caching
31
19
  end
@@ -29,7 +29,7 @@ module CacheKeeper
29
29
  end
30
30
 
31
31
  CacheKeeper::ReplaceMethod.replace(cached_method) do
32
- cached_method.call(self)
32
+ instance_variable_get(:"@#{method_name}") || instance_variable_set(:"@#{method_name}", cached_method.call(self))
33
33
  end
34
34
  end
35
35
  end
@@ -1,3 +1,3 @@
1
1
  module CacheKeeper
2
- VERSION = "0.2.1"
2
+ VERSION = "0.4.0"
3
3
  end
data/lib/cache_keeper.rb CHANGED
@@ -8,7 +8,6 @@ module CacheKeeper
8
8
  autoload :Manager
9
9
  autoload :ReplaceMethod
10
10
  autoload :Store
11
- autoload :Cron
12
11
 
13
12
  mattr_accessor :logger, default: ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new($stdout))
14
13
 
@@ -2,8 +2,6 @@ class Recording < ApplicationRecord
2
2
  caches :slow_method, expires_in: 1.hour
3
3
 
4
4
  def slow_method
5
- sleep 2
6
-
7
5
  42
8
6
  end
9
7
 
data/test/engine_test.rb CHANGED
@@ -5,7 +5,11 @@ class CacheKeeper::EngineTest < ActiveSupport::TestCase
5
5
  assert_includes Rails.application.config.active_job.custom_serializers, CacheKeeper::CachedMethodSerializer
6
6
  end
7
7
 
8
- test "doesn't register the ActiveJob serializer for whatever" do
9
- assert_not_includes Rails.application.config.active_job.custom_serializers, CacheKeeper::WhateverSerializer
8
+ test "doesn't register the ActiveJob new_instance serializer" do
9
+ assert_not_includes Rails.application.config.active_job.custom_serializers, CacheKeeper::NewInstanceSerializer
10
+ end
11
+
12
+ test "doesn't register the ActiveJob marshal serializer" do
13
+ assert_not_includes Rails.application.config.active_job.custom_serializers, CacheKeeper::MarshalSerializer
10
14
  end
11
15
  end
@@ -0,0 +1,15 @@
1
+ require "test_helper"
2
+
3
+ class CacheKeeper::RefreshJobTest < ActiveSupport::TestCase
4
+ include ActiveJob::TestHelper
5
+
6
+ test "uses the correct queue" do
7
+ CacheKeeper.configuration.queues[:refresh] = "refresh"
8
+ recording = Recording.create(number: 5)
9
+ cached_method = CacheKeeper.manager.cached_methods.first
10
+
11
+ assert_performed_with(queue: "refresh") do
12
+ CacheKeeper::RefreshJob.perform_later cached_method, recording
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ require "test_helper"
2
+
3
+ class CacheKeeper::CachedMethod::RefreshableTest < ActiveSupport::TestCase
4
+ include ActiveJob::TestHelper
5
+
6
+ test "#refresh_later enqueues a refresh job" do
7
+ recording = Recording.create(number: 5)
8
+ cached_method = CacheKeeper.manager.cached_methods.first
9
+
10
+ assert_enqueued_with(job: CacheKeeper::RefreshJob) do
11
+ cached_method.refresh_later recording
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,24 @@
1
+ require "test_helper"
2
+
3
+ class CacheKeeper::CachedMethod::SerializableTargetTest < ActiveSupport::TestCase
4
+ test "#serialize_target doesn't allow unknown serializers" do
5
+ recording = Recording.new(number: 5)
6
+ cached_method = CacheKeeper::CachedMethod.new(Recording, :another_method, serializer: :unknown_serializer)
7
+
8
+ error = assert_raises RuntimeError do
9
+ cached_method.serialize_target(recording)
10
+ end
11
+
12
+ assert_includes error.message, "Unknown serializer: unknown_serializer"
13
+ end
14
+
15
+ test "#serialize_target raises an error if unable to serialize" do
16
+ cached_method = CacheKeeper::CachedMethod.new(Recording, :another_method, serializer: :marshal)
17
+
18
+ error = assert_raises RuntimeError do
19
+ cached_method.serialize_target(Proc.new {})
20
+ end
21
+
22
+ assert_includes error.message, "Error serializing target using marshal:"
23
+ end
24
+ end
@@ -2,14 +2,14 @@ require "test_helper"
2
2
 
3
3
  class CacheKeeper::CachedMethodTest < ActiveSupport::TestCase
4
4
  test "#call caches the result of the original method" do
5
- recording = Recording.new(number: 5)
5
+ recording = Recording.create(number: 5)
6
6
  cached_method = manager.handle(Recording, :another_method, expires_in: 1.hour)
7
7
  manager.activate_if_handling(Recording, :another_method)
8
8
 
9
9
  result = cached_method.call(recording)
10
10
 
11
11
  assert_equal 5, result
12
- assert cache_has_key? "CacheKeeper/Recording/another_method"
12
+ assert cache_has_key? "CacheKeeper/recordings/#{recording.id}/another_method"
13
13
  end
14
14
 
15
15
  private
@@ -0,0 +1,24 @@
1
+ require "test_helper"
2
+
3
+ class CacheKeeper::MarshalSerializerTest < ActiveSupport::TestCase
4
+ test "serializes the marshal dump" do
5
+ target = Recording.new
6
+ serialized = serializer.serialize(target)
7
+
8
+ assert_equal serialized["dump"], Marshal.dump(target).force_encoding("ISO-8859-1").encode("UTF-8")
9
+ end
10
+
11
+ test "deserializes the marshal dump" do
12
+ target = Recording.new
13
+ serialized = serializer.serialize(target)
14
+ deserialized = serializer.deserialize(serialized)
15
+
16
+ assert_equal Recording, deserialized.class
17
+ end
18
+
19
+ private
20
+
21
+ def serializer
22
+ CacheKeeper::MarshalSerializer
23
+ end
24
+ end
@@ -1,10 +1,6 @@
1
1
  require "test_helper"
2
2
 
3
- class CacheKeeper::WhateverSerializerTest < ActiveSupport::TestCase
4
- test "serializes whatever" do
5
- assert serializer.serialize?(RecordingsController.new)
6
- end
7
-
3
+ class CacheKeeper::NewInstanceSerializerTest < ActiveSupport::TestCase
8
4
  test "serializes the class name" do
9
5
  serialized = serializer.serialize(RecordingsController.new)
10
6
 
@@ -21,6 +17,6 @@ class CacheKeeper::WhateverSerializerTest < ActiveSupport::TestCase
21
17
  private
22
18
 
23
19
  def serializer
24
- CacheKeeper::WhateverSerializer
20
+ CacheKeeper::NewInstanceSerializer
25
21
  end
26
22
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cache_keeper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Zamuner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-24 00:00:00.000000000 Z
11
+ date: 2023-10-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -32,17 +32,18 @@ extra_rdoc_files: []
32
32
  files:
33
33
  - ".github/workflows/ci.yml"
34
34
  - ".gitignore"
35
+ - ".rubocop.yml"
35
36
  - Gemfile
36
- - Gemfile.lock
37
37
  - MIT-LICENSE
38
38
  - README.md
39
39
  - app/jobs/cache_keeper/base_job.rb
40
- - app/jobs/cache_keeper/refresh_all_job.rb
41
40
  - app/jobs/cache_keeper/refresh_job.rb
42
41
  - app/models/cache_keeper/cached_method.rb
43
42
  - app/models/cache_keeper/cached_method/refreshable.rb
43
+ - app/models/cache_keeper/cached_method/serializable_target.rb
44
44
  - app/serializers/cache_keeper/cached_method_serializer.rb
45
- - app/serializers/cache_keeper/whatever_serializer.rb
45
+ - app/serializers/cache_keeper/marshal_serializer.rb
46
+ - app/serializers/cache_keeper/new_instance_serializer.rb
46
47
  - bin/rails
47
48
  - bin/release
48
49
  - bin/test
@@ -50,8 +51,6 @@ files:
50
51
  - lib/cache_keeper.rb
51
52
  - lib/cache_keeper/caching.rb
52
53
  - lib/cache_keeper/configuration.rb
53
- - lib/cache_keeper/cron.rb
54
- - lib/cache_keeper/cron/good_job_adapter.rb
55
54
  - lib/cache_keeper/engine.rb
56
55
  - lib/cache_keeper/manager.rb
57
56
  - lib/cache_keeper/replace_method.rb
@@ -128,10 +127,14 @@ files:
128
127
  - test/dummy/test/test_helper.rb
129
128
  - test/dummy/vendor/.keep
130
129
  - test/engine_test.rb
130
+ - test/jobs/refresh_job_test.rb
131
131
  - test/manager_test.rb
132
+ - test/models/cached_method/refreshable_test.rb
133
+ - test/models/cached_method/serializable_target_test.rb
132
134
  - test/models/cached_method_test.rb
133
135
  - test/serializers/cached_method_serializer_test.rb
134
- - test/serializers/whatever_serializer_test.rb
136
+ - test/serializers/marshal_serializer_test.rb
137
+ - test/serializers/new_instance_serializer_test.rb
135
138
  - test/store_test.rb
136
139
  - test/test_helper.rb
137
140
  homepage: https://github.com/martinzamuner/cache_keeper
@@ -229,9 +232,13 @@ test_files:
229
232
  - test/dummy/test/test_helper.rb
230
233
  - test/dummy/vendor/.keep
231
234
  - test/engine_test.rb
235
+ - test/jobs/refresh_job_test.rb
232
236
  - test/manager_test.rb
237
+ - test/models/cached_method/refreshable_test.rb
238
+ - test/models/cached_method/serializable_target_test.rb
233
239
  - test/models/cached_method_test.rb
234
240
  - test/serializers/cached_method_serializer_test.rb
235
- - test/serializers/whatever_serializer_test.rb
241
+ - test/serializers/marshal_serializer_test.rb
242
+ - test/serializers/new_instance_serializer_test.rb
236
243
  - test/store_test.rb
237
244
  - test/test_helper.rb
data/Gemfile.lock DELETED
@@ -1,199 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- cache_keeper (0.2.1)
5
- rails (>= 6.1.0)
6
-
7
- GEM
8
- remote: https://rubygems.org/
9
- specs:
10
- actioncable (7.1.1)
11
- actionpack (= 7.1.1)
12
- activesupport (= 7.1.1)
13
- nio4r (~> 2.0)
14
- websocket-driver (>= 0.6.1)
15
- zeitwerk (~> 2.6)
16
- actionmailbox (7.1.1)
17
- actionpack (= 7.1.1)
18
- activejob (= 7.1.1)
19
- activerecord (= 7.1.1)
20
- activestorage (= 7.1.1)
21
- activesupport (= 7.1.1)
22
- mail (>= 2.7.1)
23
- net-imap
24
- net-pop
25
- net-smtp
26
- actionmailer (7.1.1)
27
- actionpack (= 7.1.1)
28
- actionview (= 7.1.1)
29
- activejob (= 7.1.1)
30
- activesupport (= 7.1.1)
31
- mail (~> 2.5, >= 2.5.4)
32
- net-imap
33
- net-pop
34
- net-smtp
35
- rails-dom-testing (~> 2.2)
36
- actionpack (7.1.1)
37
- actionview (= 7.1.1)
38
- activesupport (= 7.1.1)
39
- nokogiri (>= 1.8.5)
40
- rack (>= 2.2.4)
41
- rack-session (>= 1.0.1)
42
- rack-test (>= 0.6.3)
43
- rails-dom-testing (~> 2.2)
44
- rails-html-sanitizer (~> 1.6)
45
- actiontext (7.1.1)
46
- actionpack (= 7.1.1)
47
- activerecord (= 7.1.1)
48
- activestorage (= 7.1.1)
49
- activesupport (= 7.1.1)
50
- globalid (>= 0.6.0)
51
- nokogiri (>= 1.8.5)
52
- actionview (7.1.1)
53
- activesupport (= 7.1.1)
54
- builder (~> 3.1)
55
- erubi (~> 1.11)
56
- rails-dom-testing (~> 2.2)
57
- rails-html-sanitizer (~> 1.6)
58
- activejob (7.1.1)
59
- activesupport (= 7.1.1)
60
- globalid (>= 0.3.6)
61
- activemodel (7.1.1)
62
- activesupport (= 7.1.1)
63
- activerecord (7.1.1)
64
- activemodel (= 7.1.1)
65
- activesupport (= 7.1.1)
66
- timeout (>= 0.4.0)
67
- activestorage (7.1.1)
68
- actionpack (= 7.1.1)
69
- activejob (= 7.1.1)
70
- activerecord (= 7.1.1)
71
- activesupport (= 7.1.1)
72
- marcel (~> 1.0)
73
- activesupport (7.1.1)
74
- base64
75
- bigdecimal
76
- concurrent-ruby (~> 1.0, >= 1.0.2)
77
- connection_pool (>= 2.2.5)
78
- drb
79
- i18n (>= 1.6, < 2)
80
- minitest (>= 5.1)
81
- mutex_m
82
- tzinfo (~> 2.0)
83
- base64 (0.1.1)
84
- bigdecimal (3.1.4)
85
- builder (3.2.4)
86
- byebug (11.1.3)
87
- concurrent-ruby (1.2.2)
88
- connection_pool (2.4.1)
89
- crass (1.0.6)
90
- date (3.3.3)
91
- drb (2.1.1)
92
- ruby2_keywords
93
- erubi (1.12.0)
94
- globalid (1.2.1)
95
- activesupport (>= 6.1)
96
- i18n (1.14.1)
97
- concurrent-ruby (~> 1.0)
98
- io-console (0.6.0)
99
- irb (1.8.3)
100
- rdoc
101
- reline (>= 0.3.8)
102
- loofah (2.21.4)
103
- crass (~> 1.0.2)
104
- nokogiri (>= 1.12.0)
105
- mail (2.8.1)
106
- mini_mime (>= 0.1.1)
107
- net-imap
108
- net-pop
109
- net-smtp
110
- marcel (1.0.2)
111
- mini_mime (1.1.5)
112
- minitest (5.20.0)
113
- mutex_m (0.1.2)
114
- net-imap (0.4.2)
115
- date
116
- net-protocol
117
- net-pop (0.1.2)
118
- net-protocol
119
- net-protocol (0.2.1)
120
- timeout
121
- net-smtp (0.4.0)
122
- net-protocol
123
- nio4r (2.5.9)
124
- nokogiri (1.15.4-arm64-darwin)
125
- racc (~> 1.4)
126
- nokogiri (1.15.4-x86_64-linux)
127
- racc (~> 1.4)
128
- psych (5.1.1.1)
129
- stringio
130
- racc (1.7.1)
131
- rack (3.0.8)
132
- rack-session (2.0.0)
133
- rack (>= 3.0.0)
134
- rack-test (2.1.0)
135
- rack (>= 1.3)
136
- rackup (2.1.0)
137
- rack (>= 3)
138
- webrick (~> 1.8)
139
- rails (7.1.1)
140
- actioncable (= 7.1.1)
141
- actionmailbox (= 7.1.1)
142
- actionmailer (= 7.1.1)
143
- actionpack (= 7.1.1)
144
- actiontext (= 7.1.1)
145
- actionview (= 7.1.1)
146
- activejob (= 7.1.1)
147
- activemodel (= 7.1.1)
148
- activerecord (= 7.1.1)
149
- activestorage (= 7.1.1)
150
- activesupport (= 7.1.1)
151
- bundler (>= 1.15.0)
152
- railties (= 7.1.1)
153
- rails-dom-testing (2.2.0)
154
- activesupport (>= 5.0.0)
155
- minitest
156
- nokogiri (>= 1.6)
157
- rails-html-sanitizer (1.6.0)
158
- loofah (~> 2.21)
159
- nokogiri (~> 1.14)
160
- railties (7.1.1)
161
- actionpack (= 7.1.1)
162
- activesupport (= 7.1.1)
163
- irb
164
- rackup (>= 1.0.0)
165
- rake (>= 12.2)
166
- thor (~> 1.0, >= 1.2.2)
167
- zeitwerk (~> 2.6)
168
- rake (13.0.6)
169
- rdoc (6.5.0)
170
- psych (>= 4.0.0)
171
- reline (0.3.9)
172
- io-console (~> 0.5)
173
- ruby2_keywords (0.0.5)
174
- sqlite3 (1.6.7-arm64-darwin)
175
- sqlite3 (1.6.7-x86_64-linux)
176
- stringio (3.0.8)
177
- thor (1.3.0)
178
- timeout (0.4.0)
179
- tzinfo (2.0.6)
180
- concurrent-ruby (~> 1.0)
181
- webrick (1.8.1)
182
- websocket-driver (0.7.6)
183
- websocket-extensions (>= 0.1.0)
184
- websocket-extensions (0.1.5)
185
- zeitwerk (2.6.12)
186
-
187
- PLATFORMS
188
- arm64-darwin-22
189
- x86_64-linux
190
-
191
- DEPENDENCIES
192
- byebug
193
- cache_keeper!
194
- rails (>= 6.1)
195
- rake
196
- sqlite3
197
-
198
- BUNDLED WITH
199
- 2.4.19
@@ -1,11 +0,0 @@
1
- class CacheKeeper::RefreshAllJob < CacheKeeper::BaseJob
2
- queue_as { CacheKeeper.configuration.queues[:refresh] }
3
-
4
- def perform
5
- CacheKeeper.manager.cached_methods.autorefreshed.each do |cached_method|
6
- next unless cached_method.stale?
7
-
8
- cached_method.refresh_later
9
- end
10
- end
11
- end
@@ -1,15 +0,0 @@
1
- class CacheKeeper::WhateverSerializer < ActiveJob::Serializers::ObjectSerializer
2
- def serialize?(argument)
3
- true
4
- end
5
-
6
- def serialize(whatever)
7
- super(
8
- "klass" => whatever.class.to_s
9
- )
10
- end
11
-
12
- def deserialize(hash)
13
- hash["klass"].constantize.new
14
- end
15
- end
@@ -1,10 +0,0 @@
1
- module CacheKeeper
2
- class Cron::GoodJobAdapter
3
- def self.setup
4
- Rails.application.config.good_job.cron[:cache_keeper] = {
5
- class: "CacheKeeper::RefreshAllJob",
6
- cron: CacheKeeper.configuration.cron_expression
7
- }
8
- end
9
- end
10
- end
@@ -1,7 +0,0 @@
1
- module CacheKeeper
2
- module Cron
3
- extend ActiveSupport::Autoload
4
-
5
- autoload :GoodJobAdapter
6
- end
7
- end