actionview 4.2.10 → 5.1.0

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.

Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +141 -272
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -3
  5. data/lib/action_view/base.rb +33 -21
  6. data/lib/action_view/buffers.rb +1 -1
  7. data/lib/action_view/context.rb +1 -1
  8. data/lib/action_view/dependency_tracker.rb +52 -20
  9. data/lib/action_view/digestor.rb +86 -83
  10. data/lib/action_view/flows.rb +9 -11
  11. data/lib/action_view/gem_version.rb +3 -3
  12. data/lib/action_view/helpers/active_model_helper.rb +8 -8
  13. data/lib/action_view/helpers/asset_tag_helper.rb +74 -38
  14. data/lib/action_view/helpers/asset_url_helper.rb +160 -59
  15. data/lib/action_view/helpers/atom_feed_helper.rb +16 -16
  16. data/lib/action_view/helpers/cache_helper.rb +90 -35
  17. data/lib/action_view/helpers/capture_helper.rb +7 -6
  18. data/lib/action_view/helpers/controller_helper.rb +3 -2
  19. data/lib/action_view/helpers/csrf_helper.rb +3 -3
  20. data/lib/action_view/helpers/date_helper.rb +156 -108
  21. data/lib/action_view/helpers/debug_helper.rb +3 -4
  22. data/lib/action_view/helpers/form_helper.rb +475 -94
  23. data/lib/action_view/helpers/form_options_helper.rb +87 -47
  24. data/lib/action_view/helpers/form_tag_helper.rb +88 -57
  25. data/lib/action_view/helpers/javascript_helper.rb +10 -10
  26. data/lib/action_view/helpers/number_helper.rb +76 -59
  27. data/lib/action_view/helpers/output_safety_helper.rb +34 -4
  28. data/lib/action_view/helpers/record_tag_helper.rb +12 -99
  29. data/lib/action_view/helpers/rendering_helper.rb +3 -3
  30. data/lib/action_view/helpers/sanitize_helper.rb +17 -14
  31. data/lib/action_view/helpers/tag_helper.rb +198 -73
  32. data/lib/action_view/helpers/tags/base.rb +132 -97
  33. data/lib/action_view/helpers/tags/check_box.rb +17 -17
  34. data/lib/action_view/helpers/tags/collection_check_boxes.rb +9 -33
  35. data/lib/action_view/helpers/tags/collection_helpers.rb +68 -36
  36. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +3 -11
  37. data/lib/action_view/helpers/tags/collection_select.rb +2 -2
  38. data/lib/action_view/helpers/tags/date_select.rb +36 -36
  39. data/lib/action_view/helpers/tags/datetime_field.rb +1 -1
  40. data/lib/action_view/helpers/tags/grouped_collection_select.rb +2 -2
  41. data/lib/action_view/helpers/tags/label.rb +5 -1
  42. data/lib/action_view/helpers/tags/password_field.rb +1 -1
  43. data/lib/action_view/helpers/tags/placeholderable.rb +1 -1
  44. data/lib/action_view/helpers/tags/radio_button.rb +4 -4
  45. data/lib/action_view/helpers/tags/search_field.rb +12 -9
  46. data/lib/action_view/helpers/tags/select.rb +9 -9
  47. data/lib/action_view/helpers/tags/text_area.rb +1 -1
  48. data/lib/action_view/helpers/tags/text_field.rb +5 -6
  49. data/lib/action_view/helpers/tags/translator.rb +15 -13
  50. data/lib/action_view/helpers/text_helper.rb +47 -30
  51. data/lib/action_view/helpers/translation_helper.rb +60 -30
  52. data/lib/action_view/helpers/url_helper.rb +132 -104
  53. data/lib/action_view/helpers.rb +1 -1
  54. data/lib/action_view/layouts.rb +59 -54
  55. data/lib/action_view/log_subscriber.rb +56 -7
  56. data/lib/action_view/lookup_context.rb +76 -61
  57. data/lib/action_view/model_naming.rb +1 -1
  58. data/lib/action_view/path_set.rb +28 -19
  59. data/lib/action_view/railtie.rb +30 -6
  60. data/lib/action_view/record_identifier.rb +51 -25
  61. data/lib/action_view/renderer/abstract_renderer.rb +19 -15
  62. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +55 -0
  63. data/lib/action_view/renderer/partial_renderer.rb +208 -206
  64. data/lib/action_view/renderer/renderer.rb +2 -6
  65. data/lib/action_view/renderer/streaming_template_renderer.rb +46 -48
  66. data/lib/action_view/renderer/template_renderer.rb +65 -66
  67. data/lib/action_view/rendering.rb +16 -9
  68. data/lib/action_view/routing_url_for.rb +25 -17
  69. data/lib/action_view/tasks/cache_digests.rake +23 -0
  70. data/lib/action_view/template/error.rb +14 -13
  71. data/lib/action_view/template/handlers/builder.rb +7 -7
  72. data/lib/action_view/template/handlers/erb/deprecated_erubis.rb +9 -0
  73. data/lib/action_view/template/handlers/erb/erubi.rb +81 -0
  74. data/lib/action_view/template/handlers/erb/erubis.rb +81 -0
  75. data/lib/action_view/template/handlers/erb.rb +9 -76
  76. data/lib/action_view/template/handlers/html.rb +9 -0
  77. data/lib/action_view/template/handlers/raw.rb +1 -3
  78. data/lib/action_view/template/handlers.rb +8 -6
  79. data/lib/action_view/template/html.rb +2 -4
  80. data/lib/action_view/template/resolver.rb +133 -109
  81. data/lib/action_view/template/text.rb +5 -8
  82. data/lib/action_view/template/types.rb +15 -17
  83. data/lib/action_view/template.rb +51 -28
  84. data/lib/action_view/test_case.rb +32 -27
  85. data/lib/action_view/testing/resolvers.rb +29 -31
  86. data/lib/action_view/version.rb +1 -1
  87. data/lib/action_view/view_paths.rb +26 -32
  88. data/lib/action_view.rb +5 -5
  89. data/lib/assets/compiled/rails-ujs.js +685 -0
  90. metadata +23 -23
  91. data/lib/action_view/tasks/dependencies.rake +0 -23
