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.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +1 -0
  3. data/.ruby-version +1 -0
  4. data/Gemfile +4 -0
  5. data/README.md +8 -0
  6. data/Rakefile +17 -9
  7. data/app/helpers/infopark_component_cache_helper.rb +1 -7
  8. data/infopark_component_cache.gemspec +6 -3
  9. data/lib/engine.rb +35 -1
  10. data/lib/infopark_component_cache.rb +0 -14
  11. data/lib/infopark_component_cache/abstract_cache_storage.rb +40 -0
  12. data/lib/infopark_component_cache/cache_storage.rb +10 -24
  13. data/lib/infopark_component_cache/component.rb +1 -3
  14. data/lib/infopark_component_cache/component_cache.rb +9 -12
  15. data/lib/infopark_component_cache/consistency_guard.rb +3 -4
  16. data/lib/infopark_component_cache/delayed_guard.rb +65 -0
  17. data/lib/infopark_component_cache/guards/always_consistent.rb +1 -1
  18. data/lib/infopark_component_cache/guards/cms_state_guard.rb +32 -0
  19. data/lib/infopark_component_cache/guards/delayed_last_changed.rb +18 -0
  20. data/lib/infopark_component_cache/guards/delayed_obj_count.rb +18 -0
  21. data/lib/infopark_component_cache/guards/delayed_valid_from.rb +18 -0
  22. data/lib/infopark_component_cache/guards/delayed_valid_until.rb +18 -0
  23. data/lib/infopark_component_cache/guards/last_changed.rb +3 -3
  24. data/lib/infopark_component_cache/guards/never_consistent.rb +1 -1
  25. data/lib/infopark_component_cache/guards/obj_count.rb +3 -3
  26. data/lib/infopark_component_cache/guards/valid_from.rb +3 -3
  27. data/lib/infopark_component_cache/guards/valid_until.rb +3 -3
  28. data/lib/infopark_component_cache/guards/value_present.rb +1 -1
  29. data/lib/infopark_component_cache/version.rb +1 -1
  30. data/lib/infopark_component_cache/volatile_cache.rb +13 -0
  31. data/lib/infopark_component_cache/volatile_cache_storage.rb +18 -0
  32. data/spec/dummy/Rakefile +7 -0
  33. data/spec/dummy/app/controllers/application_controller.rb +2 -0
  34. data/spec/dummy/app/models/obj.rb +2 -0
  35. data/spec/dummy/config.ru +4 -0
  36. data/spec/dummy/config/application.rb +19 -0
  37. data/spec/dummy/config/boot.rb +10 -0
  38. data/spec/dummy/config/database.yml +11 -0
  39. data/spec/dummy/config/environment.rb +5 -0
  40. data/spec/dummy/config/environments/development.rb +37 -0
  41. data/spec/dummy/config/environments/test.rb +10 -0
  42. data/spec/dummy/config/initializers/secret_token.rb +1 -0
  43. data/spec/dummy/config/initializers/session_store.rb +1 -0
  44. data/spec/dummy/config/initializers/wrap_parameters.rb +8 -0
  45. data/spec/dummy/config/routes.rb +58 -0
  46. data/spec/dummy/db/development.sqlite3 +0 -0
  47. data/spec/dummy/db/schema.rb +3 -0
  48. data/spec/dummy/log/.gitkeep +0 -0
  49. data/spec/lib/delayed_guard_spec.rb +72 -0
  50. data/spec/lib/guards/always_consistent_spec.rb +21 -0
  51. data/spec/lib/guards/last_changed_spec.rb +66 -0
  52. data/spec/lib/guards/never_consistent_spec.rb +21 -0
  53. data/spec/lib/guards/obj_count_spec.rb +64 -0
  54. data/spec/lib/guards/valid_from_spec.rb +66 -0
  55. data/spec/lib/guards/valid_until_spec.rb +73 -0
  56. data/spec/spec_helper.rb +25 -0
  57. data/spec/support/cache_switching_macros.rb +29 -0
  58. 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
@@ -2,3 +2,7 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in infopark_component_cache.gemspec
4
4
  gemspec
5
+
6
+ gem 'rails', '3.2.21'
7
+ gem 'infopark_fiona_connector', '6.9.4'
8
+ gem 'infopark_rails_connector', '6.9.4'
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
- require "bundler/gem_tasks"
2
- require 'rubygems'
3
- require 'bundler/setup'
4
- require 'rspec/core/rake_task'
1
+ #!/usr/bin/env rake
5
2
 
6
- desc "Run all specs in spec directory"
7
- RSpec::Core::RakeTask.new(:spec)
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
- # Remove release task
10
- task :release do ; end
11
- task(:release).clear_prerequisites.clear_actions
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 'infopark_rails_connector'
23
- gem.add_development_dependency 'rspec'
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 'singleton'
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
- # Trivial wrapper around underlying cache storages.
7
- # This class or any other class that inherits it *must*
8
- # respect Rails.application.config.action_controller.perform_caching
9
- # setting.
10
- class CacheStorage
11
- include Singleton
12
-
13
- def exist?(key)
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 |klass|
48
- klass.new(@component)
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
@@ -1,4 +1,4 @@
1
- require 'infopark_component_cache/consistency_guard'
1
+ require "infopark_component_cache/consistency_guard"
2
2
 
3
3
  module InfoparkComponentCache
4
4
  module Guards
@@ -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