cells 2.3.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.
- data/CHANGES +30 -0
- data/README +150 -0
- data/README.rdoc +150 -0
- data/Rakefile +77 -0
- data/VERSION +1 -0
- data/generators/cell/USAGE +26 -0
- data/generators/cell/cell_generator.rb +48 -0
- data/generators/cell/templates/cell.rb +9 -0
- data/generators/cell/templates/view.html.erb +2 -0
- data/generators/cell/templates/view.html.haml +4 -0
- data/init.rb +59 -0
- data/lib/cell/base.rb +454 -0
- data/lib/cell/caching.rb +151 -0
- data/lib/cell/view.rb +55 -0
- data/lib/cells_helper.rb +49 -0
- data/lib/rails_extensions.rb +75 -0
- data/test/bugs_test.rb +26 -0
- data/test/caching_test.rb +266 -0
- data/test/capture_test.rb +56 -0
- data/test/cell_view_test.rb +9 -0
- data/test/cells/cells_test_one_cell.rb +20 -0
- data/test/cells/cells_test_two_cell.rb +2 -0
- data/test/cells/really_module/nested_cell.rb +11 -0
- data/test/cells/simple_cell.rb +5 -0
- data/test/cells/test_cell.rb +34 -0
- data/test/cells_test.rb +345 -0
- data/test/helper_test.rb +159 -0
- data/test/helpers/helper_using_cell_helper.rb +5 -0
- data/test/rails_extensions_test.rb +25 -0
- data/test/render_test.rb +229 -0
- data/test/testing_helper.rb +67 -0
- metadata +85 -0
data/lib/cell/caching.rb
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
# To improve performance rendered state views can be cached using Rails' caching
|
2
|
+
# mechanism.
|
3
|
+
# If this it configured (e.g. using our fast friend memcached) all you have to do is to
|
4
|
+
# tell Cells which state you want to cache. You can further attach a proc to expire the
|
5
|
+
# cached view.
|
6
|
+
#
|
7
|
+
# As always I stole a lot of code, this time from Lance Ivy <cainlevy@gmail.com> and
|
8
|
+
# his fine components plugin at http://github.com/cainlevy/components.
|
9
|
+
|
10
|
+
module Cell::Caching
|
11
|
+
|
12
|
+
def self.included(base) #:nodoc:
|
13
|
+
base.class_eval do
|
14
|
+
# mixin Cell::Base#cache, setup vars and extend #render_state if caching's on.
|
15
|
+
extend ClassMethods
|
16
|
+
|
17
|
+
return unless cache_configured?
|
18
|
+
|
19
|
+
alias_method_chain :render_state, :caching
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
module ClassMethods
|
28
|
+
# Activate caching for the state <tt>state</tt>. If no other options are passed
|
29
|
+
# the view will be cached forever.
|
30
|
+
#
|
31
|
+
# You may pass a Proc or a Symbol as cache expiration <tt>version_proc</tt>.
|
32
|
+
# This method is called every time the state is rendered, and is expected to return a
|
33
|
+
# Hash containing the cache key ingredients.
|
34
|
+
#
|
35
|
+
# Additional options will be passed directly to the cache store when caching the state.
|
36
|
+
# Useful for simply setting a TTL for a cached state.
|
37
|
+
# Note that you may omit the <tt>version_proc</tt>.
|
38
|
+
#
|
39
|
+
#
|
40
|
+
# Example:
|
41
|
+
# class CachingCell < Cell::Base
|
42
|
+
# cache :versioned_cached_state, Proc.new{ {:version => 0} }
|
43
|
+
# would result in the complete cache key
|
44
|
+
# cells/CachingCell/versioned_cached_state/version=0
|
45
|
+
#
|
46
|
+
# If you provide a symbol, you can access the cell instance directly in the versioning
|
47
|
+
# method:
|
48
|
+
#
|
49
|
+
# class CachingCell < Cell::Base
|
50
|
+
# cache :cached_state, :my_cache_version
|
51
|
+
#
|
52
|
+
# def my_cache_version
|
53
|
+
# { :user => current_user.id,
|
54
|
+
# :item_id => params[:item] }
|
55
|
+
# }
|
56
|
+
# end
|
57
|
+
# results in a very specific cache key, for customized caching:
|
58
|
+
# cells/CachingCell/cached_state/user=18/item_id=1
|
59
|
+
#
|
60
|
+
# You may also set a TTL only, e.g. when using the memcached store:
|
61
|
+
#
|
62
|
+
# cache :cached_state, :expires_in => 3.minutes
|
63
|
+
#
|
64
|
+
# Or use both, having a versioning proc <em>and</em> a TTL expiring the state as a fallback
|
65
|
+
# after a certain amount of time.
|
66
|
+
#
|
67
|
+
# cache :cached_state, Proc.new { {:version => 0} }, :expires_in => 10.minutes
|
68
|
+
#--
|
69
|
+
### TODO: implement for string, nil.
|
70
|
+
### DISCUSS: introduce return method #sweep ? so the Proc can explicitly
|
71
|
+
### delegate re-rendering to the outside.
|
72
|
+
#--
|
73
|
+
def cache(state, version_proc=nil, cache_opts={})
|
74
|
+
if version_proc.is_a?(Hash)
|
75
|
+
cache_opts = version_proc
|
76
|
+
version_proc = nil
|
77
|
+
end
|
78
|
+
|
79
|
+
version_procs[state] = version_proc
|
80
|
+
cache_options[state] = cache_opts
|
81
|
+
end
|
82
|
+
|
83
|
+
def version_procs; @version_procs ||= {}; end
|
84
|
+
def cache_options; @cache_options ||= {}; end
|
85
|
+
|
86
|
+
def cache_store #:nodoc:
|
87
|
+
@cache_store ||= ActionController::Base.cache_store
|
88
|
+
end
|
89
|
+
|
90
|
+
def cache_key_for(cell_class, state, args = {}) #:nodoc:
|
91
|
+
key_pieces = [cell_class, state]
|
92
|
+
|
93
|
+
args.collect{|a,b| [a.to_s, b]}.sort.each{ |k,v| key_pieces << "#{k}=#{v}" }
|
94
|
+
key = key_pieces.join('/')
|
95
|
+
|
96
|
+
ActiveSupport::Cache.expand_cache_key(key, :cells)
|
97
|
+
end
|
98
|
+
|
99
|
+
def expire_cache_key(key, opts=nil)
|
100
|
+
cache_store.delete(key, opts)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
|
106
|
+
def render_state_with_caching(state)
|
107
|
+
return render_state_without_caching(state) unless state_cached?(state)
|
108
|
+
|
109
|
+
key = cache_key(state, call_version_proc_for_state(state))
|
110
|
+
### DISCUSS: see sweep discussion at #cache.
|
111
|
+
|
112
|
+
# cache hit:
|
113
|
+
if content = read_fragment(key)
|
114
|
+
return content
|
115
|
+
end
|
116
|
+
# re-render:
|
117
|
+
return write_fragment(key, render_state_without_caching(state), cache_options[state])
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
def read_fragment(key, cache_options = nil) #:nodoc:
|
122
|
+
returning self.class.cache_store.read(key, cache_options) do |content|
|
123
|
+
@controller.logger.debug "Cell Cache hit: #{key}" unless content.blank?
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def write_fragment(key, content, cache_opts = nil) #:nodoc:
|
128
|
+
@controller.logger.debug "Cell Cache miss: #{key}"
|
129
|
+
self.class.cache_store.write(key, content, cache_opts)
|
130
|
+
content
|
131
|
+
end
|
132
|
+
|
133
|
+
# Call the versioning Proc for the respective state.
|
134
|
+
def call_version_proc_for_state(state)
|
135
|
+
version_proc = version_procs[state]
|
136
|
+
|
137
|
+
return {} unless version_proc # call to #cache was without any args.
|
138
|
+
|
139
|
+
return version_proc.call(self) if version_proc.kind_of? Proc
|
140
|
+
send(version_proc)
|
141
|
+
end
|
142
|
+
|
143
|
+
def cache_key(state, args = {}) #:nodoc:
|
144
|
+
self.class.cache_key_for(self.cell_name, state, args)
|
145
|
+
end
|
146
|
+
|
147
|
+
def state_cached?(state); self.class.version_procs.has_key?(state); end
|
148
|
+
def version_procs; self.class.version_procs; end
|
149
|
+
def cache_options; self.class.cache_options; end
|
150
|
+
|
151
|
+
end
|
data/lib/cell/view.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
module Cell
|
2
|
+
class View < ::ActionView::Base
|
3
|
+
|
4
|
+
attr_accessor :cell
|
5
|
+
|
6
|
+
alias_method :render_for, :render
|
7
|
+
|
8
|
+
# Tries to find the passed template in view_paths. Returns the view on success-
|
9
|
+
# otherwise it will throw an ActionView::MissingTemplate exception.
|
10
|
+
def try_picking_template_for_path(template_path)
|
11
|
+
self.view_paths.find_template(template_path, template_format)
|
12
|
+
end
|
13
|
+
|
14
|
+
### TODO: this should just be a thin helper.
|
15
|
+
### dear rails folks, could you guys please provide a helper #render and an internal #render_for
|
16
|
+
### so that we can overwrite the helper and cleanly reuse the internal method? using the same
|
17
|
+
### method both internally and externally sucks ass.
|
18
|
+
def render(options = {}, local_assigns = {}, &block)
|
19
|
+
### TODO: delegate dynamically:
|
20
|
+
### TODO: we have to find out if this is a call to the cells #render method, or to the rails
|
21
|
+
### method (e.g. when rendering a layout). what a shit.
|
22
|
+
if view = options[:view]
|
23
|
+
return cell.render_view_for(options, view)
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
# rails compatibility we should get rid of:
|
28
|
+
if partial_path = options[:partial]
|
29
|
+
# adds the cell name to the partial name.
|
30
|
+
options[:partial] = expand_view_path(partial_path)
|
31
|
+
end
|
32
|
+
#throw Exception.new
|
33
|
+
|
34
|
+
super(options, local_assigns, &block)
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def expand_view_path(path)
|
39
|
+
path = "#{cell.cell_name}/#{path}" unless path.include?('/')
|
40
|
+
path
|
41
|
+
end
|
42
|
+
|
43
|
+
# this prevents cell ivars from being overwritten by same-named
|
44
|
+
# controller ivars.
|
45
|
+
# we'll hopefully get a cleaner way, or an API, to handle this in rails 3.
|
46
|
+
def _copy_ivars_from_controller #:nodoc:
|
47
|
+
if @controller
|
48
|
+
variables = @controller.instance_variable_names
|
49
|
+
variables -= @controller.protected_instance_variables if @controller.respond_to?(:protected_instance_variables)
|
50
|
+
variables -= assigns.keys.collect {|key| "@#{key}"} # cell ivars override controller ivars.
|
51
|
+
variables.each { |name| instance_variable_set(name, @controller.instance_variable_get(name)) }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/cells_helper.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Sorry for the interface violations, but it looks as if there are
|
2
|
+
# no interfaces in rails at all.
|
3
|
+
module CellsHelper
|
4
|
+
|
5
|
+
# Executes #capture on the global ActionView and sets <tt>name</tt> as the
|
6
|
+
# instance variable name.
|
7
|
+
#
|
8
|
+
# Example:
|
9
|
+
#
|
10
|
+
# <p>
|
11
|
+
# <% global_capture :greeting do
|
12
|
+
# <h1>Hi, Nick!</h1>
|
13
|
+
# <% end %>
|
14
|
+
#
|
15
|
+
# The captured markup can be accessed in your global action view or in your layout.
|
16
|
+
#
|
17
|
+
# <%= @greeting %>
|
18
|
+
def global_capture(name, &block)
|
19
|
+
global_view = controller.instance_variable_get( "@template" )
|
20
|
+
content = capture &block
|
21
|
+
global_view.send("instance_variable_set", "@#{name}", content)
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
# Executes #content_for on the global ActionView.
|
26
|
+
#
|
27
|
+
# Example:
|
28
|
+
#
|
29
|
+
# <p>
|
30
|
+
# <% global_content_for :greetings do
|
31
|
+
# <h1>Hi, Michal!</h1>
|
32
|
+
# <% end %>
|
33
|
+
#
|
34
|
+
# As in global_capture, the markup can be accessed in your global action view or in your layout.
|
35
|
+
#
|
36
|
+
# <%= yield :greetings %>
|
37
|
+
def global_content_for(name, content = nil, &block)
|
38
|
+
# OMG.
|
39
|
+
global_view = controller.instance_variable_get( "@template" )
|
40
|
+
ivar = "@content_for_#{name}"
|
41
|
+
content = capture(&block) if block_given?
|
42
|
+
old_content = global_view.send("instance_variable_get", ivar)
|
43
|
+
|
44
|
+
|
45
|
+
global_view.send("instance_variable_set", ivar, "#{old_content}#{content}")
|
46
|
+
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# The Cells plugin defines a number of new methods for ActionView::Base. These allow
|
2
|
+
# you to render cells from within normal controller views as well as from Cell state views.
|
3
|
+
module Cell
|
4
|
+
|
5
|
+
module ActionView
|
6
|
+
# Call a cell state and return its rendered view.
|
7
|
+
#
|
8
|
+
# ERB example:
|
9
|
+
# <div id="login">
|
10
|
+
# <%= render_cell :user, :login_prompt, :message => "Please login" %>
|
11
|
+
# </div>
|
12
|
+
#
|
13
|
+
# If you have a <tt>UserCell</tt> cell in <tt>app/cells/user_cell.rb</tt>, which has a
|
14
|
+
# <tt>UserCell#login_prompt</tt> method, this will call that method and then will
|
15
|
+
# find the view <tt>app/cells/user/login_prompt.html.erb</tt> and render it. This is
|
16
|
+
# called the <tt>:login_prompt</tt> <em>state</em> in Cells terminology.
|
17
|
+
#
|
18
|
+
# If this view file looks like this:
|
19
|
+
# <h1><%= @opts[:message] %></h1>
|
20
|
+
# <label>name: <input name="user[name]" /></label>
|
21
|
+
# <label>password: <input name="user[password]" /></label>
|
22
|
+
#
|
23
|
+
# The resulting view in the controller will be roughly equivalent to:
|
24
|
+
# <div id="login">
|
25
|
+
# <h1><%= "Please login" %></h1>
|
26
|
+
# <label>name: <input name="user[name]" /></label>
|
27
|
+
# <label>password: <input name="user[password]" /></label>
|
28
|
+
# </div>
|
29
|
+
def render_cell(name, state, opts = {})
|
30
|
+
cell = Cell::Base.create_cell_for(@controller, name, opts)
|
31
|
+
cell.render_state(state)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
# These ControllerMethods are automatically added to all Controllers when
|
37
|
+
# the cells plugin is loaded.
|
38
|
+
module ActionController
|
39
|
+
|
40
|
+
# Equivalent to ActionController#render_to_string, except it renders a cell
|
41
|
+
# rather than a regular templates.
|
42
|
+
def render_cell(name, state, opts={})
|
43
|
+
cell = Cell::Base.create_cell_for(self, name, opts)
|
44
|
+
|
45
|
+
return cell.render_state(state)
|
46
|
+
end
|
47
|
+
|
48
|
+
alias_method :render_cell_to_string, :render_cell # just for backward compatibility.
|
49
|
+
|
50
|
+
|
51
|
+
# Expires the cached cell state view, similar to ActionController::expire_fragment.
|
52
|
+
# Usually, this method is used in Sweepers.
|
53
|
+
# Beside the obvious first two args <tt>cell_name</tt> and <tt>state</tt> you can pass
|
54
|
+
# in additional cache key <tt>args</tt> and cache store specific <tt>opts</tt>.
|
55
|
+
#
|
56
|
+
# Example:
|
57
|
+
#
|
58
|
+
# class ListSweeper < ActionController::Caching::Sweeper
|
59
|
+
# observe List, Item
|
60
|
+
#
|
61
|
+
# def after_save(record)
|
62
|
+
# expire_cell_state :my_listing, :display_list
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
# will expire the view for state <tt>:display_list</tt> in the cell <tt>MyListingCell</tt>.
|
66
|
+
def expire_cell_state(cell_name, state, args={}, opts=nil)
|
67
|
+
key = Cell::Base.cache_key_for(cell_name, state, args)
|
68
|
+
Cell::Base.expire_cache_key(key, opts)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
|
data/test/bugs_test.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../../../test/test_helper'
|
2
|
+
require File.dirname(__FILE__) + '/testing_helper'
|
3
|
+
|
4
|
+
|
5
|
+
class MyTestCell < Cell::Base
|
6
|
+
def state_with_instance_var
|
7
|
+
@my_ivar = "value from cell"
|
8
|
+
render
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
class CellsTest < ActionController::TestCase
|
14
|
+
include CellsTestMethods
|
15
|
+
|
16
|
+
def test_controller_overriding_cell_ivars
|
17
|
+
@controller.class_eval do
|
18
|
+
attr_accessor :my_ivar
|
19
|
+
end
|
20
|
+
@controller.my_ivar = "value from controller"
|
21
|
+
|
22
|
+
cell = MyTestCell.new(@controller)
|
23
|
+
c = cell.render_state(:state_with_instance_var)
|
24
|
+
assert_equal "value from cell", c
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,266 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../../../test/test_helper'
|
2
|
+
require File.dirname(__FILE__) + '/testing_helper'
|
3
|
+
|
4
|
+
# usually done by rails' autoloading:
|
5
|
+
require File.dirname(__FILE__) + '/cells/test_cell'
|
6
|
+
|
7
|
+
### NOTE: add
|
8
|
+
### config.action_controller.cache_store = :memory_store
|
9
|
+
### config.action_controller.perform_caching = true
|
10
|
+
### to config/environments/test.rb to make this tests work.
|
11
|
+
|
12
|
+
class CellsCachingTest < Test::Unit::TestCase
|
13
|
+
include CellsTestMethods
|
14
|
+
|
15
|
+
def setup
|
16
|
+
super
|
17
|
+
@controller.session= {}
|
18
|
+
@cc = CachingCell.new(@controller)
|
19
|
+
@c2 = AnotherCachingCell.new(@controller)
|
20
|
+
end
|
21
|
+
|
22
|
+
#def self.path_to_test_views
|
23
|
+
# RAILS_ROOT + "/vendor/plugins/cells/test/views/"
|
24
|
+
#end
|
25
|
+
|
26
|
+
|
27
|
+
def test_state_cached?
|
28
|
+
assert @cc.state_cached?(:cached_state)
|
29
|
+
assert ! @cc.state_cached?(:not_cached_state)
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_cache_without_options
|
33
|
+
# :cached_state is cached without any options:
|
34
|
+
assert_nil @cc.version_procs[:cached_state]
|
35
|
+
assert_nil @cc.version_procs[:not_cached_state]
|
36
|
+
|
37
|
+
# cache_options must at least return an empty hash for a cached state:
|
38
|
+
assert_equal( {}, @cc.cache_options[:cached_state])
|
39
|
+
assert_nil @cc.cache_options[:not_cached_state]
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
def test_cache_with_proc_only
|
44
|
+
CachingCell.class_eval do
|
45
|
+
cache :my_state, Proc.new {}
|
46
|
+
end
|
47
|
+
|
48
|
+
assert_kind_of Proc, @cc.version_procs[:my_state]
|
49
|
+
assert_equal( {}, @cc.cache_options[:my_state])
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def test_cache_with_proc_and_cache_options
|
54
|
+
CachingCell.class_eval do
|
55
|
+
cache :my_state, Proc.new{}, {:expires_in => 10.seconds}
|
56
|
+
end
|
57
|
+
|
58
|
+
assert_kind_of Proc, @cc.version_procs[:my_state]
|
59
|
+
assert_equal( {:expires_in => 10.seconds}, @cc.cache_options[:my_state])
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
def test_cache_with_cache_options_only
|
64
|
+
CachingCell.class_eval do
|
65
|
+
cache :my_state, :expires_in => 10.seconds
|
66
|
+
end
|
67
|
+
|
68
|
+
assert @cc.version_procs.has_key?(:my_state)
|
69
|
+
assert_nil @cc.version_procs[:my_state]
|
70
|
+
assert_equal( {:expires_in => 10.seconds}, @cc.cache_options[:my_state])
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
def test_if_caching_works
|
76
|
+
c = @cc.render_state(:cached_state)
|
77
|
+
assert_equal c, "1 should remain the same forever!"
|
78
|
+
|
79
|
+
c = @cc.render_state(:cached_state)
|
80
|
+
assert_equal c, "1 should remain the same forever!", ":cached_state was invoked again"
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_cache_key
|
84
|
+
assert_equal "cells/caching/some_state", @cc.cache_key(:some_state)
|
85
|
+
assert_equal @cc.cache_key(:some_state), Cell::Base.cache_key_for(:caching, :some_state)
|
86
|
+
assert_equal "cells/caching/some_state/param=9", @cc.cache_key(:some_state, :param => 9)
|
87
|
+
assert_equal "cells/caching/some_state/a=1/b=2", @cc.cache_key(:some_state, :b => 2, :a => 1)
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_render_state_without_caching
|
91
|
+
c = @cc.render_state(:not_cached_state)
|
92
|
+
assert_equal c, "i'm really static"
|
93
|
+
c = @cc.render_state(:not_cached_state)
|
94
|
+
assert_equal c, "i'm really static"
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_caching_with_version_proc
|
98
|
+
@controller.session[:version] = 0
|
99
|
+
# render state, as it's not cached:
|
100
|
+
c = @cc.render_state(:versioned_cached_state)
|
101
|
+
assert_equal c, "0 should change every third call!"
|
102
|
+
|
103
|
+
@controller.session[:version] = -1
|
104
|
+
c = @cc.render_state(:versioned_cached_state)
|
105
|
+
assert_equal c, "0 should change every third call!"
|
106
|
+
|
107
|
+
|
108
|
+
@controller.session[:version] = 1
|
109
|
+
c = @cc.render_state(:versioned_cached_state)
|
110
|
+
assert_equal c, "1 should change every third call!"
|
111
|
+
|
112
|
+
|
113
|
+
@controller.session[:version] = 2
|
114
|
+
c = @cc.render_state(:versioned_cached_state)
|
115
|
+
assert_equal c, "2 should change every third call!"
|
116
|
+
|
117
|
+
@controller.session[:version] = 3
|
118
|
+
c = @cc.render_state(:versioned_cached_state)
|
119
|
+
assert_equal c, "3 should change every third call!"
|
120
|
+
end
|
121
|
+
|
122
|
+
def test_caching_with_instance_version_proc
|
123
|
+
CachingCell.class_eval do
|
124
|
+
cache :versioned_cached_state, :my_version_proc
|
125
|
+
end
|
126
|
+
@controller.session[:version] = 0
|
127
|
+
c = @cc.render_state(:versioned_cached_state)
|
128
|
+
assert_equal c, "0 should change every third call!"
|
129
|
+
|
130
|
+
@controller.session[:version] = 1
|
131
|
+
c = @cc.render_state(:versioned_cached_state)
|
132
|
+
assert_equal c, "1 should change every third call!"
|
133
|
+
end
|
134
|
+
|
135
|
+
def test_caching_with_two_same_named_states
|
136
|
+
c = @cc.render_state(:cheers)
|
137
|
+
assert_equal c, "cheers!"
|
138
|
+
c = @c2.render_state(:cheers)
|
139
|
+
assert_equal c, "prost!"
|
140
|
+
c = @cc.render_state(:cheers)
|
141
|
+
assert_equal c, "cheers!"
|
142
|
+
c = @c2.render_state(:cheers)
|
143
|
+
assert_equal c, "prost!"
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_caching_one_of_two_same_named_states
|
147
|
+
### DISCUSS with drogus: the problem was that CachingCell and AnotherCachingCell keep
|
148
|
+
### overwriting their version_procs, wasn't it? why don't we test that with different
|
149
|
+
### version_procs in each cell?
|
150
|
+
@cc = CachingCell.new(@controller, :str => "foo1")
|
151
|
+
c = @cc.render_state(:another_state)
|
152
|
+
assert_equal c, "foo1"
|
153
|
+
|
154
|
+
@c2 = AnotherCachingCell.new(@controller, :str => "foo2")
|
155
|
+
c = @c2.render_state(:another_state)
|
156
|
+
assert_equal c, "foo2"
|
157
|
+
|
158
|
+
@cc = CachingCell.new(@controller, :str => "bar1")
|
159
|
+
c = @cc.render_state(:another_state)
|
160
|
+
assert_equal c, "foo1"
|
161
|
+
|
162
|
+
@c2 = AnotherCachingCell.new(@controller, :str => "bar2")
|
163
|
+
c = @c2.render_state(:another_state)
|
164
|
+
assert_equal c, "bar2"
|
165
|
+
end
|
166
|
+
|
167
|
+
def test_expire_cache_key
|
168
|
+
k = @cc.cache_key(:cached_state)
|
169
|
+
@cc.render_state(:cached_state)
|
170
|
+
assert Cell::Base.cache_store.read(k)
|
171
|
+
Cell::Base.expire_cache_key(k)
|
172
|
+
assert ! Cell::Base.cache_store.read(k)
|
173
|
+
|
174
|
+
# test via ActionController::expire_cell_state, which is called from Sweepers.
|
175
|
+
@cc.render_state(:cached_state)
|
176
|
+
assert Cell::Base.cache_store.read(k)
|
177
|
+
@controller.expire_cell_state(:caching, :cached_state)
|
178
|
+
assert ! Cell::Base.cache_store.read(k)
|
179
|
+
|
180
|
+
# ..and additionally test if passing cache key args works:
|
181
|
+
k = @cc.cache_key(:cached_state, :more => :yes)
|
182
|
+
assert Cell::Base.cache_store.write(k, "test content")
|
183
|
+
@controller.expire_cell_state(:caching, :cached_state, :more => :yes)
|
184
|
+
assert ! Cell::Base.cache_store.read(k)
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
def test_find_family_view_for_state_with_caching
|
189
|
+
# test environment: --------------------------------------
|
190
|
+
assert_equal({}, ACell.state2view_cache)
|
191
|
+
|
192
|
+
a = ACell.new(@controller)
|
193
|
+
a.class.instance_eval do
|
194
|
+
def cache_configured?; false; end
|
195
|
+
end
|
196
|
+
a.render_state :existing_view
|
197
|
+
# in development/test environment, no view name caching should happen,
|
198
|
+
# if perform_caching is false.
|
199
|
+
assert_equal({}, ACell.state2view_cache)
|
200
|
+
|
201
|
+
# production environment: --------------------------------
|
202
|
+
a = ACell.new(@controller)
|
203
|
+
a.class.instance_eval do
|
204
|
+
def cache_configured?; true; end
|
205
|
+
end
|
206
|
+
a.render_state :existing_view
|
207
|
+
assert ACell.state2view_cache.has_key?("existing_view/html")
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
class CachingCell < Cell::Base
|
212
|
+
|
213
|
+
cache :cached_state
|
214
|
+
|
215
|
+
def cached_state
|
216
|
+
cnt = controller.session[:cache_count]
|
217
|
+
cnt ||= 0
|
218
|
+
cnt += 1
|
219
|
+
"#{cnt} should remain the same forever!"
|
220
|
+
end
|
221
|
+
|
222
|
+
def not_cached_state
|
223
|
+
"i'm really static"
|
224
|
+
end
|
225
|
+
|
226
|
+
cache :versioned_cached_state, Proc.new { |cell|
|
227
|
+
if (v = cell.session[:version]) > 0
|
228
|
+
{:version=>v}
|
229
|
+
else
|
230
|
+
{:version=>0}; end
|
231
|
+
}
|
232
|
+
def versioned_cached_state
|
233
|
+
"#{session[:version].inspect} should change every third call!"
|
234
|
+
end
|
235
|
+
|
236
|
+
|
237
|
+
def my_version_proc
|
238
|
+
if (v = session[:version]) > 0
|
239
|
+
{:version=>v}
|
240
|
+
else
|
241
|
+
{:version=>0}; end
|
242
|
+
end
|
243
|
+
#def cached_state_with_symbol_proc
|
244
|
+
#
|
245
|
+
#end
|
246
|
+
cache :cheers
|
247
|
+
def cheers
|
248
|
+
"cheers!"
|
249
|
+
end
|
250
|
+
|
251
|
+
cache :another_state
|
252
|
+
def another_state
|
253
|
+
@opts[:str]
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
class AnotherCachingCell < Cell::Base
|
258
|
+
cache :cheers
|
259
|
+
def cheers
|
260
|
+
"prost!"
|
261
|
+
end
|
262
|
+
|
263
|
+
def another_state
|
264
|
+
@opts[:str]
|
265
|
+
end
|
266
|
+
end
|