@@ -1,4 +1,4 @@
1
- require 'active_support/log_subscriber'
1
+ require "active_support/log_subscriber"
2
2
 
3
3
  module ActionView
4
4
  # = Action View Log Subscriber
@@ -19,25 +19,74 @@ module ActionView
19
19
  message << " (#{event.duration.round(1)}ms)"
20
20
  end
21
21
  end
22
- alias :render_partial :render_template
23
- alias :render_collection :render_template
22
+
23
+ def render_partial(event)
24
+ info do
25
+ message = " Rendered #{from_rails_root(event.payload[:identifier])}"
26
+ message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
27
+ message << " (#{event.duration.round(1)}ms)"
28
+ message << " #{cache_message(event.payload)}" if event.payload.key?(:cache_hit)
29
+ message
30
+ end
31
+ end
32
+
33
+ def render_collection(event)
34
+ identifier = event.payload[:identifier] || "templates"
35
+
36
+ info do
37
+ " Rendered collection of #{from_rails_root(identifier)}" \
38
+ " #{render_count(event.payload)} (#{event.duration.round(1)}ms)"
39
+ end
40
+ end
41
+
42
+ def start(name, id, payload)
43
+ if name == "render_template.action_view"
44
+ log_rendering_start(payload)
45
+ end
46
+
47
+ super
48
+ end
24
49
 
25
50
  def logger
26
51
  ActionView::Base.logger
27
52
  end
28
53
 
29
- protected
54
+ private
30
55
 
31
- EMPTY = ''
32
- def from_rails_root(string)
56
+ EMPTY = ""
57
+ def from_rails_root(string) # :doc:
33
58
  string = string.sub(rails_root, EMPTY)
34
59
  string.sub!(VIEWS_PATTERN, EMPTY)
35
60
  string
36
61
  end
37
62
 
38
- def rails_root
63
+ def rails_root # :doc:
39
64
  @root ||= "#{Rails.root}/"
40
65
  end
