actionview 4.2.11.3 → 5.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionview might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +136 -255
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -3
- data/lib/action_view.rb +1 -1
- data/lib/action_view/base.rb +14 -2
- data/lib/action_view/dependency_tracker.rb +46 -15
- data/lib/action_view/digestor.rb +13 -9
- data/lib/action_view/flows.rb +1 -1
- data/lib/action_view/gem_version.rb +4 -4
- data/lib/action_view/helpers/asset_tag_helper.rb +15 -5
- data/lib/action_view/helpers/asset_url_helper.rb +51 -12
- data/lib/action_view/helpers/atom_feed_helper.rb +5 -4
- data/lib/action_view/helpers/cache_helper.rb +75 -20
- data/lib/action_view/helpers/capture_helper.rb +3 -2
- data/lib/action_view/helpers/controller_helper.rb +1 -0
- data/lib/action_view/helpers/date_helper.rb +39 -10
- data/lib/action_view/helpers/debug_helper.rb +1 -1
- data/lib/action_view/helpers/form_helper.rb +81 -35
- data/lib/action_view/helpers/form_options_helper.rb +74 -35
- data/lib/action_view/helpers/form_tag_helper.rb +46 -19
- data/lib/action_view/helpers/javascript_helper.rb +4 -4
- data/lib/action_view/helpers/number_helper.rb +10 -12
- data/lib/action_view/helpers/record_tag_helper.rb +12 -99
- data/lib/action_view/helpers/rendering_helper.rb +2 -2
- data/lib/action_view/helpers/sanitize_helper.rb +1 -2
- data/lib/action_view/helpers/tag_helper.rb +20 -13
- data/lib/action_view/helpers/tags/base.rb +33 -28
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +2 -30
- data/lib/action_view/helpers/tags/collection_helpers.rb +28 -0
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +1 -9
- data/lib/action_view/helpers/tags/file_field.rb +15 -0
- data/lib/action_view/helpers/tags/label.rb +1 -1
- data/lib/action_view/helpers/tags/placeholderable.rb +1 -1
- data/lib/action_view/helpers/tags/search_field.rb +12 -9
- data/lib/action_view/helpers/tags/text_field.rb +0 -1
- data/lib/action_view/helpers/tags/translator.rb +1 -1
- data/lib/action_view/helpers/text_helper.rb +25 -9
- data/lib/action_view/helpers/translation_helper.rb +56 -26
- data/lib/action_view/helpers/url_helper.rb +40 -65
- data/lib/action_view/layouts.rb +11 -10
- data/lib/action_view/lookup_context.rb +14 -40
- data/lib/action_view/model_naming.rb +1 -1
- data/lib/action_view/path_set.rb +15 -18
- data/lib/action_view/railtie.rb +20 -3
- data/lib/action_view/record_identifier.rb +44 -19
- data/lib/action_view/renderer/abstract_renderer.rb +1 -1
- data/lib/action_view/renderer/partial_renderer.rb +27 -26
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +70 -0
- data/lib/action_view/renderer/renderer.rb +2 -6
- data/lib/action_view/renderer/streaming_template_renderer.rb +1 -1
- data/lib/action_view/renderer/template_renderer.rb +12 -11
- data/lib/action_view/rendering.rb +8 -5
- data/lib/action_view/routing_url_for.rb +18 -6
- data/lib/action_view/template.rb +50 -13
- data/lib/action_view/template/error.rb +14 -7
- data/lib/action_view/template/handlers.rb +3 -3
- data/lib/action_view/template/handlers/erb.rb +25 -0
- data/lib/action_view/template/handlers/raw.rb +1 -1
- data/lib/action_view/template/resolver.rb +36 -58
- data/lib/action_view/template/types.rb +1 -1
- data/lib/action_view/test_case.rb +13 -8
- data/lib/action_view/testing/resolvers.rb +3 -4
- data/lib/action_view/view_paths.rb +6 -22
- metadata +17 -14
data/lib/action_view/layouts.rb
CHANGED
@@ -228,7 +228,7 @@ module ActionView
|
|
228
228
|
# set by the <tt>layout</tt> method.
|
229
229
|
#
|
230
230
|
# ==== Returns
|
231
|
-
# * <tt>
|
231
|
+
# * <tt>Boolean</tt> - True if the action has a layout definition, false otherwise.
|
232
232
|
def _conditional_layout?
|
233
233
|
return unless super
|
234
234
|
|
@@ -262,7 +262,7 @@ module ActionView
|
|
262
262
|
def layout(layout, conditions = {})
|
263
263
|
include LayoutConditions unless conditions.empty?
|
264
264
|
|
265
|
-
conditions.each {|k, v| conditions[k] = Array(v).map
|
265
|
+
conditions.each {|k, v| conditions[k] = Array(v).map(&:to_s) }
|
266
266
|
self._layout_conditions = conditions
|
267
267
|
|
268
268
|
self._layout = layout
|
@@ -277,7 +277,7 @@ module ActionView
|
|
277
277
|
remove_possible_method(:_layout)
|
278
278
|
|
279
279
|
prefixes = _implied_layout_name =~ /\blayouts/ ? [] : ["layouts"]
|
280
|
-
default_behavior = "lookup_context.find_all('#{_implied_layout_name}', #{prefixes.inspect}).first || super"
|
280
|
+
default_behavior = "lookup_context.find_all('#{_implied_layout_name}', #{prefixes.inspect}, false, [], { formats: formats }).first || super"
|
281
281
|
name_clause = if name
|
282
282
|
default_behavior
|
283
283
|
else
|
@@ -316,7 +316,7 @@ module ActionView
|
|
316
316
|
end
|
317
317
|
|
318
318
|
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
319
|
-
def _layout
|
319
|
+
def _layout(formats)
|
320
320
|
if _conditional_layout?
|
321
321
|
#{layout_definition}
|
322
322
|
else
|
@@ -372,7 +372,7 @@ module ActionView
|
|
372
372
|
end
|
373
373
|
|
374
374
|
# This will be overwritten by _write_layout_method
|
375
|
-
def _layout; end
|
375
|
+
def _layout(*); end
|
376
376
|
|
377
377
|
# Determine the layout for a given name, taking into account the name type.
|
378
378
|
#
|
@@ -382,8 +382,8 @@ module ActionView
|
|
382
382
|
case name
|
383
383
|
when String then _normalize_layout(name)
|
384
384
|
when Proc then name
|
385
|
-
when true then Proc.new { _default_layout(true) }
|
386
|
-
when :default then Proc.new { _default_layout(false) }
|
385
|
+
when true then Proc.new { |formats| _default_layout(formats, true) }
|
386
|
+
when :default then Proc.new { |formats| _default_layout(formats, false) }
|
387
387
|
when false, nil then nil
|
388
388
|
else
|
389
389
|
raise ArgumentError,
|
@@ -399,14 +399,15 @@ module ActionView
|
|
399
399
|
# Optionally raises an exception if the layout could not be found.
|
400
400
|
#
|
401
401
|
# ==== Parameters
|
402
|
+
# * <tt>formats</tt> - The formats accepted to this layout
|
402
403
|
# * <tt>require_layout</tt> - If set to true and layout is not found,
|
403
|
-
# an ArgumentError exception is raised (defaults to false)
|
404
|
+
# an +ArgumentError+ exception is raised (defaults to false)
|
404
405
|
#
|
405
406
|
# ==== Returns
|
406
407
|
# * <tt>template</tt> - The template object for the default layout (or nil)
|
407
|
-
def _default_layout(require_layout = false)
|
408
|
+
def _default_layout(formats, require_layout = false)
|
408
409
|
begin
|
409
|
-
value = _layout if action_has_layout?
|
410
|
+
value = _layout(formats) if action_has_layout?
|
410
411
|
rescue NameError => e
|
411
412
|
raise e, "Could not render layout: #{e.message}"
|
412
413
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'concurrent/map'
|
2
2
|
require 'active_support/core_ext/module/remove_method'
|
3
3
|
require 'active_support/core_ext/module/attribute_accessors'
|
4
4
|
require 'action_view/template/resolver'
|
@@ -6,10 +6,11 @@ require 'action_view/template/resolver'
|
|
6
6
|
module ActionView
|
7
7
|
# = Action View Lookup Context
|
8
8
|
#
|
9
|
-
# LookupContext is the object responsible
|
10
|
-
# templates, i.e. view paths and details.
|
11
|
-
#
|
12
|
-
#
|
9
|
+
# <tt>LookupContext</tt> is the object responsible for holding all information
|
10
|
+
# required for looking up templates, i.e. view paths and details.
|
11
|
+
# <tt>LookupContext</tt> is also responsible for generating a key, given to
|
12
|
+
# view paths, used in the resolver cache lookup. Since this key is generated
|
13
|
+
# only once during the request, it speeds up all cache accesses.
|
13
14
|
class LookupContext #:nodoc:
|
14
15
|
attr_accessor :prefixes, :rendered_format
|
15
16
|
|
@@ -19,7 +20,7 @@ module ActionView
|
|
19
20
|
mattr_accessor :registered_details
|
20
21
|
self.registered_details = []
|
21
22
|
|
22
|
-
def self.register_detail(name,
|
23
|
+
def self.register_detail(name, &block)
|
23
24
|
self.registered_details << name
|
24
25
|
initialize = registered_details.map { |n| "@details[:#{n}] = details[:#{n}] || default_#{n}" }
|
25
26
|
|
@@ -54,14 +55,14 @@ module ActionView
|
|
54
55
|
end
|
55
56
|
register_detail(:formats) { ActionView::Base.default_formats || [:html, :text, :js, :css, :xml, :json] }
|
56
57
|
register_detail(:variants) { [] }
|
57
|
-
register_detail(:handlers){ Template::Handlers.extensions }
|
58
|
+
register_detail(:handlers) { Template::Handlers.extensions }
|
58
59
|
|
59
60
|
class DetailsKey #:nodoc:
|
60
61
|
alias :eql? :equal?
|
61
62
|
alias :object_hash :hash
|
62
63
|
|
63
64
|
attr_reader :hash
|
64
|
-
@details_keys =
|
65
|
+
@details_keys = Concurrent::Map.new
|
65
66
|
|
66
67
|
def self.get(details)
|
67
68
|
if details[:formats]
|
@@ -122,15 +123,11 @@ module ActionView
|
|
122
123
|
end
|
123
124
|
alias :find_template :find
|
124
125
|
|
125
|
-
def find_file(name, prefixes = [], partial = false, keys = [], options = {})
|
126
|
-
@view_paths.find_file(*args_for_lookup(name, prefixes, partial, keys, options))
|
127
|
-
end
|
128
|
-
|
129
126
|
def find_all(name, prefixes = [], partial = false, keys = [], options = {})
|
130
127
|
@view_paths.find_all(*args_for_lookup(name, prefixes, partial, keys, options))
|
131
128
|
end
|
132
129
|
|
133
|
-
def exists?(name, prefixes = [], partial = false, keys = [], options
|
130
|
+
def exists?(name, prefixes = [], partial = false, keys = [], **options)
|
134
131
|
@view_paths.exists?(*args_for_lookup(name, prefixes, partial, keys, options))
|
135
132
|
end
|
136
133
|
alias :template_exists? :exists?
|
@@ -176,13 +173,13 @@ module ActionView
|
|
176
173
|
# name instead of the prefix.
|
177
174
|
def normalize_name(name, prefixes) #:nodoc:
|
178
175
|
prefixes = prefixes.presence
|
179
|
-
parts = name.to_s.split('/')
|
176
|
+
parts = name.to_s.split('/'.freeze)
|
180
177
|
parts.shift if parts.first.empty?
|
181
178
|
name = parts.pop
|
182
179
|
|
183
180
|
return name, prefixes || [""] if parts.empty?
|
184
181
|
|
185
|
-
parts = parts.join('/')
|
182
|
+
parts = parts.join('/'.freeze)
|
186
183
|
prefixes = prefixes ? prefixes.map { |p| "#{p}/#{parts}" } : [parts]
|
187
184
|
|
188
185
|
return name, prefixes
|
@@ -195,7 +192,6 @@ module ActionView
|
|
195
192
|
|
196
193
|
def initialize(view_paths, details = {}, prefixes = [])
|
197
194
|
@details, @details_key = {}, nil
|
198
|
-
@skip_default_locale = false
|
199
195
|
@cache = true
|
200
196
|
@prefixes = prefixes
|
201
197
|
@rendered_format = nil
|
@@ -208,7 +204,7 @@ module ActionView
|
|
208
204
|
# add :html as fallback to :js.
|
209
205
|
def formats=(values)
|
210
206
|
if values
|
211
|
-
values.concat(default_formats) if values.delete "*/*"
|
207
|
+
values.concat(default_formats) if values.delete "*/*".freeze
|
212
208
|
if values == [:js]
|
213
209
|
values << :html
|
214
210
|
@html_fallback_for_js = true
|
@@ -217,12 +213,6 @@ module ActionView
|
|
217
213
|
super(values)
|
218
214
|
end
|
219
215
|
|
220
|
-
# Do not use the default locale on template lookup.
|
221
|
-
def skip_default_locale!
|
222
|
-
@skip_default_locale = true
|
223
|
-
self.locale = nil
|
224
|
-
end
|
225
|
-
|
226
216
|
# Override locale to return a symbol instead of array.
|
227
217
|
def locale
|
228
218
|
@details[:locale].first
|
@@ -237,23 +227,7 @@ module ActionView
|
|
237
227
|
config.locale = value
|
238
228
|
end
|
239
229
|
|
240
|
-
super(
|
241
|
-
end
|
242
|
-
|
243
|
-
# Uses the first format in the formats array for layout lookup.
|
244
|
-
def with_layout_format
|
245
|
-
if formats.size == 1
|
246
|
-
yield
|
247
|
-
else
|
248
|
-
old_formats = formats
|
249
|
-
_set_detail(:formats, formats[0,1])
|
250
|
-
|
251
|
-
begin
|
252
|
-
yield
|
253
|
-
ensure
|
254
|
-
_set_detail(:formats, old_formats)
|
255
|
-
end
|
256
|
-
end
|
230
|
+
super(default_locale)
|
257
231
|
end
|
258
232
|
end
|
259
233
|
end
|
data/lib/action_view/path_set.rb
CHANGED
@@ -46,35 +46,32 @@ module ActionView #:nodoc:
|
|
46
46
|
find_all(*args).first || raise(MissingTemplate.new(self, *args))
|
47
47
|
end
|
48
48
|
|
49
|
-
def find_file(path, prefixes = [], *args)
|
50
|
-
_find_all(path, prefixes, args, true).first || raise(MissingTemplate.new(self, path, prefixes, *args))
|
51
|
-
end
|
52
|
-
|
53
49
|
def find_all(path, prefixes = [], *args)
|
54
|
-
|
50
|
+
prefixes = [prefixes] if String === prefixes
|
51
|
+
prefixes.each do |prefix|
|
52
|
+
paths.each do |resolver|
|
53
|
+
templates = resolver.find_all(path, prefix, *args)
|
54
|
+
return templates unless templates.empty?
|
55
|
+
end
|
56
|
+
end
|
57
|
+
[]
|
55
58
|
end
|
56
59
|
|
57
60
|
def exists?(path, prefixes, *args)
|
58
61
|
find_all(path, prefixes, *args).any?
|
59
62
|
end
|
60
63
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
prefixes.each do |prefix|
|
66
|
-
paths.each do |resolver|
|
67
|
-
if outside_app
|
68
|
-
templates = resolver.find_all_anywhere(path, prefix, *args)
|
69
|
-
else
|
70
|
-
templates = resolver.find_all(path, prefix, *args)
|
71
|
-
end
|
72
|
-
return templates unless templates.empty?
|
73
|
-
end
|
64
|
+
def find_all_with_query(query) # :nodoc:
|
65
|
+
paths.each do |resolver|
|
66
|
+
templates = resolver.find_all_with_query(query)
|
67
|
+
return templates unless templates.empty?
|
74
68
|
end
|
69
|
+
|
75
70
|
[]
|
76
71
|
end
|
77
72
|
|
73
|
+
private
|
74
|
+
|
78
75
|
def typecast(paths)
|
79
76
|
paths.map do |path|
|
80
77
|
case path
|
data/lib/action_view/railtie.rb
CHANGED
@@ -6,6 +6,7 @@ module ActionView
|
|
6
6
|
class Railtie < Rails::Railtie # :nodoc:
|
7
7
|
config.action_view = ActiveSupport::OrderedOptions.new
|
8
8
|
config.action_view.embed_authenticity_token_in_remote_forms = false
|
9
|
+
config.action_view.debug_missing_translation = true
|
9
10
|
|
10
11
|
config.eager_load_namespaces << ActionView
|
11
12
|
|
@@ -36,14 +37,30 @@ module ActionView
|
|
36
37
|
end
|
37
38
|
end
|
38
39
|
|
40
|
+
initializer "action_view.collection_caching" do |app|
|
41
|
+
ActiveSupport.on_load(:action_controller) do
|
42
|
+
PartialRenderer.collection_cache = app.config.action_controller.cache_store
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
initializer "action_view.per_request_digest_cache" do |app|
|
47
|
+
ActiveSupport.on_load(:action_view) do
|
48
|
+
if app.config.consider_all_requests_local
|
49
|
+
app.middleware.use ActionView::Digestor::PerRequestDigestCacheExpiry
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
39
54
|
initializer "action_view.setup_action_pack" do |app|
|
40
55
|
ActiveSupport.on_load(:action_controller) do
|
41
|
-
ActionView::RoutingUrlFor.
|
56
|
+
ActionView::RoutingUrlFor.include(ActionDispatch::Routing::UrlFor)
|
42
57
|
end
|
43
58
|
end
|
44
59
|
|
45
|
-
rake_tasks do
|
46
|
-
|
60
|
+
rake_tasks do |app|
|
61
|
+
unless app.config.api_only
|
62
|
+
load "action_view/tasks/dependencies.rake"
|
63
|
+
end
|
47
64
|
end
|
48
65
|
end
|
49
66
|
end
|
@@ -2,29 +2,54 @@ require 'active_support/core_ext/module'
|
|
2
2
|
require 'action_view/model_naming'
|
3
3
|
|
4
4
|
module ActionView
|
5
|
-
#
|
6
|
-
#
|
7
|
-
# a higher logical level.
|
5
|
+
# RecordIdentifier encapsulates methods used by various ActionView helpers
|
6
|
+
# to associate records with DOM elements.
|
8
7
|
#
|
9
|
-
#
|
10
|
-
# resources :posts
|
8
|
+
# Consider for example the following code that displays the body of a post:
|
11
9
|
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
# <% end %> </div>
|
10
|
+
# <%= div_for(post) do %>
|
11
|
+
# <%= post.body %>
|
12
|
+
# <% end %>
|
16
13
|
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
# post = Post.find(params[:id])
|
20
|
-
# post.update(params[:post])
|
14
|
+
# When +post+ is a new, unsaved ActiveRecord::Base instance, the resulting HTML
|
15
|
+
# is:
|
21
16
|
#
|
22
|
-
#
|
23
|
-
#
|
17
|
+
# <div id="new_post" class="post">
|
18
|
+
# </div>
|
19
|
+
#
|
20
|
+
# When +post+ is a persisted ActiveRecord::Base instance, the resulting HTML
|
21
|
+
# is:
|
22
|
+
#
|
23
|
+
# <div id="post_42" class="post">
|
24
|
+
# What a wonderful world!
|
25
|
+
# </div>
|
26
|
+
#
|
27
|
+
# In both cases, the +id+ and +class+ of the wrapping DOM element are
|
28
|
+
# automatically generated, following naming conventions encapsulated by the
|
29
|
+
# RecordIdentifier methods #dom_id and #dom_class:
|
30
|
+
#
|
31
|
+
# dom_id(Post.new) # => "new_post"
|
32
|
+
# dom_class(Post.new) # => "post"
|
33
|
+
# dom_id(Post.find 42) # => "post_42"
|
34
|
+
# dom_class(Post.find 42) # => "post"
|
24
35
|
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
36
|
+
# Note that these methods do not strictly require +Post+ to be a subclass of
|
37
|
+
# ActiveRecord::Base.
|
38
|
+
# Any +Post+ class will work as long as its instances respond to +to_key+
|
39
|
+
# and +model_name+, given that +model_name+ responds to +param_key+.
|
40
|
+
# For instance:
|
41
|
+
#
|
42
|
+
# class Post
|
43
|
+
# attr_accessor :to_key
|
44
|
+
#
|
45
|
+
# def model_name
|
46
|
+
# OpenStruct.new param_key: 'post'
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# def self.find(id)
|
50
|
+
# new.tap { |post| post.to_key = [id] }
|
51
|
+
# end
|
52
|
+
# end
|
28
53
|
module RecordIdentifier
|
29
54
|
extend self
|
30
55
|
extend ModelNaming
|
@@ -78,7 +103,7 @@ module ActionView
|
|
78
103
|
# make sure yourself that your dom ids are valid, in case you overwrite this method.
|
79
104
|
def record_key_for_dom_id(record)
|
80
105
|
key = convert_to_model(record).to_key
|
81
|
-
key ? key.join(
|
106
|
+
key ? key.join(JOIN) : key
|
82
107
|
end
|
83
108
|
end
|
84
109
|
end
|
@@ -15,7 +15,7 @@ module ActionView
|
|
15
15
|
# that new object is called in turn. This abstracts the setup and rendering
|
16
16
|
# into a separate classes for partials and templates.
|
17
17
|
class AbstractRenderer #:nodoc:
|
18
|
-
delegate :find_template, :
|
18
|
+
delegate :find_template, :template_exists?, :with_fallbacks, :with_layout_format, :formats, :to => :@lookup_context
|
19
19
|
|
20
20
|
def initialize(lookup_context)
|
21
21
|
@lookup_context = lookup_context
|
@@ -1,4 +1,5 @@
|
|
1
|
-
require '
|
1
|
+
require 'action_view/renderer/partial_renderer/collection_caching'
|
2
|
+
require 'concurrent/map'
|
2
3
|
|
3
4
|
module ActionView
|
4
5
|
class PartialIteration
|
@@ -73,7 +74,7 @@ module ActionView
|
|
73
74
|
#
|
74
75
|
# <%= render partial: "account", locals: { user: @buyer } %>
|
75
76
|
#
|
76
|
-
# == Rendering a collection of partials
|
77
|
+
# == \Rendering a collection of partials
|
77
78
|
#
|
78
79
|
# The example of partial use describes a familiar pattern where a template needs to iterate over an array and
|
79
80
|
# render a sub template for each of the elements. This pattern has been implemented as a single method that
|
@@ -105,7 +106,7 @@ module ActionView
|
|
105
106
|
# NOTE: Due to backwards compatibility concerns, the collection can't be one of hashes. Normally you'd also
|
106
107
|
# just keep domain objects, like Active Records, in there.
|
107
108
|
#
|
108
|
-
# == Rendering shared partials
|
109
|
+
# == \Rendering shared partials
|
109
110
|
#
|
110
111
|
# Two controllers can share a set of partials and render them like this:
|
111
112
|
#
|
@@ -113,7 +114,7 @@ module ActionView
|
|
113
114
|
#
|
114
115
|
# This will render the partial "advertisement/_ad.html.erb" regardless of which controller this is being called from.
|
115
116
|
#
|
116
|
-
# == Rendering objects that respond to `to_partial_path`
|
117
|
+
# == \Rendering objects that respond to `to_partial_path`
|
117
118
|
#
|
118
119
|
# Instead of explicitly naming the location of a partial, you can also let PartialRenderer do the work
|
119
120
|
# and pick the proper path by checking `to_partial_path` method.
|
@@ -127,7 +128,7 @@ module ActionView
|
|
127
128
|
# # <%= render partial: "posts/post", collection: @posts %>
|
128
129
|
# <%= render partial: @posts %>
|
129
130
|
#
|
130
|
-
# == Rendering the default case
|
131
|
+
# == \Rendering the default case
|
131
132
|
#
|
132
133
|
# If you're not going to be using any of the options like collections or layouts, you can also use the short-hand
|
133
134
|
# defaults of render to render partials. Examples:
|
@@ -147,29 +148,29 @@ module ActionView
|
|
147
148
|
# # <%= render partial: "posts/post", collection: @posts %>
|
148
149
|
# <%= render @posts %>
|
149
150
|
#
|
150
|
-
# == Rendering partials with layouts
|
151
|
+
# == \Rendering partials with layouts
|
151
152
|
#
|
152
153
|
# Partials can have their own layouts applied to them. These layouts are different than the ones that are
|
153
154
|
# specified globally for the entire action, but they work in a similar fashion. Imagine a list with two types
|
154
155
|
# of users:
|
155
156
|
#
|
156
|
-
# <%# app/views/users/index.html.erb
|
157
|
+
# <%# app/views/users/index.html.erb %>
|
157
158
|
# Here's the administrator:
|
158
159
|
# <%= render partial: "user", layout: "administrator", locals: { user: administrator } %>
|
159
160
|
#
|
160
161
|
# Here's the editor:
|
161
162
|
# <%= render partial: "user", layout: "editor", locals: { user: editor } %>
|
162
163
|
#
|
163
|
-
# <%# app/views/users/_user.html.erb
|
164
|
+
# <%# app/views/users/_user.html.erb %>
|
164
165
|
# Name: <%= user.name %>
|
165
166
|
#
|
166
|
-
# <%# app/views/users/_administrator.html.erb
|
167
|
+
# <%# app/views/users/_administrator.html.erb %>
|
167
168
|
# <div id="administrator">
|
168
169
|
# Budget: $<%= user.budget %>
|
169
170
|
# <%= yield %>
|
170
171
|
# </div>
|
171
172
|
#
|
172
|
-
# <%# app/views/users/_editor.html.erb
|
173
|
+
# <%# app/views/users/_editor.html.erb %>
|
173
174
|
# <div id="editor">
|
174
175
|
# Deadline: <%= user.deadline %>
|
175
176
|
# <%= yield %>
|
@@ -232,7 +233,7 @@ module ActionView
|
|
232
233
|
#
|
233
234
|
# You can also apply a layout to a block within any template:
|
234
235
|
#
|
235
|
-
# <%# app/views/users/_chief.html.erb
|
236
|
+
# <%# app/views/users/_chief.html.erb %>
|
236
237
|
# <%= render(layout: "administrator", locals: { user: chief }) do %>
|
237
238
|
# Title: <%= chief.title %>
|
238
239
|
# <% end %>
|
@@ -249,13 +250,13 @@ module ActionView
|
|
249
250
|
# If you pass arguments to "yield" then this will be passed to the block. One way to use this is to pass
|
250
251
|
# an array to layout and treat it as an enumerable.
|
251
252
|
#
|
252
|
-
# <%# app/views/users/_user.html.erb
|
253
|
+
# <%# app/views/users/_user.html.erb %>
|
253
254
|
# <div class="user">
|
254
255
|
# Budget: $<%= user.budget %>
|
255
256
|
# <%= yield user %>
|
256
257
|
# </div>
|
257
258
|
#
|
258
|
-
# <%# app/views/users/index.html.erb
|
259
|
+
# <%# app/views/users/index.html.erb %>
|
259
260
|
# <%= render layout: @users do |user| %>
|
260
261
|
# Title: <%= user.title %>
|
261
262
|
# <% end %>
|
@@ -264,14 +265,14 @@ module ActionView
|
|
264
265
|
#
|
265
266
|
# You can also yield multiple times in one layout and use block arguments to differentiate the sections.
|
266
267
|
#
|
267
|
-
# <%# app/views/users/_user.html.erb
|
268
|
+
# <%# app/views/users/_user.html.erb %>
|
268
269
|
# <div class="user">
|
269
270
|
# <%= yield user, :header %>
|
270
271
|
# Budget: $<%= user.budget %>
|
271
272
|
# <%= yield user, :footer %>
|
272
273
|
# </div>
|
273
274
|
#
|
274
|
-
# <%# app/views/users/index.html.erb
|
275
|
+
# <%# app/views/users/index.html.erb %>
|
275
276
|
# <%= render layout: @users do |user, section| %>
|
276
277
|
# <%- case section when :header -%>
|
277
278
|
# Title: <%= user.title %>
|
@@ -280,8 +281,10 @@ module ActionView
|
|
280
281
|
# <%- end -%>
|
281
282
|
# <% end %>
|
282
283
|
class PartialRenderer < AbstractRenderer
|
283
|
-
|
284
|
-
|
284
|
+
include CollectionCaching
|
285
|
+
|
286
|
+
PREFIXED_PARTIAL_NAMES = Concurrent::Map.new do |h, k|
|
287
|
+
h[k] = Concurrent::Map.new
|
285
288
|
end
|
286
289
|
|
287
290
|
def initialize(*)
|
@@ -321,8 +324,9 @@ module ActionView
|
|
321
324
|
spacer = find_template(@options[:spacer_template], @locals.keys).render(@view, @locals)
|
322
325
|
end
|
323
326
|
|
324
|
-
|
325
|
-
|
327
|
+
cache_collection_render do
|
328
|
+
@template ? collection_with_template : collection_without_template
|
329
|
+
end.join(spacer).html_safe
|
326
330
|
end
|
327
331
|
|
328
332
|
def render_partial
|
@@ -334,7 +338,7 @@ module ActionView
|
|
334
338
|
end
|
335
339
|
|
336
340
|
object = locals[as] if object.nil? # Respect object when object is false
|
337
|
-
locals[as] = object
|
341
|
+
locals[as] = object if @has_object
|
338
342
|
|
339
343
|
content = @template.render(view, locals) do |*name|
|
340
344
|
view._layout_for(*name, &block)
|
@@ -344,8 +348,6 @@ module ActionView
|
|
344
348
|
content
|
345
349
|
end
|
346
350
|
|
347
|
-
private
|
348
|
-
|
349
351
|
# Sets up instance variables needed for rendering a partial. This method
|
350
352
|
# finds the options and details and extracts them. The method also contains
|
351
353
|
# logic that handles the type of object passed in as the partial.
|
@@ -518,8 +520,8 @@ module ActionView
|
|
518
520
|
|
519
521
|
def retrieve_variable(path, as)
|
520
522
|
variable = as || begin
|
521
|
-
base = path[-1] == "/" ? "" : File.basename(path)
|
522
|
-
raise_invalid_identifier(path) unless base =~ /\A_?(
|
523
|
+
base = path[-1] == "/".freeze ? "".freeze : File.basename(path)
|
524
|
+
raise_invalid_identifier(path) unless base =~ /\A_?(.*)(?:\.\w+)*\z/
|
523
525
|
$1.to_sym
|
524
526
|
end
|
525
527
|
if @collection
|
@@ -530,8 +532,7 @@ module ActionView
|
|
530
532
|
end
|
531
533
|
|
532
534
|
IDENTIFIER_ERROR_MESSAGE = "The partial name (%s) is not a valid Ruby identifier; " +
|
533
|
-
"make sure your partial name starts with underscore
|
534
|
-
"and is followed by any combination of letters, numbers and underscores."
|
535
|
+
"make sure your partial name starts with underscore."
|
535
536
|
|
536
537
|
OPTION_AS_ERROR_MESSAGE = "The value (%s) of the option `as` is not a valid Ruby identifier; " +
|
537
538
|
"make sure it starts with lowercase letter, " +
|