infopark_component_cache 2.0.0 → 4.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 +4 -4
- data/.gitignore +1 -1
- data/.rubocop.yml +25 -0
- data/.rubocop_todo.yml +115 -0
- data/.ruby-version +1 -1
- data/.travis.yml +24 -0
- data/Gemfile +2 -4
- data/Gemfile.lock +206 -0
- data/Gemfile.rails51 +6 -0
- data/Gemfile.rails51.lock +206 -0
- data/README.md +8 -0
- data/Rakefile +7 -7
- data/app/helpers/infopark_component_cache_helper.rb +2 -2
- data/infopark_component_cache.gemspec +15 -12
- data/lib/engine.rb +6 -7
- data/lib/infopark_component_cache.rb +1 -2
- data/lib/infopark_component_cache/abstract_cache_storage.rb +4 -3
- data/lib/infopark_component_cache/cache_storage.rb +1 -0
- data/lib/infopark_component_cache/component.rb +14 -6
- data/lib/infopark_component_cache/component_cache.rb +35 -31
- data/lib/infopark_component_cache/consistency_guard.rb +1 -1
- data/lib/infopark_component_cache/delayed_guard.rb +7 -7
- data/lib/infopark_component_cache/guards/cms_state_guard.rb +7 -6
- data/lib/infopark_component_cache/guards/last_changed.rb +5 -2
- data/lib/infopark_component_cache/guards/obj_count.rb +3 -3
- data/lib/infopark_component_cache/guards/valid_from.rb +7 -10
- data/lib/infopark_component_cache/guards/valid_until.rb +7 -9
- data/lib/infopark_component_cache/key_generator.rb +3 -3
- data/lib/infopark_component_cache/version.rb +1 -1
- data/lib/infopark_component_cache/volatile_cache.rb +1 -1
- data/lib/infopark_component_cache/volatile_cache_storage.rb +1 -0
- data/spec/dummy/Rakefile +1 -1
- data/spec/dummy/config.ru +1 -1
- data/spec/dummy/config/application.rb +1 -4
- data/spec/dummy/config/boot.rb +5 -5
- data/spec/dummy/config/environment.rb +1 -1
- data/spec/dummy/config/environments/development.rb +1 -35
- data/spec/dummy/config/environments/test.rb +2 -8
- data/spec/dummy/config/initializers/secret_token.rb +1 -1
- data/spec/dummy/config/initializers/session_store.rb +1 -1
- data/spec/dummy/db/schema.rb +1 -2
- data/spec/lib/infopark_component_cache/component_cache_spec.rb +118 -0
- data/spec/lib/{delayed_guard_spec.rb → infopark_component_cache/delayed_guard_spec.rb} +3 -3
- data/spec/lib/{guards → infopark_component_cache/guards}/always_consistent_spec.rb +7 -6
- data/spec/lib/{guards → infopark_component_cache/guards}/last_changed_spec.rb +20 -12
- data/spec/lib/{guards → infopark_component_cache/guards}/never_consistent_spec.rb +7 -6
- data/spec/lib/{guards → infopark_component_cache/guards}/obj_count_spec.rb +20 -12
- data/spec/lib/{guards → infopark_component_cache/guards}/valid_from_spec.rb +20 -12
- data/spec/lib/{guards → infopark_component_cache/guards}/valid_until_spec.rb +23 -14
- data/spec/spec_helper.rb +10 -7
- data/spec/support/cache_switching_macros.rb +4 -4
- metadata +81 -30
data/README.md
CHANGED
@@ -16,6 +16,14 @@ Or install it yourself as:
|
|
16
16
|
|
17
17
|
$ gem install infopark_component_cache
|
18
18
|
|
19
|
+
## Tests
|
20
|
+
|
21
|
+
To run the test suite just execute:
|
22
|
+
|
23
|
+
$ bundle exec rspec spec/
|
24
|
+
|
25
|
+
No setup neccessary.
|
26
|
+
|
19
27
|
## Usage
|
20
28
|
|
21
29
|
Set up Rails catching (see [http://guides.rubyonrails.org/caching_with_rails.html](http://guides.rubyonrails.org/caching_with_rails.html)) and
|
data/Rakefile
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
#!/usr/bin/env rake
|
2
2
|
|
3
3
|
begin
|
4
|
-
require
|
4
|
+
require "bundler/setup"
|
5
5
|
rescue LoadError
|
6
|
-
puts
|
6
|
+
puts "You must `gem install bundler` and `bundle install` to run rake tasks"
|
7
7
|
end
|
8
8
|
|
9
|
-
APP_RAKEFILE = File.expand_path("
|
9
|
+
APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
|
10
10
|
|
11
|
-
#load 'rails/tasks/engine.rake'
|
11
|
+
# load 'rails/tasks/engine.rake'
|
12
12
|
|
13
13
|
Bundler::GemHelper.install_tasks
|
14
14
|
|
15
|
-
require
|
16
|
-
require
|
15
|
+
require "rspec/core"
|
16
|
+
require "rspec/core/rake_task"
|
17
17
|
desc "Run all specs in spec directory (excluding plugin specs)"
|
18
18
|
RSpec::Core::RakeTask.new(:spec)
|
19
|
-
task :
|
19
|
+
task default: :spec
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module InfoparkComponentCacheHelper
|
2
|
-
def cache_tagged_component(obj, component, params={}, &block)
|
2
|
+
def cache_tagged_component(obj, component, params = {}, &block)
|
3
3
|
InfoparkComponentCache::ComponentCache.new(obj, component, params).fetch do
|
4
4
|
capture(&block)
|
5
5
|
end
|
6
6
|
end
|
7
|
-
end
|
7
|
+
end
|
@@ -1,27 +1,30 @@
|
|
1
|
-
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
1
|
+
lib = File.expand_path("lib", __dir__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
3
|
+
require "infopark_component_cache/version"
|
5
4
|
|
6
5
|
Gem::Specification.new do |gem|
|
7
6
|
gem.name = "infopark_component_cache"
|
8
7
|
gem.version = InfoparkComponentCache::VERSION
|
9
|
-
gem.authors = ["Tomasz Przedmojski"]
|
8
|
+
gem.authors = ["Tomasz Przedmojski, sveninfo"]
|
10
9
|
gem.email = ["tomasz.przedmojski@infopark.de"]
|
11
|
-
gem.description =
|
12
|
-
gem.summary =
|
10
|
+
gem.description = "Easy and intelligent fragment caching for RailsConnector projects"
|
11
|
+
gem.summary = "Fragment caching with automatic dependency resolution and cache invalidation."
|
13
12
|
gem.homepage = ""
|
13
|
+
gem.license = "Nonstandard"
|
14
14
|
|
15
15
|
gem.files = `git ls-files`.split($/)
|
16
|
-
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
17
17
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
18
|
gem.require_paths = ["lib"]
|
19
19
|
|
20
|
-
gem.add_dependency
|
20
|
+
gem.add_dependency "rails", "~> 5.0"
|
21
21
|
|
22
|
-
gem.add_dependency
|
22
|
+
gem.add_dependency "infopark_fiona_connector", "~> 7.0.1.5.2.3.rc5"
|
23
23
|
|
24
|
-
gem.add_development_dependency
|
25
|
-
gem.add_development_dependency
|
26
|
-
gem.add_development_dependency
|
24
|
+
gem.add_development_dependency "pry-byebug"
|
25
|
+
gem.add_development_dependency "rspec-rails", "~> 3.5"
|
26
|
+
gem.add_development_dependency "rubocop", "~> 0.87.1"
|
27
|
+
gem.add_development_dependency "rubocop-performance"
|
28
|
+
gem.add_development_dependency "rubocop-rspec"
|
29
|
+
gem.add_development_dependency "sqlite3", "~> 1.3.6"
|
27
30
|
end
|
data/lib/engine.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
-
require "
|
1
|
+
require "rails_connector/core_extensions.rb"
|
2
|
+
require "rails_connector/date_attribute.rb"
|
2
3
|
require "active_support/core_ext/numeric/time"
|
3
4
|
|
4
|
-
#require "infopark_component_cache/guards/always_consistent"
|
5
|
-
#require "infopark_component_cache/guards/never_consistent"
|
5
|
+
# require "infopark_component_cache/guards/always_consistent"
|
6
|
+
# require "infopark_component_cache/guards/never_consistent"
|
6
7
|
require "infopark_component_cache/guards/value_present"
|
7
8
|
require "infopark_component_cache/guards/cms_state_guard"
|
8
9
|
|
@@ -30,19 +31,17 @@ module InfoparkComponentCache
|
|
30
31
|
|
31
32
|
# @author Tomasz Przedmojski <tomasz.przedmojski@infopark.de>
|
32
33
|
class Engine < Rails::Engine
|
33
|
-
|
34
34
|
initializer "component_cache.helpers" do
|
35
35
|
[
|
36
36
|
InfoparkComponentCacheHelper
|
37
37
|
].each do |helper|
|
38
|
-
ActionView::Base.__send__(
|
38
|
+
ActionView::Base.__send__(:include, helper)
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
42
|
initializer "set default obj_root_class" do
|
43
43
|
InfoparkComponentCache.obj_root_class ||= ::Obj
|
44
|
-
CmsStateGuard.obj_root_class = InfoparkComponentCache.obj_root_class
|
44
|
+
CmsStateGuard.obj_root_class = InfoparkComponentCache.obj_root_class
|
45
45
|
end
|
46
|
-
|
47
46
|
end
|
48
47
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "singleton"
|
2
2
|
|
3
3
|
module InfoparkComponentCache
|
4
4
|
# @author Tomasz Przedmojski <tomasz.przedmojski@infopark.de>
|
@@ -18,10 +18,10 @@ module InfoparkComponentCache
|
|
18
18
|
|
19
19
|
def read(key)
|
20
20
|
# it is possible to read disabled cache!
|
21
|
-
backing_storage.read(key)
|
21
|
+
backing_storage.read(key)
|
22
22
|
end
|
23
23
|
|
24
|
-
def write(key, value, options={})
|
24
|
+
def write(key, value, options = {})
|
25
25
|
backing_storage.write(key, value, options) if enabled?
|
26
26
|
rescue Errno::ENOSPC => e
|
27
27
|
Rails.logger.error("Unable to write cache, cache full: #{e.message}")
|
@@ -33,6 +33,7 @@ module InfoparkComponentCache
|
|
33
33
|
end
|
34
34
|
|
35
35
|
protected
|
36
|
+
|
36
37
|
def backing_storage
|
37
38
|
raise TypeError, "Cannot use abstract cache storage. Please provide a concrete cache storage in #{self.class.name}"
|
38
39
|
end
|
@@ -5,13 +5,19 @@ module InfoparkComponentCache
|
|
5
5
|
# and some parameters (hash). It should be used to point
|
6
6
|
# to some data stored in a particular context
|
7
7
|
class Component
|
8
|
-
attr_reader :obj, :
|
8
|
+
attr_reader :obj, :component, :params
|
9
9
|
|
10
|
-
def initialize(obj, component, params={})
|
11
|
-
@obj
|
10
|
+
def initialize(obj, component, params = {})
|
11
|
+
@obj = obj
|
12
|
+
@component = component
|
13
|
+
@params = params
|
12
14
|
end
|
13
15
|
|
14
|
-
def
|
16
|
+
def name
|
17
|
+
component
|
18
|
+
end
|
19
|
+
|
20
|
+
def cache_key(meta_prefix = nil)
|
15
21
|
if meta_prefix
|
16
22
|
meta_prefix + "_" + KeyGenerator.generate_key(identity_hash)
|
17
23
|
else
|
@@ -20,7 +26,9 @@ module InfoparkComponentCache
|
|
20
26
|
end
|
21
27
|
|
22
28
|
def identity_hash
|
23
|
-
@params
|
29
|
+
@params
|
30
|
+
.except(:obj) # avoid the (ruby) object_id as part of the cache key (to_s), because it will change for each request
|
31
|
+
.merge({ obj_name: @obj.name, obj_id: @obj.id, obj_component: @component })
|
24
32
|
end
|
25
33
|
end
|
26
|
-
end
|
34
|
+
end
|
@@ -24,27 +24,27 @@ module InfoparkComponentCache
|
|
24
24
|
# @see Guard::ValuePresent
|
25
25
|
# @see Guard::LastChanged
|
26
26
|
# @see Guard::ObjCount
|
27
|
-
def initialize(obj, name, params={}, guards=[])
|
27
|
+
def initialize(obj, name, params = {}, guards = [])
|
28
28
|
@component = Component.new(obj, name, params)
|
29
|
-
if guards.empty?
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
29
|
+
@guards = if guards.empty?
|
30
|
+
[
|
31
|
+
Guards::ValuePresent.new(@component),
|
32
|
+
Guards::LastChanged.new(@component),
|
33
|
+
Guards::ObjCount.new(@component),
|
34
|
+
Guards::ValidFrom.new(@component),
|
35
|
+
Guards::ValidUntil.new(@component)
|
36
|
+
]
|
37
|
+
else
|
38
|
+
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
|
46
|
+
end
|
47
|
+
end
|
48
48
|
end
|
49
49
|
|
50
50
|
# Checks if cache is valid (in consistent state).
|
@@ -54,11 +54,13 @@ module InfoparkComponentCache
|
|
54
54
|
#
|
55
55
|
# @return true if cache is valid and in consistent state
|
56
56
|
def expired?
|
57
|
-
return
|
57
|
+
return true unless cache.enabled?
|
58
|
+
|
59
|
+
!guards.all?(&:consistent?)
|
60
|
+
rescue StandardError => e
|
61
|
+
raise e if Rails.env.test?
|
58
62
|
|
59
|
-
|
60
|
-
raise exception if Rails.env.test?
|
61
|
-
return true
|
63
|
+
true
|
62
64
|
end
|
63
65
|
|
64
66
|
# Checks if the cache is in consistent state and cached value
|
@@ -69,17 +71,19 @@ module InfoparkComponentCache
|
|
69
71
|
# @see {#expired?}
|
70
72
|
# @yieldreturn value to be used in case of cache miss
|
71
73
|
# @return cached value or the return value of the block
|
72
|
-
def fetch(&
|
74
|
+
def fetch(&_block)
|
73
75
|
if expired?
|
74
76
|
value = yield
|
75
77
|
begin
|
76
|
-
cache.
|
77
|
-
|
78
|
-
|
78
|
+
if cache.enabled?
|
79
|
+
cache.write(component.cache_key, value)
|
80
|
+
ensure_consistency!
|
81
|
+
end
|
82
|
+
value
|
83
|
+
rescue StandardError => e
|
84
|
+
raise e if Rails.env.test?
|
79
85
|
|
80
|
-
|
81
|
-
raise exception if Rails.env.test?
|
82
|
-
return value
|
86
|
+
value
|
83
87
|
end
|
84
88
|
else
|
85
89
|
cache.read(component.cache_key)
|
@@ -21,6 +21,7 @@ module InfoparkComponentCache
|
|
21
21
|
end
|
22
22
|
|
23
23
|
protected
|
24
|
+
|
24
25
|
include VolatileCache
|
25
26
|
|
26
27
|
def delayed_value(method_name)
|
@@ -30,17 +31,17 @@ module InfoparkComponentCache
|
|
30
31
|
def still_delayed?(method_name)
|
31
32
|
volatile_cache.exist?(delayed_value_key(method_name))
|
32
33
|
end
|
33
|
-
|
34
|
+
|
34
35
|
def delay_value(method_name, value, expiration)
|
35
36
|
volatile_cache.write(delayed_value_key(method_name), value, expires_in: expiration)
|
36
37
|
value
|
37
38
|
end
|
38
39
|
|
39
40
|
def delayed_value_key(method_name)
|
40
|
-
KeyGenerator.generate_key(
|
41
|
-
|
42
|
-
|
43
|
-
|
41
|
+
KeyGenerator.generate_key(options.merge({
|
42
|
+
delayed_method_name: method_name,
|
43
|
+
delayed_class_name: self.class.name
|
44
|
+
}))
|
44
45
|
end
|
45
46
|
|
46
47
|
module ClassMethods
|
@@ -55,11 +56,10 @@ module InfoparkComponentCache
|
|
55
56
|
if still_delayed?(method_name)
|
56
57
|
value
|
57
58
|
else
|
58
|
-
delay_value(method_name,
|
59
|
+
delay_value(method_name, send(:"delayed_#{method_name}"), expiration)
|
59
60
|
end
|
60
61
|
end
|
61
62
|
end
|
62
63
|
end
|
63
|
-
|
64
64
|
end
|
65
65
|
end
|
@@ -17,16 +17,17 @@ module InfoparkComponentCache
|
|
17
17
|
end
|
18
18
|
|
19
19
|
protected
|
20
|
+
|
20
21
|
# This method implements scoping on the root Obj class
|
21
22
|
# and additional constraints for limiting object classes
|
22
23
|
# used for lookup
|
23
24
|
def scoped_relation
|
24
|
-
if
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
CmsStateGuard.obj_root_class.
|
25
|
+
conditions = if options[:obj_classes].present?
|
26
|
+
{ obj_class: options[:obj_classes] }
|
27
|
+
else
|
28
|
+
{}
|
29
|
+
end
|
30
|
+
CmsStateGuard.obj_root_class.where(conditions)
|
30
31
|
end
|
31
32
|
end
|
32
33
|
end
|
@@ -27,12 +27,15 @@ module InfoparkComponentCache
|
|
27
27
|
|
28
28
|
# @return [String] the cache key for storing {#current_last_changed}
|
29
29
|
def cache_key
|
30
|
-
component.cache_key(
|
30
|
+
component.cache_key("last_changed")
|
31
31
|
end
|
32
32
|
|
33
33
|
# @return [Time] the timestamp of the most recent change to any Obj
|
34
34
|
def current_last_changed
|
35
|
-
|
35
|
+
str_value = scoped_relation.maximum(:last_changed)
|
36
|
+
return str_value if str_value.kind_of? Time
|
37
|
+
|
38
|
+
RailsConnector::DateAttribute.parse(str_value)
|
36
39
|
end
|
37
40
|
end
|
38
41
|
end
|
@@ -18,7 +18,7 @@ module InfoparkComponentCache
|
|
18
18
|
|
19
19
|
# @return true if obj count can be read from cache with {#cache_key}
|
20
20
|
def count_known?
|
21
|
-
cache.exist?(cache_key) && cache.read(cache_key).kind_of?(
|
21
|
+
cache.exist?(cache_key) && cache.read(cache_key).kind_of?(Integer)
|
22
22
|
end
|
23
23
|
|
24
24
|
# @return true if no obj has been deleted or added since last {#guard!}
|
@@ -28,10 +28,10 @@ module InfoparkComponentCache
|
|
28
28
|
|
29
29
|
# @return [String] the cache key for storing {#current_count}
|
30
30
|
def cache_key
|
31
|
-
component.cache_key(
|
31
|
+
component.cache_key("obj_count")
|
32
32
|
end
|
33
33
|
|
34
|
-
# @return [
|
34
|
+
# @return [Integer] the number of Objs in the database
|
35
35
|
def current_count
|
36
36
|
scoped_relation.count
|
37
37
|
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require "infopark_component_cache/guards/cms_state_guard"
|
2
|
-
|
3
2
|
module InfoparkComponentCache
|
4
3
|
module Guards
|
5
4
|
# @author Tomasz Przedmojski <tomasz.przedmojski@infopark.de>
|
@@ -9,9 +8,9 @@ module InfoparkComponentCache
|
|
9
8
|
class ValidFrom < CmsStateGuard
|
10
9
|
def consistent?
|
11
10
|
if min_valid_from_known?
|
12
|
-
|
11
|
+
no_changes_since?
|
13
12
|
else
|
14
|
-
|
13
|
+
current_min_valid_from.nil?
|
15
14
|
end
|
16
15
|
end
|
17
16
|
|
@@ -31,17 +30,15 @@ module InfoparkComponentCache
|
|
31
30
|
|
32
31
|
# @return [String] the cache key for storing {#current_min_valid_from}
|
33
32
|
def cache_key
|
34
|
-
component.cache_key(
|
33
|
+
component.cache_key("min_valid_from")
|
35
34
|
end
|
36
35
|
|
37
36
|
# @return [Time] the timestamp of the most recent valid_from, or nil if none found
|
38
37
|
def current_min_valid_from
|
39
|
-
str_value = scoped_relation.where(
|
40
|
-
if str_value.
|
41
|
-
|
42
|
-
|
43
|
-
nil
|
44
|
-
end
|
38
|
+
str_value = scoped_relation.where("valid_from > ?", Time.now.to_iso).minimum(:valid_from)
|
39
|
+
return str_value if str_value.kind_of? Time
|
40
|
+
|
41
|
+
RailsConnector::DateAttribute.parse(str_value) if str_value.present?
|
45
42
|
end
|
46
43
|
end
|
47
44
|
end
|