66
+
67
+ def render_count(payload) # :doc:
68
+ if payload[:cache_hits]
69
+ "[#{payload[:cache_hits]} / #{payload[:count]} cache hits]"
70
+ else
71
+ "[#{payload[:count]} times]"
72
+ end
73
+ end
74
+
75
+ def cache_message(payload) # :doc:
76
+ if payload[:cache_hit]
77
+ "[cache hit]"
78
+ else
79
+ "[cache miss]"
80
+ end
81
+ end
82
+
83
+ def log_rendering_start(payload)
84
+ info do
85
+ message = " Rendering #{from_rails_root(payload[:identifier])}"
86
+ message << " within #{from_rails_root(payload[:layout])}" if payload[:layout]
87
+ message
88
+ end
89
+ end
41
90
  end
42
91
  end
43
92
 
@@ -1,15 +1,16 @@
1
- require 'thread_safe'
2
- require 'active_support/core_ext/module/remove_method'
3
- require 'active_support/core_ext/module/attribute_accessors'
4
- require 'action_view/template/resolver'
1
+ require "concurrent/map"
2
+ require "active_support/core_ext/module/remove_method"
3
+ require "active_support/core_ext/module/attribute_accessors"
4
+ require "action_view/template/resolver"
5
5
 
6
6
  module ActionView
7
7
  # = Action View Lookup Context
8
8
  #
9
- # LookupContext is the object responsible to hold all information required to lookup
10
- # templates, i.e. view paths and details. The LookupContext is also responsible to
11
- # generate a key, given to view paths, used in the resolver cache lookup. Since
12
- # this key is generated just once during the request, it speeds up all cache accesses.
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,9 +20,9 @@ module ActionView
19
20
  mattr_accessor :registered_details
20
21
  self.registered_details = []
21
22
 
22
- def self.register_detail(name, options = {}, &block)
23
- self.registered_details << name
24
- initialize = registered_details.map { |n| "@details[:#{n}] = details[:#{n}] || default_#{n}" }
23
+ def self.register_detail(name, &block)
24
+ registered_details << name
25
+ Accessors::DEFAULT_PROCS[name] = block
25
26
 
26
27
  Accessors.send :define_method, :"default_#{name}", &block
27
28
  Accessors.module_eval <<-METHOD, __FILE__, __LINE__ + 1
@@ -33,16 +34,12 @@ module ActionView
33
34
  value = value.present? ? Array(value) : default_#{name}
