cache_fu 0.1.2

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.
@@ -0,0 +1,30 @@
1
+ module ActsAsCached
2
+ module Disabled
3
+ def fetch_cache_with_disabled(*args)
4
+ nil
5
+ end
6
+
7
+ def set_cache_with_disabled(*args)
8
+ args[1]
9
+ end
10
+
11
+ def expire_cache_with_disabled(*args)
12
+ true
13
+ end
14
+
15
+ def self.add_to(klass)
16
+ return if klass.respond_to? :fetch_cache_with_disabled
17
+ klass.extend self
18
+
19
+ class << klass
20
+ alias_method_chain :fetch_cache, :disabled
21
+ alias_method_chain :set_cache, :disabled
22
+ alias_method_chain :expire_cache, :disabled
23
+ end
24
+
25
+ class << CACHE
26
+ include FragmentCache::DisabledExtensions
27
+ end if ActsAsCached.config[:fragments] && defined?(FragmentCache::DisabledExtensions)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,102 @@
1
+ module ActsAsCached
2
+ module FragmentCache
3
+ def self.setup!
4
+ class << CACHE
5
+ include Extensions
6
+ end
7
+
8
+ setup_fragment_cache_cache
9
+ setup_rails_for_memcache_fragments
10
+ setup_rails_for_action_cache_options
11
+ end
12
+
13
+ # add :ttl option to cache helper and set cache store memcache object
14
+ def self.setup_rails_for_memcache_fragments
15
+ ::ActionController::Base.cache_store = CACHE
16
+ end
17
+
18
+ def self.setup_fragment_cache_cache
19
+ Object.const_set(:FragmentCacheCache, Class.new { acts_as_cached :store => CACHE })
20
+ end
21
+
22
+ # add :ttl option to caches_action on the per action level by passing in a hash instead of an array
23
+ #
24
+ # Examples:
25
+ # caches_action :index # will use the default ttl from your memcache.yml, or 25 minutes
26
+ # caches_action :index => { :ttl => 5.minutes } # cache index action with 5 minute ttl
27
+ # caches_action :page, :feed, :index => { :ttl => 2.hours } # cache index action with 2 hours ttl, all others use default
28
+ #
29
+ def self.setup_rails_for_action_cache_options
30
+ ::ActionController::Caching::Actions::ActionCacheFilter.class_eval do
31
+ # convert all actions into a hash keyed by action named, with a value of a ttl hash (to match other cache APIs)
32
+ def initialize(*actions, &block)
33
+ @options = actions.extract_options!
34
+ @actions = actions.inject(@options.except(:cache_path)) do |hsh, action|
35
+ action.is_a?(Hash) ? hsh.merge(action) : hsh.merge(action => { :ttl => nil })
36
+ end
37
+ @options.slice!(:cache_path)
38
+ end
39
+
40
+ # override to skip caching/rendering on evaluated if option
41
+ def before(controller)
42
+ return unless @actions.include?(controller.action_name.intern)
43
+
44
+ if @options
45
+ action_cache_path = ActionController::Caching::Actions::ActionCachePath.new(controller, path_options_for(controller, @options))
46
+ end
47
+
48
+ # should probably be like ActiveRecord::Validations.evaluate_condition. color me lazy.
49
+ if conditional = @actions[controller.action_name.intern][:if]
50
+ conditional = conditional.respond_to?(:call) ? conditional.call(controller) : controller.send(conditional)
51
+ end
52
+ @actions.delete(controller.action_name.intern) if conditional == false
53
+
54
+ cache = controller.read_fragment(action_cache_path.path)
55
+ if cache && (conditional || conditional.nil?)
56
+ controller.rendered_action_cache = true
57
+ if method(:set_content_type!).arity == 2
58
+ set_content_type!(controller, action_cache_path.extension)
59
+ else
60
+ set_content_type!(action_cache_path)
61
+ end
62
+ controller.send(:render, :text => cache)
63
+ false
64
+ else
65
+ # 1.2.x compatibility
66
+ controller.action_cache_path = action_cache_path if controller.respond_to? :action_cache_path
67
+ end
68
+ end
69
+
70
+ # override to pass along the ttl hash
71
+ def after(controller)
72
+ return if !@actions.include?(controller.action_name.intern) || controller.rendered_action_cache
73
+ # 1.2.x compatibility
74
+ path = controller.respond_to?(:action_cache_path) ? controller.action_cache_path.path : ActionController::Caching::Actions::ActionCachePath.path_for(controller)
75
+ controller.write_fragment(path, controller.response.body, action_ttl(controller))
76
+ end
77
+
78
+ private
79
+ def action_ttl(controller)
80
+ @actions[controller.action_name.intern]
81
+ end
82
+ end
83
+ end
84
+
85
+ module Extensions
86
+ def read(*args)
87
+ return if ActsAsCached.config[:skip_gets]
88
+ FragmentCacheCache.cache_store(:get, args.first)
89
+ end
90
+
91
+ def write(name, content, options = {})
92
+ ttl = (options.is_a?(Hash) ? options[:ttl] : nil) || ActsAsCached.config[:ttl] || 25.minutes
93
+ FragmentCacheCache.cache_store(:set, name, content, ttl)
94
+ end
95
+ end
96
+
97
+ module DisabledExtensions
98
+ def read(*args) nil end
99
+ def write(*args) "" end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,44 @@
1
+ module ActsAsCached
2
+ module LocalCache
3
+ @@local_cache = {}
4
+ mattr_accessor :local_cache
5
+
6
+ def fetch_cache_with_local_cache(*args)
7
+ @@local_cache[cache_key(args.first)] ||= fetch_cache_without_local_cache(*args)
8
+ end
9
+
10
+ def set_cache_with_local_cache(*args)
11
+ @@local_cache[cache_key(args.first)] = set_cache_without_local_cache(*args)
12
+ end
13
+
14
+ def expire_cache_with_local_cache(*args)
15
+ @@local_cache.delete(cache_key(args.first))
16
+ expire_cache_without_local_cache(*args)
17
+ end
18
+ alias :clear_cache_with_local_cache :expire_cache_with_local_cache
19
+
20
+ def cached_with_local_cache?(*args)
21
+ !!@@local_cache[cache_key(args.first)] || cached_without_local_cache?(*args)
22
+ end
23
+
24
+ def self.add_to(klass)
25
+ return if klass.ancestors.include? self
26
+ klass.send :include, self
27
+
28
+ klass.class_eval do
29
+ %w( fetch_cache set_cache expire_cache clear_cache cached? ).each do |target|
30
+ alias_method_chain target, :local_cache
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ module ActionController
38
+ class Base
39
+ def local_cache_for_request
40
+ ActsAsCached::LocalCache.add_to ActsAsCached::ClassMethods
41
+ ActsAsCached::LocalCache.local_cache = {}
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,13 @@
1
+ require 'cache_fu'
2
+ require 'rails'
3
+
4
+ module ActsAsCached
5
+ class Railtie < Rails::Railtie
6
+ initializer 'cache_fu:extends' do
7
+ end
8
+
9
+ rake_tasks do
10
+ load 'tasks/memcached.rb'
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ Capistrano.configuration(:must_exist).load do
2
+ %w(start stop restart kill status).each do |cmd|
3
+ desc "#{cmd} your memcached servers"
4
+ task "memcached_#{cmd}".to_sym, :roles => :app do
5
+ run "RAILS_ENV=production #{ruby} #{current_path}/script/memcached_ctl #{cmd}"
6
+ end
7
+ end
8
+ end
data/lib/cache_fu.rb ADDED
@@ -0,0 +1,62 @@
1
+ require File.dirname(__FILE__) + '/acts_as_cached/config'
2
+ require File.dirname(__FILE__) + '/acts_as_cached/cache_methods'
3
+ require File.dirname(__FILE__) + '/acts_as_cached/fragment_cache'
4
+ require File.dirname(__FILE__) + '/acts_as_cached/benchmarking'
5
+ require File.dirname(__FILE__) + '/acts_as_cached/disabled'
6
+ require File.dirname(__FILE__) + '/acts_as_cached/local_cache'
7
+ require File.dirname(__FILE__) + '/acts_as_cached/railtie' if defined?(Rails::Railtie)
8
+
9
+ module ActsAsCached
10
+ @@config = {}
11
+ mattr_reader :config
12
+
13
+ def self.config=(options)
14
+ @@config = Config.setup options
15
+ end
16
+
17
+ def self.skip_cache_gets=(boolean)
18
+ ActsAsCached.config[:skip_gets] = boolean
19
+ end
20
+
21
+ module Mixin
22
+ def acts_as_cached(options = {})
23
+ extend ClassMethods
24
+ include InstanceMethods
25
+
26
+ extend Extensions::ClassMethods if defined? Extensions::ClassMethods
27
+ include Extensions::InstanceMethods if defined? Extensions::InstanceMethods
28
+
29
+ options.symbolize_keys!
30
+
31
+ options[:store] ||= ActsAsCached.config[:store]
32
+ options[:ttl] ||= ActsAsCached.config[:ttl]
33
+
34
+ # convert the find_by shorthand
35
+ if find_by = options.delete(:find_by)
36
+ options[:finder] = "find_by_#{find_by}".to_sym
37
+ options[:cache_id] = find_by
38
+ end
39
+
40
+ cache_config.replace options.reject { |key,| not Config.valued_keys.include? key }
41
+ cache_options.replace options.reject { |key,| Config.valued_keys.include? key }
42
+
43
+ Disabled.add_to self and return if ActsAsCached.config[:disabled]
44
+ Benchmarking.add_to self if ActsAsCached.config[:benchmarking]
45
+ end
46
+ end
47
+
48
+ class CacheException < StandardError; end
49
+ class NoCacheStore < CacheException; end
50
+ class NoGetMulti < CacheException; end
51
+ end
52
+
53
+ Rails::Application.initializer("cache_fu") do
54
+ Object.send :include, ActsAsCached::Mixin
55
+ unless File.exists?(config_file = Rails.root.join('config', 'memcached.yml'))
56
+ error = "No config file found. If you used plugin version make sure you used `script/plugin install' or `rake memcached:cache_fu_install' if gem version and have memcached.yml in your config directory."
57
+ puts error
58
+ logger.error error
59
+ exit!
60
+ end
61
+ ActsAsCached.config = YAML.load(ERB.new(IO.read(config_file)).result)
62
+ end
data/lib/cache_fu.rb~ ADDED
@@ -0,0 +1,61 @@
1
+ require File.dirname(__FILE__) + '/acts_as_cached/config'
2
+ require File.dirname(__FILE__) + '/acts_as_cached/cache_methods'
3
+ require File.dirname(__FILE__) + '/acts_as_cached/fragment_cache'
4
+ require File.dirname(__FILE__) + '/acts_as_cached/benchmarking'
5
+ require File.dirname(__FILE__) + '/acts_as_cached/disabled'
6
+ require File.dirname(__FILE__) + '/acts_as_cached/local_cache'
7
+ require File.dirname(__FILE__) + '/acts_as_cached/railtie' if defined?(Rails::Railtie)
8
+
9
+ module ActsAsCached
10
+ @@config = {}
11
+ mattr_reader :config
12
+
13
+ def self.config=(options)
14
+ @@config = Config.setup options
15
+ end
16
+
17
+ def self.skip_cache_gets=(boolean)
18
+ ActsAsCached.config[:skip_gets] = boolean
19
+ end
20
+
21
+ module Mixin
22
+ def acts_as_cached(options = {})
23
+ extend ClassMethods
24
+ include InstanceMethods
25
+
26
+ extend Extensions::ClassMethods if defined? Extensions::ClassMethods
27
+ include Extensions::InstanceMethods if defined? Extensions::InstanceMethods
28
+
29
+ options.symbolize_keys!
30
+
31
+ options[:store] ||= ActsAsCached.config[:store]
32
+ options[:ttl] ||= ActsAsCached.config[:ttl]
33
+
34
+ # convert the find_by shorthand
35
+ if find_by = options.delete(:find_by)
36
+ options[:finder] = "find_by_#{find_by}".to_sym
37
+ options[:cache_id] = find_by
38
+ end
39
+
40
+ cache_config.replace options.reject { |key,| not Config.valued_keys.include? key }
41
+ cache_options.replace options.reject { |key,| Config.valued_keys.include? key }
42
+
43
+ Disabled.add_to self and return if ActsAsCached.config[:disabled]
44
+ Benchmarking.add_to self if ActsAsCached.config[:benchmarking]
45
+ end
46
+ end
47
+
48
+ class CacheException < StandardError; end
49
+ class NoCacheStore < CacheException; end
50
+ class NoGetMulti < CacheException; end
51
+ end
52
+
53
+ Object.send :include, ActsAsCached::Mixin
54
+ unless File.exists?(config_file = Rails.root.join('config', 'memcached.yml'))
55
+ error = "No config file found. If you used plugin version make sure you used `script/plugin install' or `rake memcached:cache_fu_install' if gem version and have memcached.yml in your config directory."
56
+ puts error
57
+ logger.error error
58
+ exit!
59
+ end
60
+
61
+ ActsAsCached.config = YAML.load(ERB.new(IO.read(config_file)).result)
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + '/acts_as_cached'
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + '/acts_as_cached'
@@ -0,0 +1,59 @@
1
+ require 'yaml'
2
+ require 'erb'
3
+
4
+ namespace :memcached do
5
+ desc "Start memcached locally"
6
+ task :start do
7
+ ActsAsCached::Config.memcached ActsAsCached::Config.config_args
8
+ puts "memcached started"
9
+ end
10
+
11
+ desc "Restart memcached locally"
12
+ task :restart do
13
+ Rake::Task['memcached:stop'].invoke
14
+ Rake::Task['memcached:start'].invoke
15
+ end
16
+
17
+ desc "Stop memcached locally"
18
+ task :stop do
19
+ `killall memcached`
20
+ puts "memcached killed"
21
+ end
22
+
23
+ desc "Adds the cache_fu config file"
24
+ task :cache_fu_install do
25
+ defaults_dir = File.join(File.dirname(__FILE__), '../../defaults')
26
+
27
+ config_yaml = File.join('.', 'config', 'memcached.yml')
28
+ default_yaml = File.join(defaults_dir, 'memcached.yml.default')
29
+ FileUtils.cp(default_yaml, config_yaml)
30
+
31
+ memcached_ctl = File.join('.', 'script', 'memcached_ctl')
32
+ default_ctl = File.join(defaults_dir, 'memcached_ctl.default')
33
+ FileUtils.cp(default_ctl, memcached_ctl)
34
+ end
35
+ end
36
+
37
+ module ActsAsCached
38
+ module Config
39
+ def self.config
40
+ return @config if @config
41
+ config = YAML.load(ERB.new(IO.read(Rails.root.to_s + '/config/memcached.yml')).result)
42
+ @config = config['defaults'].merge(config['development'])
43
+ end
44
+
45
+ def self.config_args
46
+ args = {
47
+ '-p' => Array(config['servers']).first.split(':').last,
48
+ '-c' => config['c_threshold'],
49
+ '-m' => config['memory'],
50
+ '-d' => ''
51
+ }
52
+ args.to_a * ' '
53
+ end
54
+
55
+ def self.memcached(*args)
56
+ `/usr/bin/env memcached #{args * ' '}`
57
+ end
58
+ end
59
+ end
data/rails/init.rb ADDED
@@ -0,0 +1,34 @@
1
+ begin
2
+ require 'memcache'
3
+ rescue LoadError
4
+ end
5
+
6
+ begin
7
+ require 'memcached'
8
+ rescue LoadError
9
+ end
10
+
11
+ begin
12
+ require 'mem_cache_with_consistent_hashing'
13
+ rescue LoadError
14
+ end
15
+
16
+ puts "=> You should be using the `dalli' or `memcache-client' gem. You're using RubyMemcache!" if Object.const_defined?(:RubyMemcache)
17
+
18
+ require 'cache_fu'
19
+
20
+ Object.send :include, ActsAsCached::Mixin
21
+
22
+ unless File.exists?(config_file = Rails.root.join('config', 'memcached.yml'))
23
+ error = "No config file found. Make sure you used `script/plugin install' and have memcached.yml in your config directory."
24
+ puts error
25
+ logger.error error
26
+ exit!
27
+ end
28
+
29
+ ActsAsCached.config = YAML.load(ERB.new(IO.read(config_file)).result)
30
+
31
+ begin
32
+ require 'extensions'
33
+ rescue LoadError
34
+ end
@@ -0,0 +1,36 @@
1
+ require File.join(File.dirname(__FILE__), 'helper')
2
+
3
+ ActsAsCached.config.clear
4
+ config = YAML.load_file(File.join(File.dirname(__FILE__), '../defaults/memcached.yml.default'))
5
+ config['test'] = config['development']
6
+ ActsAsCached.config = config
7
+ Story.send :acts_as_cached
8
+
9
+ context "When benchmarking is enabled" do
10
+ specify "ActionController::Base should respond to rendering_runtime_with_memcache" do
11
+ ActionController::Base.new.should.respond_to :rendering_runtime_with_memcache
12
+ end
13
+
14
+ specify "cachable Ruby classes should be respond to :logger" do
15
+ Story.should.respond_to :logger
16
+ end
17
+
18
+ specify "a cached object should gain a fetch_cache with and without benchmarking methods" do
19
+ Story.should.respond_to :fetch_cache_with_benchmarking
20
+ Story.should.respond_to :fetch_cache_without_benchmarking
21
+ end
22
+
23
+ specify "cache_benchmark should yield and time any action" do
24
+ ActsAsCached::Benchmarking.cache_runtime.should.equal 0.0
25
+
26
+ level = Class.new { |k| def k.method_missing(*args) true end }
27
+ Story.stubs(:logger).returns(level)
28
+
29
+ Story.cache_benchmark("Seriously, nothing.", true) {
30
+ sleep 0.01
31
+ "Nothing."
32
+ }.should.equal "Nothing."
33
+
34
+ ActsAsCached::Benchmarking.cache_runtime.should.be > 0.0
35
+ end
36
+ end