cache_advance 1.1.4

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.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ rdoc
2
+ pkg
3
+ *.gem
data/CHANGELOG ADDED
@@ -0,0 +1,8 @@
1
+ 1.1.2
2
+
3
+ * Strip whitespace from keys in order to avoid errors from memcache.
4
+ * Symbolize cache names.
5
+
6
+ 1.1.1
7
+
8
+ * Fixed a bug where the enabled flag would expire, causing caches to be reenabled.
data/README.textile ADDED
@@ -0,0 +1,47 @@
1
+ h1. cache advance
2
+
3
+ CacheAdvance is a wrapper around the Rails caching system that provides a simple, centralized
4
+ configuration file for defining caches and an even simpler way to apply them.
5
+
6
+ Written by "Aubrey Holland":mailto:aubreyholland@gmail.com.
7
+
8
+ h3. download
9
+
10
+ Github: "Page":http://github.com/aub/cache_advance/tree/master
11
+
12
+ Gem: <pre>gem install aub-cache_advance --source http://gems.github.com</pre>
13
+
14
+ Note: if you install using the gem from Github, you'll need this
15
+ in your environment.rb if you want to use Rails 2.1's dependency manager:
16
+
17
+ <pre><code>
18
+ config.gem 'aub-cache_advance', :lib => 'cache_advance', :source => 'http://gems.github.com'
19
+ </code></pre>
20
+
21
+ h3. cache definition
22
+
23
+ Caches are defined in the file config/caches.rb, which will be loaded automatically by the gem.
24
+ This file is similar in format to the rails routes config file, allowing you to specify named
25
+ caches and configure their keys and how they will be expired.
26
+
27
+ <pre><code>
28
+ CacheAdvance::Caches.define_caches do |config|
29
+
30
+ config.qualifier(:params) do |request|
31
+ request.params
32
+ end
33
+
34
+ config.plugin :template_handler_observer_cache_plugin
35
+
36
+ config.content_block :expiration_time => 10.minutes, :qualifiers => [ :subdomain, :params ]
37
+
38
+ config.change_towns_all :expiration_types => [ :publication ]
39
+ config.change_towns_limited :expiration_types => [ :publication ]
40
+
41
+ config.publication_twitter_update :expiration_time => 10.minutes, :qualifiers => [ :subdomain ]
42
+
43
+ config.weather_widget :expiration_time => 10.minutes, :qualifiers => [ :subdomain ]
44
+ end
45
+ </code></pre>
46
+
47
+ ..............More later.
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/testtask'
4
+
5
+ FileList['tasks/**/*.rake'].each { |file| load file }
6
+
7
+ task :default => 'test'
8
+
9
+ begin
10
+ require 'jeweler'
11
+ Jeweler::Tasks.new do |gemspec|
12
+ gemspec.name = 'cache_advance'
13
+ gemspec.summary = 'A declarative system for caching with ActiveRecord'
14
+ gemspec.email = 'aubreyholland@gmail.com'
15
+ gemspec.homepage = 'http://github.com/aub/cache_advance/tree/master'
16
+ gemspec.description = 'hmm'
17
+ gemspec.authors = ['Aubrey Holland']
18
+ end
19
+ Jeweler::GemcutterTasks.new
20
+ rescue LoadError
21
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
22
+ end
23
+
data/TODO.textile ADDED
@@ -0,0 +1,6 @@
1
+ - Fix to allow keys of > 256 characters with memcache
2
+ - Make test coverage not pitiful
3
+ - Add ability for cache to expire when a specific object changes
4
+ - Apply plugins only to specific caches
5
+ - Test with caching strategies other than memcache
6
+ - Improve documentation
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.1.4
@@ -0,0 +1,68 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{cache_advance}
8
+ s.version = "1.1.4"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Aubrey Holland"]
12
+ s.date = %q{2009-10-21}
13
+ s.description = %q{hmm}
14
+ s.email = %q{aubreyholland@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "README.textile"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "CHANGELOG",
21
+ "README.textile",
22
+ "Rakefile",
23
+ "TODO.textile",
24
+ "VERSION",
25
+ "cache_advance.gemspec",
26
+ "lib/cache_advance.rb",
27
+ "lib/cache_advance/active_record_sweeper.rb",
28
+ "lib/cache_advance/cache_set.rb",
29
+ "lib/cache_advance/cached_key_list.rb",
30
+ "lib/cache_advance/lock.rb",
31
+ "lib/cache_advance/mapper.rb",
32
+ "lib/cache_advance/named_cache.rb",
33
+ "lib/cache_advance/named_cache_configuration.rb",
34
+ "rails/init.rb",
35
+ "script/console",
36
+ "test/active_record_sweeper_test.rb",
37
+ "test/cache_mock.rb",
38
+ "test/cache_set_test.rb",
39
+ "test/key_test.rb",
40
+ "test/mapper_test.rb",
41
+ "test/named_cache_test.rb",
42
+ "test/test_helper.rb"
43
+ ]
44
+ s.homepage = %q{http://github.com/aub/cache_advance/tree/master}
45
+ s.rdoc_options = ["--charset=UTF-8"]
46
+ s.require_paths = ["lib"]
47
+ s.rubygems_version = %q{1.3.5}
48
+ s.summary = %q{A declarative system for caching with ActiveRecord}
49
+ s.test_files = [
50
+ "test/active_record_sweeper_test.rb",
51
+ "test/cache_mock.rb",
52
+ "test/cache_set_test.rb",
53
+ "test/key_test.rb",
54
+ "test/mapper_test.rb",
55
+ "test/named_cache_test.rb",
56
+ "test/test_helper.rb"
57
+ ]
58
+
59
+ if s.respond_to? :specification_version then
60
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
61
+ s.specification_version = 3
62
+
63
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
64
+ else
65
+ end
66
+ else
67
+ end
68
+ end
@@ -0,0 +1,32 @@
1
+ require 'rubygems'
2
+ gem 'activerecord'
3
+ require 'active_record/observer'
4
+
5
+ module CacheAdvance
6
+ class ActiveRecordSweeper < ::ActiveRecord::Observer
7
+
8
+ def self.initialize_observed(classes)
9
+ observe(classes)
10
+ end
11
+
12
+ def reload_sweeper
13
+ observed_classes.each do |klass|
14
+ klass.name.constantize.add_observer(self)
15
+ end
16
+ end
17
+
18
+ def after_create(object)
19
+ expire_caches_for(object)
20
+ end
21
+
22
+ alias_method :after_update, :after_create
23
+ alias_method :after_destroy, :after_create
24
+
25
+ protected
26
+
27
+ def expire_caches_for(object)
28
+ class_symbol = object.class.name.underscore.to_sym
29
+ CacheAdvance.cache_set.expire_for_class(class_symbol)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,57 @@
1
+ module CacheAdvance
2
+ class CacheSet
3
+ attr_reader :named_caches
4
+ attr_reader :qualifiers
5
+ attr_reader :plugins
6
+
7
+ def initialize(store)
8
+ @store, @named_caches, @qualifiers, @plugins = store, {}, {}, []
9
+ end
10
+
11
+ def setup_complete
12
+
13
+ end
14
+
15
+ def apply(cache_name, request, options, &block)
16
+ cache_name = cache_name.to_sym
17
+ if CacheAdvance.caching_enabled
18
+ named_cache = @named_caches[cache_name]
19
+ raise UnknownNamedCacheException if named_cache.nil?
20
+ named_cache.value_for(request, options, &block)
21
+ else
22
+ block.call
23
+ end
24
+ end
25
+
26
+ def add_qualifier(name, proc)
27
+ @qualifiers[name] = proc
28
+ end
29
+
30
+ def add_plugin(plugin)
31
+ @plugins << plugin
32
+ end
33
+
34
+ def add_named_cache(name, options)
35
+ name = name.to_sym
36
+ @named_caches[name] = NamedCache.new(name, options, self, @store)
37
+ end
38
+
39
+ def define_caches
40
+ yield Mapper.new(self)
41
+ end
42
+
43
+ def create_sweepers
44
+ @sweeper_type.initialize_observed(@named_caches.values.map { |c| c.expiration_types }.flatten.compact.uniq)
45
+ end
46
+
47
+ def expire_for_class(class_name)
48
+ @named_caches.values.each do |named_cache|
49
+ named_cache.expire_for(class_name)
50
+ end
51
+ end
52
+
53
+ def sweeper_type=(type)
54
+ @sweeper_type = type
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,46 @@
1
+ module CacheAdvance
2
+ class CachedKeyList
3
+ def initialize(store, cache_key, expiration_time=nil)
4
+ @store, @cache_key, @expiration_time = store, cache_key, expiration_time
5
+ end
6
+
7
+ def all_keys
8
+ key_list.keys
9
+ end
10
+
11
+ def add_key(key)
12
+ Lock.new(@store).execute_locked(@cache_key) do
13
+ data = key_list
14
+ unless data.has_key?(key)
15
+ data[key] = @expiration_time.nil? ? nil : Time.now + @expiration_time
16
+ @store.set(@cache_key, data)
17
+ end
18
+ end
19
+ end
20
+
21
+ def delete_key(key)
22
+ Lock.new(@store).execute_locked(@cache_key) do
23
+ data = key_list
24
+ if data.has_key?(key)
25
+ data.delete(key)
26
+ @store.set(@cache_key, data)
27
+ end
28
+ end
29
+ end
30
+
31
+ def clear
32
+ @store.set(@cache_key, {})
33
+ end
34
+
35
+ protected
36
+
37
+ def key_list
38
+ list = @store.get(@cache_key) || {}
39
+ if @expiration_time
40
+ now = Time.now
41
+ list.delete_if { |k,v| v <= now }
42
+ end
43
+ list
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,40 @@
1
+ module CacheAdvance
2
+ class Lock
3
+
4
+ class LockAcquisitionFailureException < Exception; end
5
+
6
+ DEFAULT_RETRIES = 5
7
+ DEFAULT_EXPIRATION_TIME = 30
8
+
9
+ def initialize(store)
10
+ @store = store
11
+ end
12
+
13
+ def execute_locked(key, lock_expiry = DEFAULT_EXPIRATION_TIME, retries = DEFAULT_RETRIES)
14
+ begin
15
+ acquire(key, lock_expiry, retries)
16
+ yield
17
+ ensure
18
+ release_lock(key)
19
+ end
20
+ end
21
+
22
+ def acquire(key, lock_expiry = DEFAULT_EXPIRATION_TIME, retries = DEFAULT_RETRIES)
23
+ retries.times do |count|
24
+ begin
25
+ return if @store.set("lock/#{key}", Process.pid, lock_expiry) == "STORED\r\n"
26
+ end
27
+ exponential_sleep(count) unless count == retries - 1
28
+ end
29
+ raise LockAcquisitionFailureException, "Couldn't acquire memcache lock for: #{key}"
30
+ end
31
+
32
+ def release_lock(key)
33
+ @store.delete("lock/#{key}")
34
+ end
35
+
36
+ def exponential_sleep(count)
37
+ sleep((2**count) / 10.0)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,29 @@
1
+ gem 'activesupport'
2
+ require 'active_support/core_ext'
3
+
4
+ module CacheAdvance
5
+ class Mapper
6
+ def initialize(cache_set)
7
+ @cache_set = cache_set
8
+ end
9
+
10
+ def qualifier(name, &proc)
11
+ @cache_set.add_qualifier(name, proc)
12
+ end
13
+
14
+ def plugin(name)
15
+ if name.is_a?(Symbol)
16
+ plugin = name.to_s.camelcase.constantize.new
17
+ elsif name.is_a?(Class)
18
+ plugin = name.new
19
+ else
20
+ plugin = name
21
+ end
22
+ @cache_set.add_plugin(plugin)
23
+ end
24
+
25
+ def method_missing(method, options={})
26
+ @cache_set.add_named_cache(method, options)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,124 @@
1
+ module CacheAdvance
2
+ class NamedCache
3
+
4
+ ENABLED_CHECK_INTERVAL = 60
5
+
6
+ def initialize(name, params, cache_set, store)
7
+ @name = name.to_s
8
+ @params = params
9
+ @cache_set = cache_set
10
+ @store = store
11
+ @cached_key_list = CachedKeyList.new(@store, "#{@name}/STORED_CACHES", expiration_time)
12
+ @enabled_check_time = Time.now + ENABLED_CHECK_INTERVAL
13
+ @enabled = nil
14
+ end
15
+
16
+ def value_for(request, options, &block)
17
+ return block.call unless enabled?
18
+
19
+ key = key_for(request, options[:key])
20
+
21
+ if (value = read_from_store(key))
22
+ each_plugin { |p| p.send('after_read', @name, key, value, options) if p.respond_to?('after_read') }
23
+ return value
24
+ end
25
+
26
+ each_plugin { |p| p.send('before_render', @name, key, options) if p.respond_to?('before_render') }
27
+ result = block.call
28
+ each_plugin { |p| p.send('after_render', @name, key, result, options) if p.respond_to?('after_render') }
29
+ each_plugin { |p| p.send('before_write', @name, key, result, options) if p.respond_to?('before_write') }
30
+ write_to_store(key, result)
31
+ each_plugin { |p| p.send('after_write', @name, key, result, options) if p.respond_to?('after_write') }
32
+ result
33
+ end
34
+
35
+ def expire_for(type)
36
+ if expiration_types.include?(type)
37
+ expire_all
38
+ end
39
+ end
40
+
41
+ def expire_all
42
+ delete_all_from_store
43
+ end
44
+
45
+ def all_cached_keys
46
+ @cached_key_list.all_keys
47
+ end
48
+
49
+ def expiration_types
50
+ Array(@params[:expiration_types])
51
+ end
52
+
53
+ def title
54
+ @params[:title] || @name.to_s
55
+ end
56
+
57
+ def enabled=(state)
58
+ @enabled = !!state
59
+ @store.set(enabled_key, @enabled)
60
+ end
61
+
62
+ def enabled?
63
+ if @enabled.nil? || Time.now >= @enabled_check_time
64
+ @enabled = [nil, true].include?(read_from_store(enabled_key, false))
65
+ @enabled_check_time = Time.now + ENABLED_CHECK_INTERVAL
66
+ end
67
+ @enabled
68
+ end
69
+
70
+ protected
71
+
72
+ def read_from_store(key, add_to_key_list=true)
73
+ data = @store.get(key)
74
+ # this is to prevent a situation where the cached key list forgets
75
+ # about keys that are actually cached.
76
+ if data && add_to_key_list
77
+ @cached_key_list.add_key(key)
78
+ end
79
+ data
80
+ end
81
+
82
+ def write_to_store(key, value)
83
+ expiration_time ? @store.set(key, value, expiration_time) : @store.set(key, value)
84
+ @cached_key_list.add_key(key)
85
+ end
86
+
87
+ def delete_from_store(key)
88
+ @store.delete(key)
89
+ @cached_key_list.delete_key(key)
90
+ end
91
+
92
+ def delete_all_from_store
93
+ @cached_key_list.all_keys.each { |key| delete_from_store(key) }
94
+ @cached_key_list.clear
95
+ end
96
+
97
+ def each_plugin
98
+ @cache_set.plugins.each do |p|
99
+ yield p
100
+ end
101
+ end
102
+
103
+ def key_for(request, suffix='')
104
+ qualifier_data = qualifiers.map do |q|
105
+ if (qualifier = @cache_set.qualifiers[q])
106
+ (qualifier.call(request) || '').to_s
107
+ end
108
+ end.join('/')
109
+ "#{@name}/#{suffix}/[#{qualifier_data}]".gsub(/\s/, '')
110
+ end
111
+
112
+ def enabled_key
113
+ "#{@name}/ENABLED_STATUS"
114
+ end
115
+
116
+ def expiration_time
117
+ @params[:expiration_time]
118
+ end
119
+
120
+ def qualifiers
121
+ Array(@params[:qualifiers])
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,7 @@
1
+ module CacheAdvance
2
+ class NamedCacheConfiguration
3
+ def initialize(options)
4
+ @options = options
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,21 @@
1
+ %w( cache_set cached_key_list lock mapper named_cache named_cache_configuration).each do |file|
2
+ require File.join(File.dirname(__FILE__), 'cache_advance', file)
3
+ end
4
+
5
+ module CacheAdvance
6
+ class UnknownNamedCacheException < Exception; end
7
+
8
+ class << self
9
+ attr_reader :cache_set
10
+ attr_accessor :caching_enabled
11
+ end
12
+
13
+ @cache_set = nil
14
+ @caching_enabled = true
15
+
16
+ def self.define_caches(store)
17
+ @cache_set = CacheSet.new(store)
18
+ yield Mapper.new(@cache_set)
19
+ @cache_set.setup_complete # This allows the cache set to finalize some of its configuration
20
+ end
21
+ end
data/rails/init.rb ADDED
@@ -0,0 +1,52 @@
1
+ require 'cache_advance'
2
+ require 'cache_advance/active_record_sweeper'
3
+
4
+ require "#{RAILS_ROOT}/config/caches"
5
+ require 'dispatcher'
6
+
7
+ # Setup the sweeper and cache types as appropriate for Rails.
8
+ CacheAdvance.cache_set.sweeper_type = CacheAdvance::ActiveRecordSweeper
9
+
10
+ CacheAdvance.caching_enabled = config.action_controller.perform_caching
11
+
12
+ # This is the helper method that can be used in rails views/controllers/helpers.
13
+ # If caching is disabled, just make it yield the results of the block.
14
+ if config.action_controller.perform_caching
15
+ ActionController::Base.helper do
16
+ def cache_it(cache, options={}, &block)
17
+ CacheAdvance.cache_set.plugins.each do |plugin|
18
+ options.merge!(plugin.cache_it_options(self)) if plugin.respond_to?('cache_it_options')
19
+ end
20
+ CacheAdvance.cache_set.apply(cache, request, options) do
21
+ capture(&block)
22
+ end
23
+ end
24
+ end
25
+ else
26
+ ActionController::Base.helper do
27
+ def cache_it(cache, options={}, &block)
28
+ capture(&block)
29
+ end
30
+ end
31
+ end
32
+
33
+ ActionMailer::Base.helper do
34
+ def cache_it(cache, options={}, &block)
35
+ capture(&block)
36
+ end
37
+ end
38
+
39
+ # This will get called after the standard rails environment is initialized.
40
+ config.after_initialize do
41
+ if config.action_controller.perform_caching
42
+ # This hooks the sweepers into the observer system and adds it to the list.
43
+ CacheAdvance.cache_set.create_sweepers
44
+ ActiveRecord::Base.observers << CacheAdvance::ActiveRecordSweeper
45
+
46
+ # In development mode, the models we observe get reloaded with each request. Using
47
+ # this hook allows us to reload the observer relationships each time as well.
48
+ ActionController::Dispatcher.to_prepare(:cache_advance_reload) do
49
+ CacheAdvance::ActiveRecordSweeper.instance.reload_sweeper
50
+ end
51
+ end
52
+ end
data/script/console ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.join(File.dirname(__FILE__), '..', 'test', 'test_helper')
4
+
5
+ IRB.start
@@ -0,0 +1,32 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper.rb')
2
+
3
+ require 'cache_advance/active_record_sweeper'
4
+
5
+ class Article; end
6
+ class Publication; end
7
+
8
+ class ActiveRecordSweeperTest < Test::Unit::TestCase
9
+
10
+ def setup
11
+ @sweeper = CacheAdvance::ActiveRecordSweeper.instance
12
+ end
13
+
14
+ def test_should_call_observe_with_a_given_set_of_classes
15
+ CacheAdvance::ActiveRecordSweeper.expects(:observe).with([:publication, :article])
16
+ CacheAdvance::ActiveRecordSweeper.initialize_observed([:publication, :article])
17
+ end
18
+
19
+ def test_should_re_add_observers
20
+ CacheAdvance::ActiveRecordSweeper.initialize_observed([:publication, :article])
21
+ Article.expects(:add_observer).with(@sweeper)
22
+ Publication.expects(:add_observer).with(@sweeper)
23
+ @sweeper.reload_sweeper
24
+ end
25
+
26
+ def test_should_expire_caches_on_changes
27
+ CacheAdvance.cache_set.expects(:expire_for_class).with(:publication).times(3)
28
+ %w(after_create after_update after_destroy).each do |method|
29
+ @sweeper.send(method, Publication.new)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,21 @@
1
+ module CacheAdvance
2
+ class CacheMock
3
+ def initialize
4
+ @values = {}
5
+ end
6
+
7
+ def get(key)
8
+ @values[key]
9
+ end
10
+
11
+ def set(key, value, options={})
12
+ result = @values.has_key?(key) ? @values[key] : "STORED\r\n"
13
+ @values[key] = value
14
+ result
15
+ end
16
+
17
+ def delete(key)
18
+ @values[key] = nil
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,40 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class Plugin; end
4
+
5
+ require 'cache_advance/active_record_sweeper'
6
+
7
+ class CacheSetTest < Test::Unit::TestCase
8
+ def setup
9
+ @cache = CacheAdvance::CacheMock.new
10
+ @cache_set = CacheAdvance::CacheSet.new(@cache)
11
+ end
12
+
13
+ def test_define_caches_should_yield_a_mapper
14
+ @cache_set.define_caches do |mapper|
15
+ assert_equal CacheAdvance::Mapper, mapper.class
16
+ end
17
+ end
18
+
19
+ def test_should_apply_the_cache_if_found
20
+ request = mock
21
+ options = { :key => 'hippo' }
22
+ @cache_set.add_named_cache(:kewl, {})
23
+ @cache_set.named_caches[:kewl].expects(:value_for).with(request, options)
24
+ @cache_set.apply(:kewl, request, options) { }
25
+ end
26
+
27
+ def test_apply_should_throw_exception_with_invalid_name
28
+ assert_raise CacheAdvance::UnknownNamedCacheException do
29
+ @cache_set.apply(:total_hack, mock(), {}) { }
30
+ end
31
+ end
32
+
33
+ def test_should_pass_expiration_types_to_the_sweeper
34
+ @cache_set.sweeper_type = CacheAdvance::ActiveRecordSweeper
35
+ @cache_set.add_named_cache(:kewl, { :expiration_types => [:publication, :article] })
36
+ @cache_set.add_named_cache(:howza, { :expiration_types => [:publication] })
37
+ CacheAdvance::ActiveRecordSweeper.expects(:initialize_observed).with([:publication, :article])
38
+ @cache_set.create_sweepers
39
+ end
40
+ end
data/test/key_test.rb ADDED
@@ -0,0 +1,41 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ class KeyTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ @cache = CacheAdvance::CacheMock.new
7
+ @cache_set = CacheAdvance::CacheSet.new(@cache)
8
+ end
9
+
10
+ def test_should_symbolize_the_cache_name
11
+ @cache_set.add_named_cache('booya', {})
12
+ assert_nothing_raised do
13
+ @cache_set.apply(:booya, nil, :key => 'abc') do
14
+ '123'
15
+ end
16
+ end
17
+ end
18
+
19
+ def test_should_symbolize_the_cache_name_when_using_it
20
+ @cache_set.add_named_cache(:booya, {})
21
+ assert_nothing_raised do
22
+ @cache_set.apply('booya', nil, :key => 'abc') do
23
+ '123'
24
+ end
25
+ end
26
+ end
27
+
28
+ def test_should_remove_whitespace_from_the_key
29
+ @cache_set.add_named_cache(:booya, {})
30
+ result = @cache_set.apply(:booya, nil, :key => 'abc def') do
31
+ '123'
32
+ end
33
+ assert_equal '123', @cache.get('booya/abcdef/[]')
34
+ assert_equal '123', result
35
+ @cache.set('booya/abcdef/[]', '234')
36
+ result = @cache_set.apply(:booya, nil, :key => 'abc def') do
37
+ '123'
38
+ end
39
+ assert_equal '234', result
40
+ end
41
+ end
@@ -0,0 +1,46 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class Hack
4
+ end
5
+
6
+ class MapperTest < Test::Unit::TestCase
7
+
8
+ def setup
9
+ @cache = CacheAdvance::CacheMock.new
10
+ @cache_set = CacheAdvance::CacheSet.new(@cache)
11
+ @mapper = CacheAdvance::Mapper.new(@cache_set)
12
+ end
13
+
14
+ def test_qualifier
15
+ @mapper.qualifier(:thirty_four) do
16
+ 34
17
+ end
18
+ assert_equal 1, @cache_set.qualifiers.size
19
+ assert_equal 34, @cache_set.qualifiers[:thirty_four].call
20
+ end
21
+
22
+ def test_plugin_from_symbol
23
+ @mapper.plugin(:hack)
24
+ assert_equal 1, @cache_set.plugins.size
25
+ assert_equal Hack, @cache_set.plugins.first.class
26
+ end
27
+
28
+ def test_plugin_from_class
29
+ @mapper.plugin(Hack)
30
+ assert_equal 1, @cache_set.plugins.size
31
+ assert_equal Hack, @cache_set.plugins.first.class
32
+ end
33
+
34
+ def test_plugin_from_object
35
+ hack = Hack.new
36
+ @mapper.plugin(hack)
37
+ assert_equal 1, @cache_set.plugins.size
38
+ assert_equal hack, @cache_set.plugins.first
39
+ end
40
+
41
+ def test_adding_caches_through_method_missing
42
+ @mapper.say_what :option => 2
43
+ assert_equal 1, @cache_set.named_caches.size
44
+ assert_equal CacheAdvance::NamedCache, @cache_set.named_caches[:say_what].class
45
+ end
46
+ end
@@ -0,0 +1,29 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class NamedCacheTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ @request = mock
7
+ end
8
+
9
+ def test_key_for_in_basic_case
10
+ create_named_cache
11
+ assert_equal 'test_cache//[]', @named_cache.send(:key_for, @request)
12
+ assert_equal 'test_cache/suf/[]', @named_cache.send(:key_for, @request, 'suf')
13
+ end
14
+
15
+ def test_key_with_qualifiers
16
+
17
+ end
18
+
19
+ protected
20
+
21
+ def create_named_cache(options={})
22
+ @name = options[:name] || 'test_cache'
23
+ @params = {}
24
+ @cache = CacheAdvance::CacheMock.new
25
+ @cache_set = CacheAdvance::CacheSet.new(@cache)
26
+ @named_cache = CacheAdvance::NamedCache.new(@name, @params, @cache_set, @cache)
27
+ end
28
+ end
29
+
@@ -0,0 +1,10 @@
1
+ $: << File.join(File.dirname(__FILE__), '..', 'lib')
2
+ $: << File.join(File.dirname(__FILE__))
3
+
4
+ require 'test/unit'
5
+ require 'rubygems'
6
+ require 'ruby-debug'
7
+
8
+ require 'cache_advance'
9
+ require 'mocha'
10
+ require 'cache_mock'
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cache_advance
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.4
5
+ platform: ruby
6
+ authors:
7
+ - Aubrey Holland
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-21 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: hmm
17
+ email: aubreyholland@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.textile
24
+ files:
25
+ - .gitignore
26
+ - CHANGELOG
27
+ - README.textile
28
+ - Rakefile
29
+ - TODO.textile
30
+ - VERSION
31
+ - cache_advance.gemspec
32
+ - lib/cache_advance.rb
33
+ - lib/cache_advance/active_record_sweeper.rb
34
+ - lib/cache_advance/cache_set.rb
35
+ - lib/cache_advance/cached_key_list.rb
36
+ - lib/cache_advance/lock.rb
37
+ - lib/cache_advance/mapper.rb
38
+ - lib/cache_advance/named_cache.rb
39
+ - lib/cache_advance/named_cache_configuration.rb
40
+ - rails/init.rb
41
+ - script/console
42
+ - test/active_record_sweeper_test.rb
43
+ - test/cache_mock.rb
44
+ - test/cache_set_test.rb
45
+ - test/key_test.rb
46
+ - test/mapper_test.rb
47
+ - test/named_cache_test.rb
48
+ - test/test_helper.rb
49
+ has_rdoc: true
50
+ homepage: http://github.com/aub/cache_advance/tree/master
51
+ licenses: []
52
+
53
+ post_install_message:
54
+ rdoc_options:
55
+ - --charset=UTF-8
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ version:
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ version:
70
+ requirements: []
71
+
72
+ rubyforge_project:
73
+ rubygems_version: 1.3.5
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: A declarative system for caching with ActiveRecord
77
+ test_files:
78
+ - test/active_record_sweeper_test.rb
79
+ - test/cache_mock.rb
80
+ - test/cache_set_test.rb
81
+ - test/key_test.rb
82
+ - test/mapper_test.rb
83
+ - test/named_cache_test.rb
84
+ - test/test_helper.rb