34
35
  _set_detail(:#{name}, value) if value != @details[:#{name}]
35
36
  end
36
-
37
- remove_possible_method :initialize_details
38
- def initialize_details(details)
39
- #{initialize.join("\n")}
40
- end
41
37
  METHOD
42
38
  end
43
39
 
44
40
  # Holds accessors for the registered details.
45
41
  module Accessors #:nodoc:
42
+ DEFAULT_PROCS = {}
46
43
  end
47
44
 
48
45
  register_detail(:locale) do
@@ -54,29 +51,27 @@ module ActionView
54
51
  end
55
52
  register_detail(:formats) { ActionView::Base.default_formats || [:html, :text, :js, :css, :xml, :json] }
56
53
  register_detail(:variants) { [] }
57
- register_detail(:handlers){ Template::Handlers.extensions }
54
+ register_detail(:handlers) { Template::Handlers.extensions }
58
55
 
59
56
  class DetailsKey #:nodoc:
60
57
  alias :eql? :equal?
61
- alias :object_hash :hash
62
58
 
63
- attr_reader :hash
64
- @details_keys = ThreadSafe::Cache.new
59
+ @details_keys = Concurrent::Map.new
65
60
 
66
61
  def self.get(details)
67
62
  if details[:formats]
68
63
  details = details.dup
69
- details[:formats] &= Mime::SET.symbols
64
+ details[:formats] &= Template::Types.symbols
70
65
  end
71
- @details_keys[details] ||= new
66
+ @details_keys[details] ||= Concurrent::Map.new
72
67
  end
73
68
 
74
69
  def self.clear
75
70
  @details_keys.clear
76
71
  end
77
72
 
78
- def initialize
79
- @hash = object_hash
73
+ def self.digest_caches
74
+ @details_keys.values
80
75
  end
81
76
  end
82
77
 
@@ -98,9 +93,9 @@ module ActionView
98
93
  @cache = old_value
99
94
  end
100
95
 
101
- protected
96
+ private
102
97
 
103
- def _set_detail(key, value)
98
+ def _set_detail(key, value) # :doc:
104
99
  @details = @details.dup if @details_key
105
100
  @details_key = nil
106
101
  @details[key] = value
@@ -130,11 +125,16 @@ module ActionView
130
125
  @view_paths.find_all(*args_for_lookup(name, prefixes, partial, keys, options))
131
126
  end
132
127
 
133
- def exists?(name, prefixes = [], partial = false, keys = [], options = {})
128
+ def exists?(name, prefixes = [], partial = false, keys = [], **options)
134
129
  @view_paths.exists?(*args_for_lookup(name, prefixes, partial, keys, options))
135
130
  end
136
131
  alias :template_exists? :exists?
137
132
 
133
+ def any?(name, prefixes = [], partial = false)
134
+ @view_paths.exists?(*args_for_any(name, prefixes, partial))
135
+ end
136
+ alias :any_templates? :any?
137
+
138
138
  # Adds fallbacks to the view paths. Useful in cases when you are rendering
139
139
  # a :file.
140
140
  def with_fallbacks
@@ -149,16 +149,16 @@ module ActionView
149
149
  added_resolvers.times { view_paths.pop }
150
150
  end
151
151
 
152
- protected
152
+ private
153
153
 
154
- def args_for_lookup(name, prefixes, partial, keys, details_options) #:nodoc:
154
+ def args_for_lookup(name, prefixes, partial, keys, details_options)
155
155
  name, prefixes = normalize_name(name, prefixes)
156
156
  details, details_key = detail_args_for(details_options)
157
157
  [name, prefixes, partial || false, details, details_key, keys]
158
158
  end
159
159
 
160
160
  # Compute details hash and key according to user options (e.g. passed from #render).
161
- def detail_args_for(options)
161
+ def detail_args_for(options) # :doc:
162
162
  return @details, details_key if options.empty? # most common path.
163
163
  user_details = @details.merge(options)
164
164
 
@@ -171,18 +171,44 @@ module ActionView
171
171
  [user_details, details_key]
172
172
  end
173
173
 
174
+ def args_for_any(name, prefixes, partial)
175
+ name, prefixes = normalize_name(name, prefixes)
176
+ details, details_key = detail_args_for_any
177
+ [name, prefixes, partial || false, details, details_key]
178
+ end
179
+
180
+ def detail_args_for_any
181
+ @detail_args_for_any ||= begin
182
+ details = {}
183
+
184
+ registered_details.each do |k|
185
+ if k == :variants
186
+ details[k] = :any
187
+ else
188
+ details[k] = Accessors::DEFAULT_PROCS[k].call
189
+ end
190
+ end
191
+
192
+ if @cache
193
+ [details, DetailsKey.get(details)]
194
+ else
195
+ [details, nil]
196
+ end
197
+ end
198
+ end
199
+
174
200
  # Support legacy foo.erb names even though we now ignore .erb
175
201
  # as well as incorrectly putting part of the path in the template
176
202
  # name instead of the prefix.
177
- def normalize_name(name, prefixes) #:nodoc:
203
+ def normalize_name(name, prefixes)
178
204
  prefixes = prefixes.presence
179
- parts = name.to_s.split('/')
205
+ parts = name.to_s.split("/".freeze)
180
206
  parts.shift if parts.first.empty?
181
- name = parts.pop
207
+ name = parts.pop
182
208
 
183
209
  return name, prefixes || [""] if parts.empty?
184
210
 
185
- parts = parts.join('/')
211
+ parts = parts.join("/".freeze)
186
212
  prefixes = prefixes ? prefixes.map { |p| "#{p}/#{parts}" } : [parts]
187
213
 
188
214
  return name, prefixes
@@ -194,21 +220,32 @@ module ActionView
194
220
  include ViewPaths
195
221
 
196
222
  def initialize(view_paths, details = {}, prefixes = [])
197
- @details, @details_key = {}, nil
198
- @skip_default_locale = false
223
+ @details_key = nil
199
224
  @cache = true
200
225
  @prefixes = prefixes
201
226
  @rendered_format = nil
202
227
 
228
+ @details = initialize_details({}, details)
203
229
  self.view_paths = view_paths
204
- initialize_details(details)
205
230
  end
206
231
 
232
+ def digest_cache
233
+ details_key
234
+ end
235
+
236
+ def initialize_details(target, details)
237
+ registered_details.each do |k|
238
+ target[k] = details[k] || Accessors::DEFAULT_PROCS[k].call
239
+ end
240
+ target
241
+ end
242
+ private :initialize_details
243
+
207
244
  # Override formats= to expand ["*/*"] values and automatically
208
245
  # add :html as fallback to :js.
209
246
  def formats=(values)
210
247
  if values
211
- values.concat(default_formats) if values.delete "*/*"
248
+ values.concat(default_formats) if values.delete "*/*".freeze
212
249
  if values == [:js]
213
250
  values << :html
214
251
  @html_fallback_for_js = true
@@ -217,12 +254,6 @@ module ActionView
217
254
  super(values)
218
255
  end
219
256
 
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
257
  # Override locale to return a symbol instead of array.
227
258
  def locale
228
259
  @details[:locale].first
@@ -237,23 +268,7 @@ module ActionView
237
268
  config.locale = value
238
269
  end
239
270
 
240
- super(@skip_default_locale ? I18n.locale : default_locale)
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
271
+ super(default_locale)
257
272
  end
258
273
  end
259
274
  end
@@ -1,5 +1,5 @@
1
1
  module ActionView
2
- module ModelNaming
2
+ module ModelNaming #:nodoc:
3
3
  # Converts the given object to an ActiveModel compliant one.
4
4
  def convert_to_model(object)
5
5
  object.respond_to?(:to_model) ? object.to_model : object
@@ -58,32 +58,41 @@ module ActionView #:nodoc:
58
58
  find_all(path, prefixes, *args).any?
59
59
  end
60
60
 
61
+ def find_all_with_query(query) # :nodoc:
62
+ paths.each do |resolver|
63
+ templates = resolver.find_all_with_query(query)
64
+ return templates unless templates.empty?
65
+ end
66
+
67
+ []
68
+ end
69
+
61
70
  private
62
71
 
63
- def _find_all(path, prefixes, args, outside_app)
64
- prefixes = [prefixes] if String === prefixes
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)
72
+ def _find_all(path, prefixes, args, outside_app)
73
+ prefixes = [prefixes] if String === prefixes
74
+ prefixes.each do |prefix|
75
+ paths.each do |resolver|
76
+ if outside_app
77
+ templates = resolver.find_all_anywhere(path, prefix, *args)
78
+ else
79
+ templates = resolver.find_all(path, prefix, *args)
80
+ end
81
+ return templates unless templates.empty?
71
82
  end
