infopark_component_cache 3.1.0 → 4.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 +5 -5
- 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 +3 -1
- data/Gemfile.lock +214 -0
- data/Gemfile.rails50 +6 -0
- data/Gemfile.rails50.lock +206 -0
- data/Gemfile.rails51 +6 -0
- data/Gemfile.rails51.lock +206 -0
- data/README.md +1 -1
- data/Rakefile +7 -7
- data/app/helpers/infopark_component_cache_helper.rb +2 -2
- data/infopark_component_cache.gemspec +16 -13
- 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 +9 -5
- data/lib/infopark_component_cache/component_cache.rb +31 -30
- 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 +6 -5
- 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 +4 -2
- data/spec/dummy/config/boot.rb +5 -5
- data/spec/dummy/config/environment.rb +1 -1
- data/spec/dummy/config/environments/development.rb +1 -0
- data/spec/dummy/config/environments/test.rb +1 -0
- 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 +8 -8
- data/spec/support/cache_switching_macros.rb +4 -4
- metadata +96 -40
- data/spec/lib/compontent_cache_spec.rb +0 -116
@@ -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,15 +17,16 @@ 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
|
-
|
25
|
+
conditions = if options[:obj_classes].present?
|
26
|
+
{ obj_class: options[:obj_classes] }
|
27
|
+
else
|
28
|
+
{}
|
29
|
+
end
|
29
30
|
CmsStateGuard.obj_root_class.where(conditions)
|
30
31
|
end
|
31
32
|
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
|
@@ -9,9 +9,9 @@ module InfoparkComponentCache
|
|
9
9
|
class ValidUntil < CmsStateGuard
|
10
10
|
def consistent?
|
11
11
|
if min_valid_until_known?
|
12
|
-
|
12
|
+
no_changes_since?
|
13
13
|
else
|
14
|
-
|
14
|
+
current_min_valid_until.nil?
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
@@ -31,17 +31,15 @@ module InfoparkComponentCache
|
|
31
31
|
|
32
32
|
# @return [String] the cache key for storing {#current_min_valid_until}
|
33
33
|
def cache_key
|
34
|
-
component.cache_key(
|
34
|
+
component.cache_key("min_valid_until")
|
35
35
|
end
|
36
36
|
|
37
37
|
# @return [Time] the timestamp of the the object that will be deactivated in nearest future
|
38
38
|
def current_min_valid_until
|
39
|
-
str_value = scoped_relation.where(
|
40
|
-
if str_value.
|
41
|
-
|
42
|
-
|
43
|
-
nil
|
44
|
-
end
|
39
|
+
str_value = scoped_relation.where("valid_until > ?", Time.now.to_iso).minimum(:valid_until)
|
40
|
+
return str_value if str_value.kind_of? Time
|
41
|
+
|
42
|
+
RailsConnector::DateAttribute.parse(str_value) if str_value.present?
|
45
43
|
end
|
46
44
|
end
|
47
45
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "digest/sha2"
|
2
2
|
|
3
3
|
module InfoparkComponentCache
|
4
4
|
module KeyGenerator
|
@@ -19,7 +19,7 @@ module InfoparkComponentCache
|
|
19
19
|
# uses some kind of hashing algorithm and therefore has the same
|
20
20
|
# characteristics: equal inputs yield equal outputs, but different
|
21
21
|
# inputs can yield same outputs (although it is very very unlikely)
|
22
|
-
#
|
22
|
+
#
|
23
23
|
# @param [String] string input string to be encoded
|
24
24
|
# @return [String] string that is guaranteed to
|
25
25
|
# consist only of alphanumeric characters,
|
@@ -28,4 +28,4 @@ module InfoparkComponentCache
|
|
28
28
|
Digest::SHA2.hexdigest(string)
|
29
29
|
end
|
30
30
|
end
|
31
|
-
end
|
31
|
+
end
|
@@ -3,7 +3,7 @@ require "infopark_component_cache/volatile_cache_storage"
|
|
3
3
|
module InfoparkComponentCache
|
4
4
|
# @author Tomasz Przedmojski <tomasz.przedmojski@infopark.de>
|
5
5
|
#
|
6
|
-
# This module provides quick an convieniet access to
|
6
|
+
# This module provides quick an convieniet access to
|
7
7
|
# VolatileCacheStorage in any class that includes it.
|
8
8
|
module VolatileCache
|
9
9
|
def volatile_cache
|
@@ -11,6 +11,7 @@ module InfoparkComponentCache
|
|
11
11
|
# it is "nice to have" but also has to be extremely quick.
|
12
12
|
class VolatileCacheStorage < AbstractCacheStorage
|
13
13
|
protected
|
14
|
+
|
14
15
|
def backing_storage
|
15
16
|
@@backing_storage ||= ActiveSupport::Cache::MemoryStore.new({ size: 10.megabytes })
|
16
17
|
end
|
data/spec/dummy/Rakefile
CHANGED
@@ -2,6 +2,6 @@
|
|
2
2
|
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
3
3
|
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
4
4
|
|
5
|
-
require File.expand_path(
|
5
|
+
require File.expand_path("config/application", __dir__)
|
6
6
|
|
7
7
|
Dummy::Application.load_tasks
|
data/spec/dummy/config.ru
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.expand_path(
|
1
|
+
require File.expand_path("boot", __dir__)
|
2
2
|
|
3
3
|
# Pick the frameworks you want:
|
4
4
|
require "active_record/railtie"
|
@@ -12,6 +12,8 @@ module Dummy
|
|
12
12
|
config.encoding = "utf-8"
|
13
13
|
config.filter_parameters += [:password]
|
14
14
|
config.active_support.escape_html_entities_in_json = true
|
15
|
+
if Rails::VERSION::MAJOR == 5 && Rails::VERSION::MINOR == 2
|
16
|
+
config.active_record.sqlite3.represent_boolean_as_integer = true
|
17
|
+
end
|
15
18
|
end
|
16
19
|
end
|
17
|
-
|
data/spec/dummy/config/boot.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
require
|
2
|
-
gemfile = File.expand_path(
|
1
|
+
require "rubygems"
|
2
|
+
gemfile = File.expand_path("../../../Gemfile", __dir__)
|
3
3
|
|
4
4
|
if File.exist?(gemfile)
|
5
|
-
ENV[
|
6
|
-
require
|
5
|
+
ENV["BUNDLE_GEMFILE"] = gemfile
|
6
|
+
require "bundler"
|
7
7
|
Bundler.setup
|
8
8
|
end
|
9
9
|
|
10
|
-
$:.unshift File.expand_path(
|
10
|
+
$:.unshift File.expand_path("../../../lib", __dir__)
|
@@ -1 +1 @@
|
|
1
|
-
Dummy::Application.config.secret_token =
|
1
|
+
Dummy::Application.config.secret_token = "4806ad3606eea6388c0428912c8c8b37266f29c4c45fa5ecd233316cd5b6a0016aea879cfa603059a7c00296daef192955e26ae3298830bd904c8ff6a791feb1"
|
@@ -1 +1 @@
|
|
1
|
-
Dummy::Application.config.session_store :cookie_store, key:
|
1
|
+
Dummy::Application.config.session_store :cookie_store, key: "_dummy_session"
|
data/spec/dummy/db/schema.rb
CHANGED
@@ -0,0 +1,118 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe InfoparkComponentCache::ComponentCache do
|
4
|
+
subject(:component_cache) { described_class.new(obj, name, params, [guard]) }
|
5
|
+
|
6
|
+
let(:obj) { double(name: "spec_obj", id: 2001) }
|
7
|
+
let(:name) { "spec_cached_component" }
|
8
|
+
let(:params) { { some: "additional", params: "supplied" } }
|
9
|
+
let(:guard) { Class.new(Struct.new(:component)) }
|
10
|
+
|
11
|
+
context "with caching disabled" do
|
12
|
+
before { allow(Rails.application.config.action_controller).to receive(:perform_caching).and_return(false) }
|
13
|
+
|
14
|
+
describe "#fetch" do
|
15
|
+
let(:value) { "very_hard_computation_required_for_this_string" }
|
16
|
+
let(:specific_guard) { component_cache.guards.first }
|
17
|
+
|
18
|
+
it "returns the passed the block value" do
|
19
|
+
expect(component_cache.fetch { value }).to eq(value)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "never calls any methods on the guard" do
|
23
|
+
expect(specific_guard).not_to receive(:consistent?)
|
24
|
+
expect(specific_guard).not_to receive(:guard!)
|
25
|
+
|
26
|
+
component_cache.fetch { value }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "with caching enabled" do
|
32
|
+
before { allow(Rails.application.config.action_controller).to receive(:perform_caching).and_return(true) }
|
33
|
+
|
34
|
+
let(:guard) do
|
35
|
+
Class.new(InfoparkComponentCache::ConsistencyGuard) do
|
36
|
+
def consistent?
|
37
|
+
cache.exist?(:guard_called)
|
38
|
+
end
|
39
|
+
|
40
|
+
def guard!
|
41
|
+
cache.write(:guard_called, 1)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#fetch" do
|
47
|
+
let(:value) { "very_hard_computation_required_for_this_string" }
|
48
|
+
let(:specific_guard) { component_cache.guards.first }
|
49
|
+
let(:computer) { double }
|
50
|
+
|
51
|
+
it "returns the passed the block value" do
|
52
|
+
expect(component_cache.fetch { value }).to eq(value)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "calls the required methods on the guard" do
|
56
|
+
expect(specific_guard).to receive(:consistent?).exactly(3).times
|
57
|
+
expect(specific_guard).to receive(:guard!).exactly(3).times
|
58
|
+
|
59
|
+
3.times { component_cache.fetch { value } }
|
60
|
+
end
|
61
|
+
|
62
|
+
it "only evalues the block once" do
|
63
|
+
expect(computer).to receive(:compute).and_return(value).once
|
64
|
+
|
65
|
+
3.times { expect(component_cache.fetch { computer.compute }).to eq(value) }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "#compontent" do
|
71
|
+
subject(:component_cache) { described_class.new(obj, name, params).component }
|
72
|
+
|
73
|
+
it "stores the passed obj" do
|
74
|
+
expect(component_cache.obj).to eq(obj)
|
75
|
+
end
|
76
|
+
|
77
|
+
it "stores the passed name" do
|
78
|
+
expect(component_cache.name).to eq(name)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "stores the passed params" do
|
82
|
+
expect(component_cache.params).to eq(params)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "#guards" do
|
87
|
+
subject(:component_cache) { described_class.new(obj, name, params, [guard_class1, guard_params]).guards }
|
88
|
+
|
89
|
+
let(:guard_class1) { Struct.new(:component) }
|
90
|
+
let(:guard_class2) { Struct.new(:compontent, :extra) }
|
91
|
+
let(:guard_params) { { guard: guard_class2, something: "more" } }
|
92
|
+
|
93
|
+
it "contains an array of passed guards" do
|
94
|
+
expect(component_cache).to match_array([
|
95
|
+
an_instance_of(guard_class1),
|
96
|
+
an_instance_of(guard_class2)
|
97
|
+
])
|
98
|
+
end
|
99
|
+
|
100
|
+
it "preserves the extra params" do
|
101
|
+
expect(component_cache.last.extra).to eq(guard_params)
|
102
|
+
end
|
103
|
+
|
104
|
+
context "with no guards specified in the constructors" do
|
105
|
+
subject(:component_cache) { described_class.new(obj, name, params).guards }
|
106
|
+
|
107
|
+
it "contains standard guards" do
|
108
|
+
expect(component_cache).to match_array([
|
109
|
+
an_instance_of(InfoparkComponentCache::Guards::ValuePresent),
|
110
|
+
an_instance_of(InfoparkComponentCache::Guards::LastChanged),
|
111
|
+
an_instance_of(InfoparkComponentCache::Guards::ObjCount),
|
112
|
+
an_instance_of(InfoparkComponentCache::Guards::ValidFrom),
|
113
|
+
an_instance_of(InfoparkComponentCache::Guards::ValidUntil)
|
114
|
+
])
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "spec_helper"
|
2
|
+
require "infopark_component_cache/delayed_guard"
|
3
3
|
|
4
4
|
describe InfoparkComponentCache::DelayedGuard do
|
5
5
|
let(:base_class_with_options) do
|
@@ -56,7 +56,7 @@ describe InfoparkComponentCache::DelayedGuard do
|
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
|
-
context "after the delay has passed" do
|
59
|
+
context "when after the delay has passed" do
|
60
60
|
specify "the calls returns new value" do
|
61
61
|
first_value = delayed_subject.simple_counter
|
62
62
|
expect(first_value).to eq(undelayed_subject.simple_counter)
|