infopark_component_cache 1.1.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,18 @@
|
|
1
|
+
require "infopark_component_cache/delayed_guard"
|
2
|
+
require "infopark_component_cache/guards/obj_count"
|
3
|
+
|
4
|
+
module InfoparkComponentCache
|
5
|
+
module Guards
|
6
|
+
# @author Tomasz Przedmojski <tomasz.przedmojski@infopark.de>
|
7
|
+
#
|
8
|
+
# This class is a delayed version of ObjCount guard.
|
9
|
+
# The preconfigured delay is 10 seconds.
|
10
|
+
#
|
11
|
+
# @see DelayedGuard
|
12
|
+
# @see ObjCount
|
13
|
+
class DelayedObjCount < ObjCount
|
14
|
+
include DelayedGuard
|
15
|
+
delay :current_count, for: 10.seconds
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "infopark_component_cache/delayed_guard"
|
2
|
+
require "infopark_component_cache/guards/valid_from"
|
3
|
+
|
4
|
+
module InfoparkComponentCache
|
5
|
+
module Guards
|
6
|
+
# @author Tomasz Przedmojski <tomasz.przedmojski@infopark.de>
|
7
|
+
#
|
8
|
+
# This class is a delayed version of ValidFrom guard.
|
9
|
+
# The preconfigured delay is 10 seconds.
|
10
|
+
#
|
11
|
+
# @see DelayedGuard
|
12
|
+
# @see ValidFrom
|
13
|
+
class DelayedValidFrom < ValidFrom
|
14
|
+
include DelayedGuard
|
15
|
+
delay :current_min_valid_from, for: 10.seconds
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "infopark_component_cache/delayed_guard"
|
2
|
+
require "infopark_component_cache/guards/valid_until"
|
3
|
+
|
4
|
+
module InfoparkComponentCache
|
5
|
+
module Guards
|
6
|
+
# @author Tomasz Przedmojski <tomasz.przedmojski@infopark.de>
|
7
|
+
#
|
8
|
+
# This class is a delayed version of ValidUntil guard.
|
9
|
+
# The preconfigured delay is 10 seconds.
|
10
|
+
#
|
11
|
+
# @see DelayedGuard
|
12
|
+
# @see ValidUntil
|
13
|
+
class DelayedValidUntil < ValidUntil
|
14
|
+
include DelayedGuard
|
15
|
+
delay :current_min_valid_until, for: 10.seconds
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "infopark_component_cache/guards/cms_state_guard"
|
2
2
|
|
3
3
|
module InfoparkComponentCache
|
4
4
|
module Guards
|
@@ -6,7 +6,7 @@ module InfoparkComponentCache
|
|
6
6
|
#
|
7
7
|
# This Guard class ensures that the objects in database do not change inbetween.
|
8
8
|
# (It caches the newest timestamp and compares it to the current value)
|
9
|
-
class LastChanged <
|
9
|
+
class LastChanged < CmsStateGuard
|
10
10
|
def consistent?
|
11
11
|
last_changed_known? && no_changes_since?
|
12
12
|
end
|
@@ -32,7 +32,7 @@ module InfoparkComponentCache
|
|
32
32
|
|
33
33
|
# @return [Time] the timestamp of the most recent change to any Obj
|
34
34
|
def current_last_changed
|
35
|
-
RailsConnector::DateAttribute.parse(
|
35
|
+
RailsConnector::DateAttribute.parse(scoped_relation.maximum(:last_changed))
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "infopark_component_cache/guards/cms_state_guard"
|
2
2
|
|
3
3
|
module InfoparkComponentCache
|
4
4
|
module Guards
|
@@ -7,7 +7,7 @@ module InfoparkComponentCache
|
|
7
7
|
# This Guard class ensures that the total count of Objs in the database
|
8
8
|
# does not change inbetween. (It caches the number of Objs and compares
|
9
9
|
# it to the current count)
|
10
|
-
class ObjCount <
|
10
|
+
class ObjCount < CmsStateGuard
|
11
11
|
def consistent?
|
12
12
|
count_known? && no_changes_since?
|
13
13
|
end
|
@@ -33,7 +33,7 @@ module InfoparkComponentCache
|
|
33
33
|
|
34
34
|
# @return [Fixnum] the number of Objs in the database
|
35
35
|
def current_count
|
36
|
-
|
36
|
+
scoped_relation.count
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "infopark_component_cache/guards/cms_state_guard"
|
2
2
|
|
3
3
|
module InfoparkComponentCache
|
4
4
|
module Guards
|
@@ -6,7 +6,7 @@ module InfoparkComponentCache
|
|
6
6
|
#
|
7
7
|
# This guard ensures that any object that becomes valid (through valid_from)
|
8
8
|
# will cause inconsistency
|
9
|
-
class ValidFrom <
|
9
|
+
class ValidFrom < CmsStateGuard
|
10
10
|
def consistent?
|
11
11
|
if min_valid_from_known?
|
12
12
|
return no_changes_since?
|
@@ -36,7 +36,7 @@ module InfoparkComponentCache
|
|
36
36
|
|
37
37
|
# @return [Time] the timestamp of the most recent valid_from, or nil if none found
|
38
38
|
def current_min_valid_from
|
39
|
-
str_value =
|
39
|
+
str_value = scoped_relation.where('valid_from > ?', Time.now.to_iso).minimum(:valid_from)
|
40
40
|
if str_value.present?
|
41
41
|
RailsConnector::DateAttribute.parse(str_value)
|
42
42
|
else
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "infopark_component_cache/guards/cms_state_guard"
|
2
2
|
|
3
3
|
module InfoparkComponentCache
|
4
4
|
module Guards
|
@@ -6,7 +6,7 @@ module InfoparkComponentCache
|
|
6
6
|
#
|
7
7
|
# This guard ensures that any object, whose valid until date has been passed
|
8
8
|
# will cause inconsistency.
|
9
|
-
class ValidUntil <
|
9
|
+
class ValidUntil < CmsStateGuard
|
10
10
|
def consistent?
|
11
11
|
if min_valid_until_known?
|
12
12
|
return no_changes_since?
|
@@ -36,7 +36,7 @@ module InfoparkComponentCache
|
|
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 =
|
39
|
+
str_value = scoped_relation.where('valid_until > ?', Time.now.to_iso).minimum(:valid_until)
|
40
40
|
if str_value.present?
|
41
41
|
RailsConnector::DateAttribute.parse(str_value)
|
42
42
|
else
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require "infopark_component_cache/volatile_cache_storage"
|
2
|
+
|
3
|
+
module InfoparkComponentCache
|
4
|
+
# @author Tomasz Przedmojski <tomasz.przedmojski@infopark.de>
|
5
|
+
#
|
6
|
+
# This module provides quick an convieniet access to
|
7
|
+
# VolatileCacheStorage in any class that includes it.
|
8
|
+
module VolatileCache
|
9
|
+
def volatile_cache
|
10
|
+
@__volatile_cache ||= VolatileCacheStorage.instance
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "infopark_component_cache/abstract_cache_storage"
|
2
|
+
|
3
|
+
module InfoparkComponentCache
|
4
|
+
# @author Tomasz Przedmojski <tomasz.przedmojski@infopark.de>
|
5
|
+
#
|
6
|
+
# This class has the exact same interface as CacheStorage
|
7
|
+
# but it does not offer any guarantees regarding persistence
|
8
|
+
# of the cache.
|
9
|
+
# It should be not regarded any more safe than a NullCache.
|
10
|
+
# The primasy use case here is to provide caching where
|
11
|
+
# it is "nice to have" but also has to be extremely quick.
|
12
|
+
class VolatileCacheStorage < AbstractCacheStorage
|
13
|
+
protected
|
14
|
+
def backing_storage
|
15
|
+
@@backing_storage ||= ActiveSupport::Cache::MemoryStore.new({ size: 10.megabytes })
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/spec/dummy/Rakefile
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
3
|
+
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
4
|
+
|
5
|
+
require File.expand_path('../config/application', __FILE__)
|
6
|
+
|
7
|
+
Dummy::Application.load_tasks
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require File.expand_path('../boot', __FILE__)
|
2
|
+
|
3
|
+
# Pick the frameworks you want:
|
4
|
+
require "active_record/railtie"
|
5
|
+
require "action_controller/railtie"
|
6
|
+
require "active_resource/railtie"
|
7
|
+
|
8
|
+
Bundler.require(*Rails.groups)
|
9
|
+
require "infopark_component_cache"
|
10
|
+
|
11
|
+
module Dummy
|
12
|
+
class Application < Rails::Application
|
13
|
+
config.encoding = "utf-8"
|
14
|
+
config.filter_parameters += [:password]
|
15
|
+
config.active_support.escape_html_entities_in_json = true
|
16
|
+
config.active_record.whitelist_attributes = true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
Dummy::Application.configure do
|
2
|
+
# Settings specified here will take precedence over those in config/application.rb
|
3
|
+
|
4
|
+
# In the development environment your application's code is reloaded on
|
5
|
+
# every request. This slows down response time but is perfect for development
|
6
|
+
# since you don't have to restart the web server when you make code changes.
|
7
|
+
config.cache_classes = false
|
8
|
+
|
9
|
+
# Log error messages when you accidentally call methods on nil.
|
10
|
+
config.whiny_nils = true
|
11
|
+
|
12
|
+
# Show full error reports and disable caching
|
13
|
+
config.consider_all_requests_local = true
|
14
|
+
config.action_controller.perform_caching = false
|
15
|
+
|
16
|
+
# Don't care if the mailer can't send
|
17
|
+
config.action_mailer.raise_delivery_errors = false
|
18
|
+
|
19
|
+
# Print deprecation notices to the Rails logger
|
20
|
+
config.active_support.deprecation = :log
|
21
|
+
|
22
|
+
# Only use best-standards-support built into browsers
|
23
|
+
config.action_dispatch.best_standards_support = :builtin
|
24
|
+
|
25
|
+
# Raise exception on mass assignment protection for Active Record models
|
26
|
+
config.active_record.mass_assignment_sanitizer = :strict
|
27
|
+
|
28
|
+
# Log the query plan for queries taking more than this (works
|
29
|
+
# with SQLite, MySQL, and PostgreSQL)
|
30
|
+
config.active_record.auto_explain_threshold_in_seconds = 0.5
|
31
|
+
|
32
|
+
# Do not compress assets
|
33
|
+
config.assets.compress = false
|
34
|
+
|
35
|
+
# Expands the lines which load the assets
|
36
|
+
config.assets.debug = true
|
37
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
Dummy::Application.configure do
|
2
|
+
config.cache_classes = true
|
3
|
+
config.whiny_nils = true
|
4
|
+
config.consider_all_requests_local = true
|
5
|
+
config.action_controller.perform_caching = false
|
6
|
+
config.action_dispatch.show_exceptions = false
|
7
|
+
config.action_controller.allow_forgery_protection = false
|
8
|
+
config.active_record.mass_assignment_sanitizer = :strict
|
9
|
+
config.active_support.deprecation = :stderr
|
10
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
Dummy::Application.config.secret_token = '4806ad3606eea6388c0428912c8c8b37266f29c4c45fa5ecd233316cd5b6a0016aea879cfa603059a7c00296daef192955e26ae3298830bd904c8ff6a791feb1'
|
@@ -0,0 +1 @@
|
|
1
|
+
Dummy::Application.config.session_store :cookie_store, key: '_dummy_session'
|
@@ -0,0 +1,58 @@
|
|
1
|
+
Dummy::Application.routes.draw do
|
2
|
+
# The priority is based upon order of creation:
|
3
|
+
# first created -> highest priority.
|
4
|
+
|
5
|
+
# Sample of regular route:
|
6
|
+
# match 'products/:id' => 'catalog#view'
|
7
|
+
# Keep in mind you can assign values other than :controller and :action
|
8
|
+
|
9
|
+
# Sample of named route:
|
10
|
+
# match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
|
11
|
+
# This route can be invoked with purchase_url(:id => product.id)
|
12
|
+
|
13
|
+
# Sample resource route (maps HTTP verbs to controller actions automatically):
|
14
|
+
# resources :products
|
15
|
+
|
16
|
+
# Sample resource route with options:
|
17
|
+
# resources :products do
|
18
|
+
# member do
|
19
|
+
# get 'short'
|
20
|
+
# post 'toggle'
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# collection do
|
24
|
+
# get 'sold'
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
|
28
|
+
# Sample resource route with sub-resources:
|
29
|
+
# resources :products do
|
30
|
+
# resources :comments, :sales
|
31
|
+
# resource :seller
|
32
|
+
# end
|
33
|
+
|
34
|
+
# Sample resource route with more complex sub-resources
|
35
|
+
# resources :products do
|
36
|
+
# resources :comments
|
37
|
+
# resources :sales do
|
38
|
+
# get 'recent', :on => :collection
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
|
42
|
+
# Sample resource route within a namespace:
|
43
|
+
# namespace :admin do
|
44
|
+
# # Directs /admin/products/* to Admin::ProductsController
|
45
|
+
# # (app/controllers/admin/products_controller.rb)
|
46
|
+
# resources :products
|
47
|
+
# end
|
48
|
+
|
49
|
+
# You can have the root of your site routed with "root"
|
50
|
+
# just remember to delete public/index.html.
|
51
|
+
# root :to => 'welcome#index'
|
52
|
+
|
53
|
+
# See how all your routes lay out with "rake routes"
|
54
|
+
|
55
|
+
# This is a legacy wild controller route that's not recommended for RESTful applications.
|
56
|
+
# Note: This route will make all actions in every controller accessible via GET requests.
|
57
|
+
# match ':controller(/:action(/:id))(.:format)'
|
58
|
+
end
|
Binary file
|
File without changes
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'infopark_component_cache/delayed_guard'
|
3
|
+
|
4
|
+
describe InfoparkComponentCache::DelayedGuard do
|
5
|
+
let(:base_class_with_options) do
|
6
|
+
Class.new do
|
7
|
+
def options
|
8
|
+
{}
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:undelayed_class) do
|
14
|
+
Class.new(base_class_with_options) do
|
15
|
+
def simple_counter
|
16
|
+
@simple_counter ||= 0
|
17
|
+
@simple_counter += 1
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
let(:delayed_class) do
|
23
|
+
Class.new(undelayed_class) do
|
24
|
+
include InfoparkComponentCache::DelayedGuard
|
25
|
+
delay :simple_counter, for: 1.second
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
let(:undelayed_subject) { undelayed_class.new }
|
30
|
+
let(:delayed_subject) { delayed_class.new }
|
31
|
+
|
32
|
+
context "with caching disabled" do
|
33
|
+
disable_cache
|
34
|
+
|
35
|
+
specify "the calls are not delayed" do
|
36
|
+
10.times do
|
37
|
+
expect(undelayed_subject.simple_counter).to eq(delayed_subject.simple_counter)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "with caching enabled" do
|
43
|
+
enable_cache
|
44
|
+
before { InfoparkComponentCache::VolatileCacheStorage.instance.send(:backing_storage).send(:clear) }
|
45
|
+
|
46
|
+
specify "first call returns the same value" do
|
47
|
+
expect(undelayed_subject.simple_counter).to eq(delayed_subject.simple_counter)
|
48
|
+
end
|
49
|
+
|
50
|
+
specify "following call does return the same value" do
|
51
|
+
first_value = delayed_subject.simple_counter
|
52
|
+
expect(first_value).to eq(undelayed_subject.simple_counter)
|
53
|
+
10.times do
|
54
|
+
expect(delayed_subject.simple_counter).to eq(first_value)
|
55
|
+
expect(undelayed_subject.simple_counter).not_to eq(first_value)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "after the delay has passed" do
|
60
|
+
specify "the calls returns new value" do
|
61
|
+
first_value = delayed_subject.simple_counter
|
62
|
+
expect(first_value).to eq(undelayed_subject.simple_counter)
|
63
|
+
|
64
|
+
sleep 1
|
65
|
+
|
66
|
+
second_value = delayed_subject.simple_counter
|
67
|
+
expect(second_value).not_to eq(first_value)
|
68
|
+
expect(second_value).to eq(undelayed_subject.simple_counter)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|