72
- return templates unless templates.empty?
73
83
  end
84
+ []
74
85
  end
75
- []
76
- end
77
86
 
78
- def typecast(paths)
79
- paths.map do |path|
80
- case path
81
- when Pathname, String
82
- OptimizedFileSystemResolver.new path.to_s
83
- else
84
- path
87
+ def typecast(paths)
88
+ paths.map do |path|
89
+ case path
90
+ when Pathname, String
91
+ OptimizedFileSystemResolver.new path.to_s
92
+ else
93
+ path
94
+ end
85
95
  end
86
96
  end
87
- end
88
97
  end
89
98
  end
@@ -3,9 +3,10 @@ require "rails"
3
3
 
4
4
  module ActionView
5
5
  # = Action View Railtie
6
- class Railtie < Rails::Railtie # :nodoc:
6
+ class Railtie < Rails::Engine # :nodoc:
7
7
  config.action_view = ActiveSupport::OrderedOptions.new
8
- config.action_view.embed_authenticity_token_in_remote_forms = false
8
+ config.action_view.embed_authenticity_token_in_remote_forms = nil
9
+ config.action_view.debug_missing_translation = true
9
10
 
10
11
  config.eager_load_namespaces << ActionView
11
12
 
@@ -16,13 +17,22 @@ module ActionView
16
17
  end
17
18
  end
18
19
 
