infopark_component_cache 1.1.0 → 2.0.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 +7 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/README.md +8 -0
- data/Rakefile +17 -9
- data/app/helpers/infopark_component_cache_helper.rb +1 -7
- data/infopark_component_cache.gemspec +6 -3
- data/lib/engine.rb +35 -1
- data/lib/infopark_component_cache.rb +0 -14
- data/lib/infopark_component_cache/abstract_cache_storage.rb +40 -0
- data/lib/infopark_component_cache/cache_storage.rb +10 -24
- data/lib/infopark_component_cache/component.rb +1 -3
- data/lib/infopark_component_cache/component_cache.rb +9 -12
- data/lib/infopark_component_cache/consistency_guard.rb +3 -4
- data/lib/infopark_component_cache/delayed_guard.rb +65 -0
- data/lib/infopark_component_cache/guards/always_consistent.rb +1 -1
- data/lib/infopark_component_cache/guards/cms_state_guard.rb +32 -0
- data/lib/infopark_component_cache/guards/delayed_last_changed.rb +18 -0
- data/lib/infopark_component_cache/guards/delayed_obj_count.rb +18 -0
- data/lib/infopark_component_cache/guards/delayed_valid_from.rb +18 -0
- data/lib/infopark_component_cache/guards/delayed_valid_until.rb +18 -0
- data/lib/infopark_component_cache/guards/last_changed.rb +3 -3
- data/lib/infopark_component_cache/guards/never_consistent.rb +1 -1
- data/lib/infopark_component_cache/guards/obj_count.rb +3 -3
- data/lib/infopark_component_cache/guards/valid_from.rb +3 -3
- data/lib/infopark_component_cache/guards/valid_until.rb +3 -3
- data/lib/infopark_component_cache/guards/value_present.rb +1 -1
- data/lib/infopark_component_cache/version.rb +1 -1
- data/lib/infopark_component_cache/volatile_cache.rb +13 -0
- data/lib/infopark_component_cache/volatile_cache_storage.rb +18 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/controllers/application_controller.rb +2 -0
- data/spec/dummy/app/models/obj.rb +2 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +19 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +11 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/test.rb +10 -0
- data/spec/dummy/config/initializers/secret_token.rb +1 -0
- data/spec/dummy/config/initializers/session_store.rb +1 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +8 -0
- data/spec/dummy/config/routes.rb +58 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/schema.rb +3 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/lib/delayed_guard_spec.rb +72 -0
- data/spec/lib/guards/always_consistent_spec.rb +21 -0
- data/spec/lib/guards/last_changed_spec.rb +66 -0
- data/spec/lib/guards/never_consistent_spec.rb +21 -0
- data/spec/lib/guards/obj_count_spec.rb +64 -0
- data/spec/lib/guards/valid_from_spec.rb +66 -0
- data/spec/lib/guards/valid_until_spec.rb +73 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/support/cache_switching_macros.rb +29 -0
- metadata +147 -79
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4c9951c7ea10085ae1cd14375a86ca566f3fcad1
|
4
|
+
data.tar.gz: adad2fd6542596562b972efd2dd8796f040cec53
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4ddbcdc5cd1d85e677ddf264e2044b82ad59d899eb9ecc7d6b55310de7ec23922151dfbe1fce2f27afb79d093cc8a1f4f3d9063d69b7c9373244030c097d56c4
|
7
|
+
data.tar.gz: 95256ba30f389cdb5d4984a67a231f68cd1a79bbb97839bec1250103cb03418b5485cf49c6efcec960fd9e456a458223ae0f2eda694f6ca8de205ad6116c5b1f
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.1.6
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -33,6 +33,14 @@ Alternatively you could use component cache directly:
|
|
33
33
|
This content will be cached
|
34
34
|
<% end %>
|
35
35
|
|
36
|
+
Most guard also provide an option to limit the lookup to specific object classes, for example:
|
37
|
+
|
38
|
+
<%= cache_tagged_component(@gallery, 'gallery', {}, [{guard: InfoparkComponentCache::ObjCount, obj_classes: ['Image']}]) do %>
|
39
|
+
This content will be cached
|
40
|
+
<% end %>
|
41
|
+
|
42
|
+
will only get invalidated when the number of objects of the class 'Image' changes in the CMS.
|
43
|
+
|
36
44
|
## Contributing
|
37
45
|
|
38
46
|
1. Fork it
|
data/Rakefile
CHANGED
@@ -1,11 +1,19 @@
|
|
1
|
-
|
2
|
-
require 'rubygems'
|
3
|
-
require 'bundler/setup'
|
4
|
-
require 'rspec/core/rake_task'
|
1
|
+
#!/usr/bin/env rake
|
5
2
|
|
6
|
-
|
7
|
-
|
3
|
+
begin
|
4
|
+
require 'bundler/setup'
|
5
|
+
rescue LoadError
|
6
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
7
|
+
end
|
8
|
+
|
9
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
10
|
+
|
11
|
+
#load 'rails/tasks/engine.rake'
|
8
12
|
|
9
|
-
|
10
|
-
|
11
|
-
|
13
|
+
Bundler::GemHelper.install_tasks
|
14
|
+
|
15
|
+
require 'rspec/core'
|
16
|
+
require 'rspec/core/rake_task'
|
17
|
+
desc "Run all specs in spec directory (excluding plugin specs)"
|
18
|
+
RSpec::Core::RakeTask.new(:spec)
|
19
|
+
task :default => :spec
|
@@ -4,10 +4,4 @@ module InfoparkComponentCacheHelper
|
|
4
4
|
capture(&block)
|
5
5
|
end
|
6
6
|
end
|
7
|
-
|
8
|
-
def cache_guarded_component(obj, component, guards, params, &block)
|
9
|
-
InfoparkComponentCache::ComponentCache.new(obj, component, params, guards).fetch do
|
10
|
-
capture(&block)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
7
|
+
end
|
@@ -17,8 +17,11 @@ Gem::Specification.new do |gem|
|
|
17
17
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
18
|
gem.require_paths = ["lib"]
|
19
19
|
|
20
|
-
gem.add_dependency 'rails'
|
20
|
+
gem.add_dependency 'rails', '> 3.0.0'
|
21
21
|
|
22
|
-
gem.add_dependency '
|
23
|
-
|
22
|
+
gem.add_dependency 'infopark_fiona_connector'
|
23
|
+
|
24
|
+
gem.add_development_dependency 'rspec-rails'
|
25
|
+
gem.add_development_dependency 'sqlite3'
|
26
|
+
gem.add_development_dependency 'byebug'
|
24
27
|
end
|
data/lib/engine.rb
CHANGED
@@ -1,5 +1,34 @@
|
|
1
|
+
require "infopark_fiona_connector"
|
2
|
+
require "active_support/core_ext/numeric/time"
|
3
|
+
|
4
|
+
#require "infopark_component_cache/guards/always_consistent"
|
5
|
+
#require "infopark_component_cache/guards/never_consistent"
|
6
|
+
require "infopark_component_cache/guards/value_present"
|
7
|
+
require "infopark_component_cache/guards/cms_state_guard"
|
8
|
+
|
9
|
+
require "infopark_component_cache/guards/last_changed"
|
10
|
+
require "infopark_component_cache/guards/obj_count"
|
11
|
+
require "infopark_component_cache/guards/valid_from"
|
12
|
+
require "infopark_component_cache/guards/valid_until"
|
13
|
+
|
14
|
+
require "infopark_component_cache/guards/delayed_last_changed"
|
15
|
+
require "infopark_component_cache/guards/delayed_obj_count"
|
16
|
+
require "infopark_component_cache/guards/delayed_valid_from"
|
17
|
+
require "infopark_component_cache/guards/delayed_valid_until"
|
18
|
+
|
19
|
+
require "infopark_component_cache/key_generator"
|
20
|
+
require "infopark_component_cache/consistency_guard"
|
21
|
+
require "infopark_component_cache/cache_storage"
|
22
|
+
require "infopark_component_cache/component"
|
23
|
+
require "infopark_component_cache/component_cache"
|
24
|
+
|
1
25
|
module InfoparkComponentCache
|
26
|
+
# @!scope class
|
27
|
+
# This parameter should be initialized to the root object
|
28
|
+
# class of the project. It defaults to "::Obj".
|
29
|
+
mattr_accessor :obj_root_class
|
2
30
|
|
31
|
+
# @author Tomasz Przedmojski <tomasz.przedmojski@infopark.de>
|
3
32
|
class Engine < Rails::Engine
|
4
33
|
|
5
34
|
initializer "component_cache.helpers" do
|
@@ -9,6 +38,11 @@ module InfoparkComponentCache
|
|
9
38
|
ActionView::Base.__send__( :include, helper)
|
10
39
|
end
|
11
40
|
end
|
12
|
-
end
|
13
41
|
|
42
|
+
initializer "set default obj_root_class" do
|
43
|
+
InfoparkComponentCache.obj_root_class ||= ::Obj
|
44
|
+
CmsStateGuard.obj_root_class = InfoparkComponentCache.obj_root_class
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
14
48
|
end
|
@@ -1,19 +1,5 @@
|
|
1
1
|
require "infopark_component_cache/version"
|
2
2
|
|
3
|
-
#require "infopark_component_cache/guards/always_consistent"
|
4
|
-
#require "infopark_component_cache/guards/never_consistent"
|
5
|
-
require "infopark_component_cache/guards/value_present"
|
6
|
-
require "infopark_component_cache/guards/last_changed"
|
7
|
-
require "infopark_component_cache/guards/obj_count"
|
8
|
-
require "infopark_component_cache/guards/valid_from"
|
9
|
-
require "infopark_component_cache/guards/valid_until"
|
10
|
-
|
11
|
-
require "infopark_component_cache/key_generator"
|
12
|
-
require "infopark_component_cache/consistency_guard"
|
13
|
-
require "infopark_component_cache/cache_storage"
|
14
|
-
require "infopark_component_cache/component"
|
15
|
-
require "infopark_component_cache/component_cache"
|
16
|
-
|
17
3
|
module InfoparkComponentCache
|
18
4
|
|
19
5
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module InfoparkComponentCache
|
4
|
+
# @author Tomasz Przedmojski <tomasz.przedmojski@infopark.de>
|
5
|
+
#
|
6
|
+
# This abstract base class represents a very thin wrapper
|
7
|
+
# around the underlying cache storage.
|
8
|
+
#
|
9
|
+
# @note Any valid implementation *must* respect
|
10
|
+
# Rails.application.config.action_controller.perform_caching
|
11
|
+
# setting.
|
12
|
+
class AbstractCacheStorage
|
13
|
+
include Singleton
|
14
|
+
|
15
|
+
def exist?(key)
|
16
|
+
enabled? && backing_storage.exist?(key)
|
17
|
+
end
|
18
|
+
|
19
|
+
def read(key)
|
20
|
+
# it is possible to read disabled cache!
|
21
|
+
backing_storage.read(key)
|
22
|
+
end
|
23
|
+
|
24
|
+
def write(key, value, options={})
|
25
|
+
backing_storage.write(key, value, options) if enabled?
|
26
|
+
rescue Errno::ENOSPC => e
|
27
|
+
Rails.logger.error("Unable to write cache, cache full: #{e.message}")
|
28
|
+
end
|
29
|
+
|
30
|
+
# @private
|
31
|
+
def enabled?
|
32
|
+
Rails.application.config.action_controller.perform_caching
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
def backing_storage
|
37
|
+
raise TypeError, "Cannot use abstract cache storage. Please provide a concrete cache storage in #{self.class.name}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -1,30 +1,16 @@
|
|
1
|
-
require
|
1
|
+
require "infopark_component_cache/abstract_cache_storage"
|
2
2
|
|
3
3
|
module InfoparkComponentCache
|
4
4
|
# @author Tomasz Przedmojski <tomasz.przedmojski@infopark.de>
|
5
5
|
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
class CacheStorage
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
enabled? && Rails.cache.exist?(key)
|
15
|
-
end
|
16
|
-
|
17
|
-
def read(key)
|
18
|
-
Rails.cache.read(key) # you can still read the cache if you wish...
|
19
|
-
end
|
20
|
-
|
21
|
-
def write(key, value)
|
22
|
-
Rails.cache.write(key, value) if enabled?
|
23
|
-
end
|
24
|
-
|
25
|
-
# @private
|
26
|
-
def enabled?
|
27
|
-
Rails.application.config.action_controller.perform_caching
|
6
|
+
# This class implements a persistent cache storage, that
|
7
|
+
# it a cache which will probably persists between requests.
|
8
|
+
# In the current version it relies on the preconfigured
|
9
|
+
# Rails cache (Rails.application.config.cache_store).
|
10
|
+
class CacheStorage < AbstractCacheStorage
|
11
|
+
protected
|
12
|
+
def backing_storage
|
13
|
+
Rails.cache
|
28
14
|
end
|
29
15
|
end
|
30
|
-
end
|
16
|
+
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'infopark_component_cache/key_generator'
|
2
|
-
|
3
1
|
module InfoparkComponentCache
|
4
2
|
# @author Tomasz Przedmojski <tomasz.przedmojski@infopark.de>
|
5
3
|
#
|
@@ -25,4 +23,4 @@ module InfoparkComponentCache
|
|
25
23
|
@params.merge({:obj_name=>@obj.name, :obj_id=>@obj.id, :obj_component=>@component})
|
26
24
|
end
|
27
25
|
end
|
28
|
-
end
|
26
|
+
end
|
@@ -1,12 +1,3 @@
|
|
1
|
-
require 'infopark_component_cache/component'
|
2
|
-
require 'infopark_component_cache/cache_storage'
|
3
|
-
|
4
|
-
require 'infopark_component_cache/guards/value_present'
|
5
|
-
require 'infopark_component_cache/guards/last_changed'
|
6
|
-
require 'infopark_component_cache/guards/obj_count'
|
7
|
-
require 'infopark_component_cache/guards/valid_from'
|
8
|
-
require 'infopark_component_cache/guards/valid_until'
|
9
|
-
|
10
1
|
module InfoparkComponentCache
|
11
2
|
# @author Tomasz Przedmojski <tomasz.przedmojski@infopark.de>
|
12
3
|
# This class provides user-level access to component cache.
|
@@ -27,7 +18,7 @@ module InfoparkComponentCache
|
|
27
18
|
# the component
|
28
19
|
# @see Component
|
29
20
|
#
|
30
|
-
# @param [Array<Class>] guards list of guard
|
21
|
+
# @param [Array<Class>, Array<Hash>] guards list of guard
|
31
22
|
# classes used when deciding whether cache is valid
|
32
23
|
# when left empty the default set is used:
|
33
24
|
# @see Guard::ValuePresent
|
@@ -44,8 +35,14 @@ module InfoparkComponentCache
|
|
44
35
|
Guards::ValidUntil.new(@component)
|
45
36
|
]
|
46
37
|
else
|
47
|
-
@guards = guards.map do |
|
48
|
-
|
38
|
+
@guards = guards.map do |klass_or_hash|
|
39
|
+
if klass_or_hash.kind_of?(Hash)
|
40
|
+
klass = klass_or_hash.delete(:guard)
|
41
|
+
klass.new(@component, klass_or_hash)
|
42
|
+
else
|
43
|
+
klass = klass_or_hash
|
44
|
+
klass.new(@component)
|
45
|
+
end
|
49
46
|
end
|
50
47
|
end
|
51
48
|
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'infopark_component_cache/cache_storage'
|
2
|
-
|
3
1
|
module InfoparkComponentCache
|
4
2
|
# @author Tomasz Przedmojski <tomasz.przedmojski@infopark.de>
|
5
3
|
#
|
@@ -15,10 +13,11 @@ module InfoparkComponentCache
|
|
15
13
|
# This consistency is crucial for the function of cache, because
|
16
14
|
# inconsistent cache is (automatically) invalidated.
|
17
15
|
class ConsistencyGuard
|
18
|
-
attr_reader :component
|
16
|
+
attr_reader :component, :options
|
19
17
|
|
20
|
-
def initialize(component)
|
18
|
+
def initialize(component, options={})
|
21
19
|
@component = component
|
20
|
+
@options = options
|
22
21
|
end
|
23
22
|
|
24
23
|
# @return [CacheStorage] instance of CacheStorage
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require "infopark_component_cache/volatile_cache"
|
2
|
+
require "infopark_component_cache/key_generator"
|
3
|
+
|
4
|
+
module InfoparkComponentCache
|
5
|
+
# @author Tomasz Przedmojski <tomasz.przedmojski@infopark.de>
|
6
|
+
#
|
7
|
+
# This module provides an easy way to create delayed guards
|
8
|
+
# by using meta programming.
|
9
|
+
#
|
10
|
+
# @example Delaying an ObjectCount which guards on :object_count
|
11
|
+
# class DelayedObjectCount < ObjectCount
|
12
|
+
# include DelayedGuard
|
13
|
+
# delay :object_count, for: 10.minutes
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# @note Including classes must implement options method which
|
17
|
+
# completely described the object state and returns a hash.
|
18
|
+
module DelayedGuard
|
19
|
+
def self.included(base)
|
20
|
+
base.send(:extend, ClassMethods)
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
include VolatileCache
|
25
|
+
|
26
|
+
def delayed_value(method_name)
|
27
|
+
volatile_cache.read(delayed_value_key(method_name))
|
28
|
+
end
|
29
|
+
|
30
|
+
def still_delayed?(method_name)
|
31
|
+
volatile_cache.exist?(delayed_value_key(method_name))
|
32
|
+
end
|
33
|
+
|
34
|
+
def delay_value(method_name, value, expiration)
|
35
|
+
volatile_cache.write(delayed_value_key(method_name), value, expires_in: expiration)
|
36
|
+
value
|
37
|
+
end
|
38
|
+
|
39
|
+
def delayed_value_key(method_name)
|
40
|
+
KeyGenerator.generate_key(self.options.merge({
|
41
|
+
delayed_method_name: method_name,
|
42
|
+
delayed_class_name: self.class.name
|
43
|
+
}))
|
44
|
+
end
|
45
|
+
|
46
|
+
module ClassMethods
|
47
|
+
def delay(method_name, options)
|
48
|
+
expiration = options[:for]
|
49
|
+
return unless expiration
|
50
|
+
|
51
|
+
# allow to overwrite the delays
|
52
|
+
alias_method :"delayed_#{method_name}", method_name unless method_defined?(:"delayed_#{method_name}")
|
53
|
+
define_method method_name do
|
54
|
+
value = delayed_value(method_name)
|
55
|
+
if still_delayed?(method_name)
|
56
|
+
value
|
57
|
+
else
|
58
|
+
delay_value(method_name, self.send(:"delayed_#{method_name}"), expiration)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "infopark_component_cache/consistency_guard"
|
2
|
+
|
3
|
+
module InfoparkComponentCache
|
4
|
+
# @author Tomasz Przedmojski <tomasz.przedmojski@infopark.de>
|
5
|
+
#
|
6
|
+
# @abstract
|
7
|
+
# This abstract class enables the implementing classes
|
8
|
+
# to access the cms and read the current state.
|
9
|
+
#
|
10
|
+
# It also provides the option to limit the object classes
|
11
|
+
# used for lookup.
|
12
|
+
class CmsStateGuard < ConsistencyGuard
|
13
|
+
class << self
|
14
|
+
# This parameter should be initialized to the root Obj
|
15
|
+
# class of the project
|
16
|
+
attr_accessor :obj_root_class
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
# This method implements scoping on the root Obj class
|
21
|
+
# and additional constraints for limiting object classes
|
22
|
+
# used for lookup
|
23
|
+
def scoped_relation
|
24
|
+
if self.options[:obj_classes].present?
|
25
|
+
conditions = {obj_class: self.options[:obj_classes]}
|
26
|
+
else
|
27
|
+
conditions = {}
|
28
|
+
end
|
29
|
+
CmsStateGuard.obj_root_class.scoped.where(conditions)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "infopark_component_cache/delayed_guard"
|
2
|
+
require "infopark_component_cache/guards/last_changed"
|
3
|
+
|
4
|
+
module InfoparkComponentCache
|
5
|
+
module Guards
|
6
|
+
# @author Tomasz Przedmojski <tomasz.przedmojski@infopark.de>
|
7
|
+
#
|
8
|
+
# This class is a delayed version of LastChanged guard.
|
9
|
+
# The preconfigured delay is 10 seconds.
|
10
|
+
#
|
11
|
+
# @see DelayedGuard
|
12
|
+
# @see LastChanged
|
13
|
+
class DelayedLastChanged < LastChanged
|
14
|
+
include DelayedGuard
|
15
|
+
delay :current_last_changed, for: 10.seconds
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|