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/init.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# Copyright (c) 2007-2009 Nick Sutterer <apotonick@gmail.com>
|
2
|
+
# Copyright (c) 2007-2008 Solide ICT by Peter Bex <peter.bex@solide-ict.nl>
|
3
|
+
# and Bob Leers <bleers@fastmail.fm>
|
4
|
+
# Some portions and ideas stolen ruthlessly from Ezra Zygmuntowicz <ezmobius@gmail.com>
|
5
|
+
#
|
6
|
+
# The MIT License
|
7
|
+
#
|
8
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
9
|
+
# of this software and associated documentation files (the "Software"), to deal
|
10
|
+
# in the Software without restriction, including without limitation the rights
|
11
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
12
|
+
# copies of the Software, and to permit persons to whom the Software is
|
13
|
+
# furnished to do so, subject to the following conditions:
|
14
|
+
#
|
15
|
+
# The above copyright notice and this permission notice shall be included in
|
16
|
+
# all copies or substantial portions of the Software.
|
17
|
+
#
|
18
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
19
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
20
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
21
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
22
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
23
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
24
|
+
# THE SOFTWARE.
|
25
|
+
|
26
|
+
# load the baby:
|
27
|
+
Cell::Base
|
28
|
+
require 'rails_extensions'
|
29
|
+
|
30
|
+
|
31
|
+
ActionController::Base.class_eval do include Cell::ActionController end
|
32
|
+
ActionView::Base.class_eval do include Cell::ActionView end
|
33
|
+
Cell::Base.class_eval do include Cell::Caching end
|
34
|
+
|
35
|
+
|
36
|
+
ActiveSupport::Dependencies.load_paths << RAILS_ROOT+"/app/cells"
|
37
|
+
Cell::Base.add_view_path "app/cells"
|
38
|
+
### DISCUSS: do we need shared layouts for different cells?
|
39
|
+
Cell::Base.add_view_path "app/cells/layouts"
|
40
|
+
|
41
|
+
|
42
|
+
# process cells in plugins ("engine-cells").
|
43
|
+
# thanks to Tore Torell for making me aware of the initializer instance here:
|
44
|
+
config.after_initialize do
|
45
|
+
initializer.loaded_plugins.each do |plugin|
|
46
|
+
engine_cells_dir = File.join([plugin.directory, "app/cells"])
|
47
|
+
next unless plugin.engine?
|
48
|
+
next unless File.exists?(engine_cells_dir)
|
49
|
+
|
50
|
+
# propagate the view- and code path of this engine-cell:
|
51
|
+
Cell::Base.view_paths << engine_cells_dir
|
52
|
+
ActiveSupport::Dependencies.load_paths << engine_cells_dir
|
53
|
+
|
54
|
+
# if a path is in +load_once_path+ it won't be reloaded between requests.
|
55
|
+
unless config.reload_plugins?
|
56
|
+
ActiveSupport::Dependencies.load_once_paths << engine_cells_dir
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/cell/base.rb
ADDED
@@ -0,0 +1,454 @@
|
|
1
|
+
module Cell
|
2
|
+
# == Basic overview
|
3
|
+
#
|
4
|
+
# A Cell is the central notion of the cells plugin. A cell acts as a
|
5
|
+
# lightweight controller in the sense that it will assign variables and
|
6
|
+
# render a view. Cells can be rendered from other cells as well as from
|
7
|
+
# regular controllers and views (see ActionView::Base#render_cell and
|
8
|
+
# ControllerMethods#render_cell)
|
9
|
+
#
|
10
|
+
# == A render_cell() cycle
|
11
|
+
#
|
12
|
+
# A typical <tt>render_cell</tt> state rendering cycle looks like this:
|
13
|
+
# render_cell :blog, :newest_article, {...}
|
14
|
+
# - an instance of the class <tt>BlogCell</tt> is created, and a hash containing
|
15
|
+
# arbitrary parameters is passed
|
16
|
+
# - the <em>state method</em> <tt>newest_article</tt> is executed and assigns instance
|
17
|
+
# variables to be used in the view
|
18
|
+
# - Usually the state method will call #render and return
|
19
|
+
# - #render will retrieve the corresponding view
|
20
|
+
# (e.g. <tt>app/cells/blog/newest_article.html. [erb|haml|...]</tt>),
|
21
|
+
# renders this template and returns the markup.
|
22
|
+
#
|
23
|
+
# == Design Principles
|
24
|
+
# A cell is a completely autonomous object and it should not know or have to know
|
25
|
+
# from what controller it is being rendered. For this reason, the controller's
|
26
|
+
# instance variables and params hash are not directly available from the cell or
|
27
|
+
# its views. This is not a bug, this is a feature! It means cells are truly
|
28
|
+
# reusable components which can be plugged in at any point in your application
|
29
|
+
# without having to think about what information is available at that point.
|
30
|
+
# When rendering a cell, you can explicitly pass variables to the cell in the
|
31
|
+
# extra opts argument hash, just like you would pass locals in partials.
|
32
|
+
# This hash is then available inside the cell as the @opts instance variable.
|
33
|
+
#
|
34
|
+
# == Directory hierarchy
|
35
|
+
#
|
36
|
+
# To get started creating your own cells, you can simply create a new directory
|
37
|
+
# structure under your <tt>app</tt> directory called <tt>cells</tt>. Cells are
|
38
|
+
# ruby classes which end in the name Cell. So for example, if you have a
|
39
|
+
# cell which manages all user information, it would be called <tt>UserCell</tt>.
|
40
|
+
# A cell which manages a shopping cart could be called <tt>ShoppingCartCell</tt>.
|
41
|
+
#
|
42
|
+
# The directory structure of this example would look like this:
|
43
|
+
# app/
|
44
|
+
# models/
|
45
|
+
# ..
|
46
|
+
# views/
|
47
|
+
# ..
|
48
|
+
# helpers/
|
49
|
+
# application_helper.rb
|
50
|
+
# product_helper.rb
|
51
|
+
# ..
|
52
|
+
# controllers/
|
53
|
+
# ..
|
54
|
+
# cells/
|
55
|
+
# shopping_cart_cell.rb
|
56
|
+
# shopping_cart/
|
57
|
+
# status.html.erb
|
58
|
+
# product_list.html.erb
|
59
|
+
# empty_prompt.html.erb
|
60
|
+
# user_cell.rb
|
61
|
+
# user/
|
62
|
+
# login.html.erb
|
63
|
+
# layouts/
|
64
|
+
# box.html.erb
|
65
|
+
# ..
|
66
|
+
#
|
67
|
+
# The directory with the same name as the cell contains views for the
|
68
|
+
# cell's <em>states</em>. A state is an executed method along with a
|
69
|
+
# rendered view, resulting in content. This means that states are to
|
70
|
+
# cells as actions are to controllers, so each state has its own view.
|
71
|
+
# The use of partials is deprecated with cells, it is better to just
|
72
|
+
# render a different state on the same cell (which also works recursively).
|
73
|
+
#
|
74
|
+
# Anyway, <tt>render :partial </tt> in a cell view will work, if the
|
75
|
+
# partial is contained in the cell's view directory.
|
76
|
+
#
|
77
|
+
# As can be seen above, Cells also can make use of helpers. All Cells
|
78
|
+
# include ApplicationHelper by default, but you can add additional helpers
|
79
|
+
# as well with the Cell::Base.helper class method:
|
80
|
+
# class ShoppingCartCell < Cell::Base
|
81
|
+
# helper :product
|
82
|
+
# ...
|
83
|
+
# end
|
84
|
+
#
|
85
|
+
# This will make the <tt>ProductHelper</tt> from <tt>app/helpers/product_helper.rb</tt>
|
86
|
+
# available from all state views from our <tt>ShoppingCartCell</tt>.
|
87
|
+
#
|
88
|
+
# == Cell inheritance
|
89
|
+
#
|
90
|
+
# Unlike controllers, Cells can form a class hierarchy. When a cell class
|
91
|
+
# is inherited by another cell class, its states are inherited as regular
|
92
|
+
# methods are, but also its views are inherited. Whenever a view is looked up,
|
93
|
+
# the view finder first looks for a file in the directory belonging to the
|
94
|
+
# current cell class, but if this is not found in the application or any
|
95
|
+
# engine, the superclass' directory is checked. This continues all the
|
96
|
+
# way up until it stops at Cell::Base.
|
97
|
+
#
|
98
|
+
# For instance, when you have two cells:
|
99
|
+
# class MenuCell < Cell::Base
|
100
|
+
# def show
|
101
|
+
# end
|
102
|
+
#
|
103
|
+
# def edit
|
104
|
+
# end
|
105
|
+
# end
|
106
|
+
#
|
107
|
+
# class MainMenuCell < MenuCell
|
108
|
+
# .. # no need to redefine show/edit if they do the same!
|
109
|
+
# end
|
110
|
+
# and the following directory structure in <tt>app/cells</tt>:
|
111
|
+
# app/cells/
|
112
|
+
# menu/
|
113
|
+
# show.html.erb
|
114
|
+
# edit.html.erb
|
115
|
+
# main_menu/
|
116
|
+
# show.html.erb
|
117
|
+
# then when you call
|
118
|
+
# render_cell :main_menu, :show
|
119
|
+
# the main menu specific show.html.erb (<tt>app/cells/main_menu/show.html.erb</tt>)
|
120
|
+
# is rendered, but when you call
|
121
|
+
# render_cell :main_menu, :edit
|
122
|
+
# cells notices that the main menu does not have a specific view for the
|
123
|
+
# <tt>edit</tt> state, so it will render the view for the parent class,
|
124
|
+
# <tt>app/cells/menu/edit.html.erb</tt>
|
125
|
+
#
|
126
|
+
#
|
127
|
+
# == Gettext support
|
128
|
+
#
|
129
|
+
# Cells support gettext, just name your views accordingly. It works exactly equivalent
|
130
|
+
# to controller views.
|
131
|
+
#
|
132
|
+
# cells/user/user_form.html.erb
|
133
|
+
# cells/user/user_form_de.html.erb
|
134
|
+
#
|
135
|
+
# If gettext is set to DE_de, the latter view will be chosen.
|
136
|
+
class Base
|
137
|
+
include ActionController::Helpers
|
138
|
+
include ActionController::RequestForgeryProtection
|
139
|
+
|
140
|
+
helper ApplicationHelper
|
141
|
+
|
142
|
+
|
143
|
+
class << self
|
144
|
+
attr_accessor :request_forgery_protection_token
|
145
|
+
|
146
|
+
# A template file will be looked for in each view path. This is typically
|
147
|
+
# just RAILS_ROOT/app/cells, but you might want to add e.g.
|
148
|
+
# RAILS_ROOT/app/views.
|
149
|
+
def add_view_path(path)
|
150
|
+
self.view_paths << RAILS_ROOT + '/' + path
|
151
|
+
end
|
152
|
+
|
153
|
+
# Creates a cell instance of the class <tt>name</tt>Cell, passing through
|
154
|
+
# <tt>opts</tt>.
|
155
|
+
def create_cell_for(controller, name, opts={})
|
156
|
+
class_from_cell_name(name).new(controller, opts)
|
157
|
+
end
|
158
|
+
|
159
|
+
# Declare a controller method as a helper. For example,
|
160
|
+
# helper_method :link_to
|
161
|
+
# def link_to(name, options) ... end
|
162
|
+
# makes the link_to controller method available in the view.
|
163
|
+
def helper_method(*methods)
|
164
|
+
methods.flatten.each do |method|
|
165
|
+
master_helper_module.module_eval <<-end_eval
|
166
|
+
def #{method}(*args, &block)
|
167
|
+
@cell.send(%(#{method}), *args, &block)
|
168
|
+
end
|
169
|
+
end_eval
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# Return the default view for the given state on this cell subclass.
|
174
|
+
# This is a file with the name of the state under a directory with the
|
175
|
+
# name of the cell followed by a template extension.
|
176
|
+
def view_for_state(state)
|
177
|
+
"#{cell_name}/#{state}"
|
178
|
+
end
|
179
|
+
|
180
|
+
# Find a possible template for a cell's current state. It tries to find a
|
181
|
+
# template file with the name of the state under a subdirectory
|
182
|
+
# with the name of the cell under the <tt>app/cells</tt> directory.
|
183
|
+
# If this file cannot be found, it will try to call this method on
|
184
|
+
# the superclass. This way you only have to write a state template
|
185
|
+
# once when a more specific cell does not need to change anything in
|
186
|
+
# that view.
|
187
|
+
def find_class_view_for_state(state)
|
188
|
+
return [view_for_state(state)] if superclass == Cell::Base
|
189
|
+
|
190
|
+
superclass.find_class_view_for_state(state) << view_for_state(state)
|
191
|
+
end
|
192
|
+
|
193
|
+
# Get the name of this cell's class as an underscored string,
|
194
|
+
# with _cell removed.
|
195
|
+
#
|
196
|
+
# Example:
|
197
|
+
# UserCell.cell_name
|
198
|
+
# => "user"
|
199
|
+
def cell_name
|
200
|
+
name.underscore.sub(/_cell/, '')
|
201
|
+
end
|
202
|
+
|
203
|
+
# Given a cell name, finds the class that belongs to it.
|
204
|
+
#
|
205
|
+
# Example:
|
206
|
+
# Cell::Base.class_from_cell_name(:user)
|
207
|
+
# => UserCell
|
208
|
+
def class_from_cell_name(cell_name)
|
209
|
+
"#{cell_name}_cell".classify.constantize
|
210
|
+
end
|
211
|
+
|
212
|
+
def state2view_cache
|
213
|
+
@state2view_cache ||= {}
|
214
|
+
end
|
215
|
+
|
216
|
+
def cache_configured?; ::ActionController::Base.cache_configured?; end
|
217
|
+
end
|
218
|
+
|
219
|
+
class_inheritable_array :view_paths, :instance_writer => false
|
220
|
+
self.view_paths = ActionView::PathSet.new
|
221
|
+
|
222
|
+
class_inheritable_accessor :allow_forgery_protection
|
223
|
+
self.allow_forgery_protection = true
|
224
|
+
|
225
|
+
class_inheritable_accessor :default_template_format
|
226
|
+
self.default_template_format = :html
|
227
|
+
|
228
|
+
|
229
|
+
delegate :params, :session, :request, :logger, :to => :controller
|
230
|
+
|
231
|
+
|
232
|
+
attr_accessor :controller
|
233
|
+
attr_reader :state_name
|
234
|
+
|
235
|
+
|
236
|
+
def initialize(controller, options={})
|
237
|
+
@controller = controller
|
238
|
+
@opts = options
|
239
|
+
end
|
240
|
+
|
241
|
+
def cell_name
|
242
|
+
self.class.cell_name
|
243
|
+
end
|
244
|
+
|
245
|
+
|
246
|
+
# Render the given state. You can pass the name as either a symbol or
|
247
|
+
# a string.
|
248
|
+
def render_state(state)
|
249
|
+
@cell = self
|
250
|
+
@state_name = state
|
251
|
+
|
252
|
+
content = dispatch_state(state)
|
253
|
+
|
254
|
+
return content if content.kind_of? String
|
255
|
+
|
256
|
+
render_view_for_backward_compat(content, state)
|
257
|
+
end
|
258
|
+
|
259
|
+
# Call the state method.
|
260
|
+
def dispatch_state(state)
|
261
|
+
send(state)
|
262
|
+
end
|
263
|
+
|
264
|
+
# We will soon remove the implicit call to render_view_for, but here it is for your convenience.
|
265
|
+
def render_view_for_backward_compat(opts, state)
|
266
|
+
ActiveSupport::Deprecation.warn "You either didn't call #render or forgot to return a string in the state method '#{state}'. However, returning nil is deprecated for the sake of explicitness"
|
267
|
+
|
268
|
+
render_view_for(opts, state)
|
269
|
+
end
|
270
|
+
|
271
|
+
|
272
|
+
# Renders the view for the current state and returns the markup for the component.
|
273
|
+
# Usually called and returned at the end of a state method.
|
274
|
+
#
|
275
|
+
# ==== Options
|
276
|
+
# * <tt>:view</tt> - Specifies the name of the view file to render. Defaults to the current state name.
|
277
|
+
# * <tt>:template_format</tt> - Allows using a format different to <tt>:html</tt>.
|
278
|
+
# * <tt>:layout</tt> - If set to a valid filename inside your cell's view_paths, the current state view will be rendered inside the layout (as known from controller actions). Layouts should reside in <tt>app/cells/layouts</tt>.
|
279
|
+
# * <tt>:locals</tt> - Makes the named parameters available as variables in the view.
|
280
|
+
# * <tt>:text</tt> - Just renders plain text.
|
281
|
+
# * <tt>:inline</tt> - Renders an inline template as state view. See ActionView::Base#render for details.
|
282
|
+
# * <tt>:file</tt> - Specifies the name of the file template to render.
|
283
|
+
# * <tt>:nothing</tt> - Will make the component kinda invisible and doesn't invoke the rendering cycle.
|
284
|
+
# * <tt>:state</tt> - Instantly invokes another rendering cycle for the passed state and returns.
|
285
|
+
# Example:
|
286
|
+
# class MyCell < Cell::Base
|
287
|
+
# def my_first_state
|
288
|
+
# # ... do something
|
289
|
+
# render
|
290
|
+
# end
|
291
|
+
#
|
292
|
+
# will just render the view <tt>my_first_state.html</tt>.
|
293
|
+
#
|
294
|
+
# def my_first_state
|
295
|
+
# # ... do something
|
296
|
+
# render :view => :my_first_state, :layout => "metal"
|
297
|
+
# end
|
298
|
+
#
|
299
|
+
# will also use the view <tt>my_first_state.html</tt> as template and even put it in the layout
|
300
|
+
# <tt>metal</tt> that's located at <tt>$RAILS_ROOT/app/cells/layouts/metal.html.erb</tt>.
|
301
|
+
#
|
302
|
+
# def say_your_name
|
303
|
+
# render :locals => {:name => "Nick"}
|
304
|
+
# end
|
305
|
+
#
|
306
|
+
# will make the variable +name+ available in the view <tt>say_your_name.html</tt>.
|
307
|
+
#
|
308
|
+
# def say_your_name
|
309
|
+
# render :nothing => true
|
310
|
+
# end
|
311
|
+
#
|
312
|
+
# will render an empty string thus keeping your name a secret.
|
313
|
+
#
|
314
|
+
#
|
315
|
+
# ==== Where have all the partials gone?
|
316
|
+
# In Cells we abandoned the term 'partial' in favor of plain 'views' - we don't need to distinguish
|
317
|
+
# between both terms. A cell view is both, a view and a kind of partial as it represents only a small
|
318
|
+
# part of the page.
|
319
|
+
# Just use <tt>:view</tt> and enjoy.
|
320
|
+
def render(opts={})
|
321
|
+
render_view_for(opts, @state_name) ### FIXME: i don't like the magic access to @state_name here. ugly!
|
322
|
+
end
|
323
|
+
|
324
|
+
# Render the view belonging to the given state. Will raise ActionView::MissingTemplate
|
325
|
+
# if it can not find one of the requested view template. Note that this behaviour was
|
326
|
+
# introduced in cells 2.3 and replaces the former warning message.
|
327
|
+
def render_view_for(opts, state)
|
328
|
+
return "" if opts[:nothing]
|
329
|
+
|
330
|
+
action_view = setup_action_view
|
331
|
+
|
332
|
+
### TODO: dispatch dynamically:
|
333
|
+
if opts[:text]
|
334
|
+
elsif opts[:inline]
|
335
|
+
elsif opts[:file]
|
336
|
+
elsif opts[:state]
|
337
|
+
opts[:text] = render_state(opts[:state])
|
338
|
+
else
|
339
|
+
# handle :layout, :template_format, :view
|
340
|
+
opts = defaultize_render_options_for(opts, state)
|
341
|
+
|
342
|
+
# set instance vars, include helpers:
|
343
|
+
prepare_action_view_for(action_view, opts)
|
344
|
+
|
345
|
+
template = find_family_view_for_state_with_caching(opts[:view], action_view)
|
346
|
+
opts[:file] = template
|
347
|
+
end
|
348
|
+
|
349
|
+
opts = sanitize_render_options(opts)
|
350
|
+
|
351
|
+
action_view.render_for(opts)
|
352
|
+
end
|
353
|
+
|
354
|
+
|
355
|
+
# Defaultize the passed options from #render.
|
356
|
+
def defaultize_render_options_for(opts, state)
|
357
|
+
opts[:template_format] ||= self.class.default_template_format
|
358
|
+
opts[:view] ||= state
|
359
|
+
opts
|
360
|
+
end
|
361
|
+
|
362
|
+
def prepare_action_view_for(action_view, opts)
|
363
|
+
# make helpers available:
|
364
|
+
include_helpers_in_class(action_view.class)
|
365
|
+
|
366
|
+
action_view.assigns = assigns_for_view # make instance vars available.
|
367
|
+
action_view.template_format = opts[:template_format]
|
368
|
+
end
|
369
|
+
|
370
|
+
def setup_action_view
|
371
|
+
view_class = Class.new(Cell::View)
|
372
|
+
action_view = view_class.new(self.class.view_paths, {}, @controller)
|
373
|
+
action_view.cell = self
|
374
|
+
action_view
|
375
|
+
end
|
376
|
+
|
377
|
+
# Prepares <tt>opts</tt> to be passed to ActionView::Base#render by removing
|
378
|
+
# unknown parameters.
|
379
|
+
def sanitize_render_options(opts)
|
380
|
+
opts.except!(:view, :state)
|
381
|
+
end
|
382
|
+
|
383
|
+
|
384
|
+
# Climbs up the inheritance hierarchy of the Cell, looking for a view
|
385
|
+
# for the current <tt>state</tt> in each level.
|
386
|
+
# As soon as a view file is found it is returned as an ActionView::Template
|
387
|
+
# instance.
|
388
|
+
### DISCUSS: moved to Cell::View#find_template in rainhead's fork:
|
389
|
+
def find_family_view_for_state(state, action_view)
|
390
|
+
missing_template_exception = nil
|
391
|
+
|
392
|
+
possible_paths_for_state(state).each do |template_path|
|
393
|
+
# we need to catch MissingTemplate, since we want to try for all possible
|
394
|
+
# family views.
|
395
|
+
begin
|
396
|
+
if view = action_view.try_picking_template_for_path(template_path)
|
397
|
+
return view
|
398
|
+
end
|
399
|
+
rescue ::ActionView::MissingTemplate => missing_template_exception
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
raise missing_template_exception
|
404
|
+
end
|
405
|
+
|
406
|
+
# In production mode, the view for a state/template_format is cached.
|
407
|
+
### DISCUSS: ActionView::Base already caches results for #pick_template, so maybe
|
408
|
+
### we should just cache the family path for a state/format?
|
409
|
+
def find_family_view_for_state_with_caching(state, action_view)
|
410
|
+
return find_family_view_for_state(state, action_view) unless self.class.cache_configured?
|
411
|
+
|
412
|
+
# in production mode:
|
413
|
+
key = "#{state}/#{action_view.template_format}"
|
414
|
+
state2view = self.class.state2view_cache
|
415
|
+
state2view[key] || state2view[key] = find_family_view_for_state(state, action_view)
|
416
|
+
end
|
417
|
+
|
418
|
+
# Find possible files that belong to the state. This first tries the cell's
|
419
|
+
# <tt>#view_for_state</tt> method and if that returns a true value, it
|
420
|
+
# will accept that value as a string and interpret it as a pathname for
|
421
|
+
# the view file. If it returns a falsy value, it will call the Cell's class
|
422
|
+
# method find_class_view_for_state to determine the file to check.
|
423
|
+
#
|
424
|
+
# You can override the Cell::Base#view_for_state method for a particular
|
425
|
+
# cell if you wish to make it decide dynamically what file to render.
|
426
|
+
def possible_paths_for_state(state)
|
427
|
+
self.class.find_class_view_for_state(state).reverse!
|
428
|
+
end
|
429
|
+
|
430
|
+
# Prepares the hash {instance_var => value, ...} that should be available
|
431
|
+
# in the ActionView when rendering the state view.
|
432
|
+
def assigns_for_view
|
433
|
+
assigns = {}
|
434
|
+
(self.instance_variables - ivars_to_ignore).each do |k|
|
435
|
+
assigns[k[1..-1]] = instance_variable_get(k)
|
436
|
+
end
|
437
|
+
assigns
|
438
|
+
end
|
439
|
+
|
440
|
+
|
441
|
+
# When passed a copy of the ActionView::Base class, it
|
442
|
+
# will mix in all helper classes for this cell in that class.
|
443
|
+
def include_helpers_in_class(view_klass)
|
444
|
+
view_klass.send(:include, self.class.master_helper_module)
|
445
|
+
end
|
446
|
+
|
447
|
+
|
448
|
+
# Defines the instance variables that should <em>not</em> be copied to the
|
449
|
+
# View instance.
|
450
|
+
def ivars_to_ignore; ['@controller']; end
|
451
|
+
|
452
|
+
|
453
|
+
end
|
454
|
+
end
|