20
+ initializer "action_view.form_with_generates_remote_forms" do |app|
21
+ ActiveSupport.on_load(:action_view) do
22
+ form_with_generates_remote_forms = app.config.action_view.delete(:form_with_generates_remote_forms)
23
+ unless form_with_generates_remote_forms.nil?
24
+ ActionView::Helpers::FormHelper.form_with_generates_remote_forms = form_with_generates_remote_forms
25
+ end
26
+ end
27
+ end
28
+
19
29
  initializer "action_view.logger" do
20
30
  ActiveSupport.on_load(:action_view) { self.logger ||= Rails.logger }
21
31
  end
22
32
 
23
33
  initializer "action_view.set_configs" do |app|
24
34
  ActiveSupport.on_load(:action_view) do
25
- app.config.action_view.each do |k,v|
35
+ app.config.action_view.each do |k, v|
26
36
  send "#{k}=", v
27
37
  end
28
38
  end
@@ -36,14 +46,28 @@ module ActionView
36
46
  end
37
47
  end
38
48
 
49
+ initializer "action_view.per_request_digest_cache" do |app|
50
+ ActiveSupport.on_load(:action_view) do
51
+ unless ActionView::Resolver.caching?
52
+ app.executor.to_run ActionView::Digestor::PerExecutionDigestCacheExpiry
53
+ end
54
+ end
55
+ end
56
+
39
57
  initializer "action_view.setup_action_pack" do |app|
40
58
  ActiveSupport.on_load(:action_controller) do
41
- ActionView::RoutingUrlFor.send(:include, ActionDispatch::Routing::UrlFor)
59
+ ActionView::RoutingUrlFor.include(ActionDispatch::Routing::UrlFor)
42
60
  end
43
61
  end
44
62
 
45
- rake_tasks do
46
- load "action_view/tasks/dependencies.rake"
63
+ initializer "action_view.collection_caching", after: "action_controller.set_configs" do |app|
64
+ PartialRenderer.collection_cache = app.config.action_controller.cache_store
65
+ end
66
+
67
+ rake_tasks do |app|
68
+ unless app.config.api_only
69
+ load "action_view/tasks/cache_digests.rake"
70
+ end
47
71
  end
48
72
  end
49
73
  end
@@ -1,38 +1,64 @@
1
- require 'active_support/core_ext/module'
2
- require 'action_view/model_naming'
1
+ require "active_support/core_ext/module"
2
+ require "action_view/model_naming"
3
3
 
4
4
  module ActionView
5
- # The record identifier encapsulates a number of naming conventions for dealing with records, like Active Records or
6
- # pretty much any other model type that has an id. These patterns are then used to try elevate the view actions to
7
- # a higher logical level.
5
+ # RecordIdentifier encapsulates methods used by various ActionView helpers
6
+ # to associate records with DOM elements.
8
7
  #
9
- # # routes
10
- # resources :posts
8
+ # Consider for example the following code that form of post:
11
9
  #
12
- # # view
13
- # <%= div_for(post) do %> <div id="post_45" class="post">
14
- # <%= post.body %> What a wonderful world!
15
- # <% end %> </div>
10
+ # <%= form_for(post) do |f| %>
11
+ # <%= f.text_field :body %>
12
+ # <% end %>
16
13
  #
17
- # # controller
18
- # def update
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
- # redirect_to(post) # Calls polymorphic_url(post) which in turn calls post_url(post)
23
- # end
17
+ # <form class="new_post" id="new_post" action="/posts" accept-charset="UTF-8" method="post">
18
+ # <input type="text" name="post[body]" id="post_body" />
19
+ # </form>
20
+ #
21
+ # When +post+ is a persisted ActiveRecord::Base instance, the resulting HTML
22
+ # is:
23
+ #
24
+ # <form class="edit_post" id="edit_post_42" action="/posts/42" accept-charset="UTF-8" method="post">
25
+ # <input type="text" value="What a wonderful world!" name="post[body]" id="post_body" />
26
+ # </form>
27
+ #
28
+ # In both cases, the +id+ and +class+ of the wrapping DOM element are
29
+ # automatically generated, following naming conventions encapsulated by the
30
+ # RecordIdentifier methods #dom_id and #dom_class:
31
+ #
32
+ # dom_id(Post.new) # => "new_post"
33
+ # dom_class(Post.new) # => "post"
34
+ # dom_id(Post.find 42) # => "post_42"
35
+ # dom_class(Post.find 42) # => "post"
24
36
  #
25
- # As the example above shows, you can stop caring to a large extent what the actual id of the post is.
26
- # You just know that one is being assigned and that the subsequent calls in redirect_to expect that
27
- # same naming convention and allows you to write less code if you follow it.
37
+ # Note that these methods do not strictly require +Post+ to be a subclass of
38
+ # ActiveRecord::Base.
39
+ # Any +Post+ class will work as long as its instances respond to +to_key+
40
+ # and +model_name+, given that +model_name+ responds to +param_key+.
41
+ # For instance:
42
+ #
43
+ # class Post
44
+ # attr_accessor :to_key
45
+ #
46
+ # def model_name
47
+ # OpenStruct.new param_key: 'post'
48
+ # end
49
+ #
50
+ # def self.find(id)
51
+ # new.tap { |post| post.to_key = [id] }
52
+ # end
53
+ # end
28
54
  module RecordIdentifier
29
55
  extend self
30
56
  extend ModelNaming
31
57
 
32
58
  include ModelNaming
33
59
 
34
- JOIN = '_'.freeze
35
- NEW = 'new'.freeze
60
+ JOIN = "_".freeze
61
+ NEW = "new".freeze
36
62
 
37
63
  # The DOM class convention is to use the singular form of an object or class.
38
64
  #
@@ -66,7 +92,7 @@ module ActionView
66
92
  end
67
93
  end
68
94
 
69
- protected
95
+ private
70
96
 
71
97
  # Returns a string representation of the key attribute(s) that is suitable for use in an HTML DOM id.
72
98
  # This can be overwritten to customize the default generated string representation if desired.
@@ -76,9 +102,9 @@ module ActionView
76
102
  # overwritten version of the method. By default, this implementation passes the key string through a
77
103
  # method that replaces all characters that are invalid inside DOM ids, with valid ones. You need to
78
104
  # make sure yourself that your dom ids are valid, in case you overwrite this method.
79
- def record_key_for_dom_id(record)
105
+ def record_key_for_dom_id(record) # :doc:
80
106
  key = convert_to_model(record).to_key
81
- key ? key.join('_') : key
107
+ key ? key.join(JOIN) : key
82
108
  end
83
109
  end
84
110
  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, :find_file, :template_exists?, :with_fallbacks, :with_layout_format, :formats, :to => :@lookup_context
18
+ delegate :find_template, :find_file, :template_exists?, :any_templates?, :with_fallbacks, :with_layout_format, :formats, to: :@lookup_context
19
19
 
20
20
  def initialize(lookup_context)
21
21
  @lookup_context = lookup_context
@@ -25,25 +25,29 @@ module ActionView
25
25
  raise NotImplementedError
26
26
  end
27
27
 
28
- protected
28
+ private
29
29
 
30
- def extract_details(options)
31
- @lookup_context.registered_details.each_with_object({}) do |key, details|
32
- value = options[key]
30
+ def extract_details(options) # :doc:
31
+ @lookup_context.registered_details.each_with_object({}) do |key, details|
32
+ value = options[key]
33
33
 
34
- details[key] = Array(value) if value
34
+ details[key] = Array(value) if value
35
+ end
35
36
  end
36
- end
37
37
 
38
- def instrument(name, options={})
39
- ActiveSupport::Notifications.instrument("render_#{name}.action_view", options){ yield }
40
- end
38
+ def instrument(name, **options) # :doc:
39
+ options[:identifier] ||= (@template && @template.identifier) || @path
40
+
41
+ ActiveSupport::Notifications.instrument("render_#{name}.action_view", options) do |payload|
42
+ yield payload
43
+ end
44
+ end
41
45
 
42
- def prepend_formats(formats)
43
- formats = Array(formats)
44
- return if formats.empty? || @lookup_context.html_fallback_for_js
46
+ def prepend_formats(formats) # :doc:
47
+ formats = Array(formats)
48
+ return if formats.empty? || @lookup_context.html_fallback_for_js
45
49
 
46
- @lookup_context.formats = formats | @lookup_context.formats
47
- end
50
+ @lookup_context.formats = formats | @lookup_context.formats
51
+ end
48
52
  end
49
53
  end