diff options
Diffstat (limited to 'actionview')
23 files changed, 87 insertions, 94 deletions
diff --git a/actionview/README.rdoc b/actionview/README.rdoc index 03a0723564..ba50c67024 100644 --- a/actionview/README.rdoc +++ b/actionview/README.rdoc @@ -5,6 +5,8 @@ view helpers that assist when building HTML forms, Atom feeds and more. Template formats that Action View handles are ERB (embedded Ruby, typically used to inline short Ruby snippets inside HTML), and XML Builder. +You can read more about Action View in the {Action View Overview}[https://edgeguides.rubyonrails.org/action_view_overview.html] guide. + == Download and installation The latest version of Action View can be installed with RubyGems: @@ -27,7 +29,7 @@ Action View is released under the MIT license: API documentation is at -* http://api.rubyonrails.org +* https://api.rubyonrails.org Bug reports for the Ruby on Rails project can be filed here: diff --git a/actionview/actionview.gemspec b/actionview/actionview.gemspec index d8bd233ceb..200c775dcc 100644 --- a/actionview/actionview.gemspec +++ b/actionview/actionview.gemspec @@ -15,7 +15,7 @@ Gem::Specification.new do |s| s.author = "David Heinemeier Hansson" s.email = "david@loudthinking.com" - s.homepage = "http://rubyonrails.org" + s.homepage = "https://rubyonrails.org" s.files = Dir["CHANGELOG.md", "README.rdoc", "MIT-LICENSE", "lib/**/*"] s.require_path = "lib" diff --git a/actionview/app/assets/javascripts/README.md b/actionview/app/assets/javascripts/README.md index b9682b61e2..aa167004b6 100644 --- a/actionview/app/assets/javascripts/README.md +++ b/actionview/app/assets/javascripts/README.md @@ -52,5 +52,5 @@ Run `bundle exec rake ujs:server` first, and then run the web tests by visiting rails-ujs is released under the [MIT License](MIT-LICENSE). [data]: https://www.w3.org/TR/html5/dom.html#embedding-custom-non-visible-data-with-the-data-attributes "Embedding custom non-visible data with the data-* attributes" -[validator]: http://validator.w3.org/ -[csrf]: http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection.html +[validator]: https://validator.w3.org/ +[csrf]: https://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection.html diff --git a/actionview/app/assets/javascripts/rails-ujs/start.coffee b/actionview/app/assets/javascripts/rails-ujs/start.coffee index 5c1214df59..0347058195 100644 --- a/actionview/app/assets/javascripts/rails-ujs/start.coffee +++ b/actionview/app/assets/javascripts/rails-ujs/start.coffee @@ -2,6 +2,7 @@ fire, delegate getData, $ refreshCSRFTokens, CSRFProtection + loadCSPNonce enableElement, disableElement, handleDisabledElement handleConfirm, preventInsignificantClick handleRemote, formSubmitButtonClick, @@ -67,6 +68,7 @@ Rails.start = -> delegate document, Rails.formInputClickSelector, 'click', formSubmitButtonClick document.addEventListener('DOMContentLoaded', refreshCSRFTokens) + document.addEventListener('DOMContentLoaded', loadCSPNonce) window._rails_loaded = true if window.Rails is Rails and fire(document, 'rails:attachBindings') diff --git a/actionview/app/assets/javascripts/rails-ujs/utils/csp.coffee b/actionview/app/assets/javascripts/rails-ujs/utils/csp.coffee index 8d2d6ce447..a33f531375 100644 --- a/actionview/app/assets/javascripts/rails-ujs/utils/csp.coffee +++ b/actionview/app/assets/javascripts/rails-ujs/utils/csp.coffee @@ -1,4 +1,8 @@ -# Content-Security-Policy nonce for inline scripts -cspNonce = Rails.cspNonce = -> - meta = document.querySelector('meta[name=csp-nonce]') - meta and meta.content +nonce = null + +Rails.loadCSPNonce = -> + nonce = document.querySelector("meta[name=csp-nonce]")?.content + +# Returns the Content-Security-Policy nonce for inline scripts. +Rails.cspNonce = -> + nonce ? Rails.loadCSPNonce() diff --git a/actionview/lib/action_view/base.rb b/actionview/lib/action_view/base.rb index c4565ca272..cf71b4803b 100644 --- a/actionview/lib/action_view/base.rb +++ b/actionview/lib/action_view/base.rb @@ -242,7 +242,7 @@ module ActionView #:nodoc: @_config = ActiveSupport::InheritableOptions.new unless formats == NULL - ActiveSupport::Deprecation.warn <<~eowarn + ActiveSupport::Deprecation.warn <<~eowarn.squish Passing formats to ActionView::Base.new is deprecated eowarn end @@ -251,7 +251,7 @@ module ActionView #:nodoc: when ActionView::LookupContext @lookup_context = lookup_context else - ActiveSupport::Deprecation.warn <<~eowarn + ActiveSupport::Deprecation.warn <<~eowarn.squish ActionView::Base instances should be constructed with a lookup context, assignments, and a controller. eowarn @@ -278,7 +278,7 @@ module ActionView #:nodoc: def compiled_method_container if self.class == ActionView::Base - ActiveSupport::Deprecation.warn <<~eowarn + ActiveSupport::Deprecation.warn <<~eowarn.squish ActionView::Base instances must implement `compiled_method_container` or use the class method `with_empty_template_cache` for constructing an ActionView::Base instances that has an empty cache. diff --git a/actionview/lib/action_view/file_template.rb b/actionview/lib/action_view/file_template.rb index 43206f02e5..dea02176eb 100644 --- a/actionview/lib/action_view/file_template.rb +++ b/actionview/lib/action_view/file_template.rb @@ -22,11 +22,11 @@ module ActionView # to ensure that references to the template object can be marshalled as well. This means forgoing # the marshalling of the compiler mutex and instantiating that again on unmarshalling. def marshal_dump # :nodoc: - [ @identifier, @handler, @compiled, @original_encoding, @locals, @virtual_path, @updated_at, @format, @variants ] + [ @identifier, @handler, @compiled, @locals, @virtual_path, @updated_at, @format, @variant ] end def marshal_load(array) # :nodoc: - @identifier, @handler, @compiled, @original_encoding, @locals, @virtual_path, @updated_at, @format, @variants = *array + @identifier, @handler, @compiled, @locals, @virtual_path, @updated_at, @format, @variant = *array @compile_mutex = Mutex.new end end diff --git a/actionview/lib/action_view/helpers/form_helper.rb b/actionview/lib/action_view/helpers/form_helper.rb index c5a736bfb4..5533cef249 100644 --- a/actionview/lib/action_view/helpers/form_helper.rb +++ b/actionview/lib/action_view/helpers/form_helper.rb @@ -739,7 +739,7 @@ module ActionView # def labelled_form_with(**options, &block) # form_with(**options.merge(builder: LabellingFormBuilder), &block) # end - def form_with(model: nil, scope: nil, url: nil, format: nil, **options) + def form_with(model: nil, scope: nil, url: nil, format: nil, **options, &block) options[:allow_method_names_outside_object] = true options[:skip_default_ids] = !form_with_generates_ids @@ -752,7 +752,7 @@ module ActionView if block_given? builder = instantiate_builder(scope, model, options) - output = capture(builder, &Proc.new) + output = capture(builder, &block) options[:multipart] ||= builder.multipart? html_options = html_options_for_form_with(url, model, options) diff --git a/actionview/lib/action_view/helpers/output_safety_helper.rb b/actionview/lib/action_view/helpers/output_safety_helper.rb index 279cde5e76..52a951b2ca 100644 --- a/actionview/lib/action_view/helpers/output_safety_helper.rb +++ b/actionview/lib/action_view/helpers/output_safety_helper.rb @@ -38,7 +38,7 @@ module ActionView #:nodoc: # Converts the array to a comma-separated sentence where the last element is # joined by the connector word. This is the html_safe-aware version of - # ActiveSupport's {Array#to_sentence}[http://api.rubyonrails.org/classes/Array.html#method-i-to_sentence]. + # ActiveSupport's {Array#to_sentence}[https://api.rubyonrails.org/classes/Array.html#method-i-to_sentence]. # def to_sentence(array, options = {}) options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale) diff --git a/actionview/lib/action_view/helpers/translation_helper.rb b/actionview/lib/action_view/helpers/translation_helper.rb index 67c86592b9..d5b0a9263f 100644 --- a/actionview/lib/action_view/helpers/translation_helper.rb +++ b/actionview/lib/action_view/helpers/translation_helper.rb @@ -114,7 +114,7 @@ module ActionView # Delegates to <tt>I18n.localize</tt> with no additional functionality. # - # See http://rubydoc.info/github/svenfuchs/i18n/master/I18n/Backend/Base:localize + # See https://www.rubydoc.info/github/svenfuchs/i18n/master/I18n/Backend/Base:localize # for more information. def localize(*args) I18n.localize(*args) @@ -138,7 +138,7 @@ module ActionView end def html_safe_translation_key?(key) - /([_.]|\b)html\z/.match?(key.to_s) + /(?:_|\b)html\z/.match?(key.to_s) end end end diff --git a/actionview/lib/action_view/lookup_context.rb b/actionview/lib/action_view/lookup_context.rb index 10cd61bbd6..2eaf188ecf 100644 --- a/actionview/lib/action_view/lookup_context.rb +++ b/actionview/lib/action_view/lookup_context.rb @@ -154,7 +154,7 @@ module ActionView view_paths = build_view_paths((@view_paths.paths + self.class.fallbacks).uniq) if block_given? - ActiveSupport::Deprecation.warn <<~eowarn + ActiveSupport::Deprecation.warn <<~eowarn.squish Calling `with_fallbacks` with a block is deprecated. Call methods on the lookup context returned by `with_fallbacks` instead. eowarn diff --git a/actionview/lib/action_view/rendering.rb b/actionview/lib/action_view/rendering.rb index e5e2771323..ac861c44d4 100644 --- a/actionview/lib/action_view/rendering.rb +++ b/actionview/lib/action_view/rendering.rb @@ -118,7 +118,8 @@ module ActionView renderer.render_to_object(context, options) end - @rendered_format = Template::Types[rendered_template.format] + rendered_format = rendered_template.format || lookup_context.formats.first + @rendered_format = Template::Types[rendered_format] rendered_template.body end diff --git a/actionview/lib/action_view/template.rb b/actionview/lib/action_view/template.rb index ca387e9229..2c27e11b9e 100644 --- a/actionview/lib/action_view/template.rb +++ b/actionview/lib/action_view/template.rb @@ -122,24 +122,21 @@ module ActionView extend Template::Handlers - attr_accessor :locals, :variants, :virtual_path - attr_reader :source, :identifier, :handler, :original_encoding, :updated_at - attr_reader :variable, :format + attr_reader :variable, :format, :variant, :locals, :virtual_path - def initialize(source, identifier, handler, format: nil, **details) - unless format - ActiveSupport::Deprecation.warn "ActionView::Template#initialize requires a format parameter" - format = :html + def initialize(source, identifier, handler, format: nil, variant: nil, locals: nil, virtual_path: nil, updated_at: Time.now) + unless locals + ActiveSupport::Deprecation.warn "ActionView::Template#initialize requires a locals parameter" + locals = [] end @source = source @identifier = identifier @handler = handler @compiled = false - @original_encoding = nil - @locals = details[:locals] || [] - @virtual_path = details[:virtual_path] + @locals = locals + @virtual_path = virtual_path @variable = if @virtual_path base = @virtual_path[-1] == "/" ? "" : File.basename(@virtual_path) @@ -147,17 +144,19 @@ module ActionView $1.to_sym end - @updated_at = details[:updated_at] || Time.now + @updated_at = updated_at @format = format - @variants = [details[:variant]] + @variant = variant @compile_mutex = Mutex.new end - def formats=(_) - end - deprecate :formats= - + deprecate :original_encoding + deprecate def virtual_path=(_); end + deprecate def locals=(_); end + deprecate def formats=(_); end deprecate def formats; Array(format); end + deprecate def variants=(_); end + deprecate def variants; [variant]; end # Returns whether the underlying handler supports streaming. If so, # a streaming buffer *may* be passed when it starts rendering. @@ -262,11 +261,11 @@ module ActionView # to ensure that references to the template object can be marshalled as well. This means forgoing # the marshalling of the compiler mutex and instantiating that again on unmarshalling. def marshal_dump # :nodoc: - [ @source, @identifier, @handler, @compiled, @original_encoding, @locals, @virtual_path, @updated_at, @format, @variants ] + [ @source, @identifier, @handler, @compiled, @locals, @virtual_path, @updated_at, @format, @variant ] end def marshal_load(array) # :nodoc: - @source, @identifier, @handler, @compiled, @original_encoding, @locals, @virtual_path, @updated_at, @format, @variants = *array + @source, @identifier, @handler, @compiled, @locals, @virtual_path, @updated_at, @format, @variant = *array @compile_mutex = Mutex.new end diff --git a/actionview/lib/action_view/template/resolver.rb b/actionview/lib/action_view/template/resolver.rb index 220794e443..1dc9c9919a 100644 --- a/actionview/lib/action_view/template/resolver.rb +++ b/actionview/lib/action_view/template/resolver.rb @@ -143,14 +143,18 @@ module ActionView # Normalizes the arguments and passes it on to find_templates. def find_all(name, prefix = nil, partial = false, details = {}, key = nil, locals = []) + locals = locals.map(&:to_s).sort!.freeze + cached(key, [name, prefix, partial], details, locals) do - find_templates(name, prefix, partial, details) + find_templates(name, prefix, partial, details, false, locals) end end def find_all_anywhere(name, prefix, partial = false, details = {}, key = nil, locals = []) + locals = locals.map(&:to_s).sort!.freeze + cached(key, [name, prefix, partial], details, locals) do - find_templates(name, prefix, partial, details, true) + find_templates(name, prefix, partial, details, true, locals) end end @@ -165,13 +169,8 @@ module ActionView # This is what child classes implement. No defaults are needed # because Resolver guarantees that the arguments are present and # normalized. - def find_templates(name, prefix, partial, details, outside_app_allowed = false) - raise NotImplementedError, "Subclasses must implement a find_templates(name, prefix, partial, details, outside_app_allowed = false) method" - end - - # Helpers that builds a path. Useful for building virtual paths. - def build_path(name, prefix, partial) - Path.build(name, prefix, partial) + def find_templates(name, prefix, partial, details, outside_app_allowed = false, locals = []) + raise NotImplementedError, "Subclasses must implement a find_templates(name, prefix, partial, details, outside_app_allowed = false, locals = []) method" end # Handles templates caching. If a key is given and caching is on @@ -180,24 +179,13 @@ module ActionView # resolver is fresher before returning it. def cached(key, path_info, details, locals) name, prefix, partial = path_info - locals = locals.map(&:to_s).sort! if key @cache.cache(key, name, prefix, partial, locals) do - decorate(yield, path_info, details, locals) + yield end else - decorate(yield, path_info, details, locals) - end - end - - # Ensures all the resolver information is set in the template. - def decorate(templates, path_info, details, locals) - cached = nil - templates.each do |t| - t.locals = locals - t.variants = details[:variants] || [] if t.variants.empty? - t.virtual_path ||= (cached ||= build_path(*path_info)) + yield end end end @@ -214,22 +202,23 @@ module ActionView private - def find_templates(name, prefix, partial, details, outside_app_allowed = false) + def find_templates(name, prefix, partial, details, outside_app_allowed = false, locals) path = Path.build(name, prefix, partial) - query(path, details, details[:formats], outside_app_allowed) + query(path, details, details[:formats], outside_app_allowed, locals) end - def query(path, details, formats, outside_app_allowed) + def query(path, details, formats, outside_app_allowed, locals) template_paths = find_template_paths_from_details(path, details) template_paths = reject_files_external_to_app(template_paths) unless outside_app_allowed template_paths.map do |template| - handler, format, variant = extract_handler_and_format_and_variant(template, formats.first) + handler, format, variant = extract_handler_and_format_and_variant(template) FileTemplate.new(File.expand_path(template), handler, virtual_path: path.virtual, format: format, variant: variant, + locals: locals, updated_at: mtime(template) ) end @@ -291,7 +280,7 @@ module ActionView # Extract handler, formats and variant from path. If a format cannot be found neither # from the path, or the handler, we should return the array of formats given # to the resolver. - def extract_handler_and_format_and_variant(path, query_format) + def extract_handler_and_format_and_variant(path) pieces = File.basename(path).split(".") pieces.shift @@ -305,12 +294,12 @@ module ActionView if handler.respond_to?(:default_format) # default_format can return nil handler.default_format else - query_format + nil end end # Template::Types[format] and handler.default_format can return nil - [handler, format || query_format, variant] + [handler, format, variant] end end @@ -430,9 +419,5 @@ module ActionView def self.instances [new(""), new("/")] end - - def decorate(*) - super.each { |t| t.virtual_path = nil } - end end end diff --git a/actionview/lib/action_view/testing/resolvers.rb b/actionview/lib/action_view/testing/resolvers.rb index 275e2e9182..3ca8420c6c 100644 --- a/actionview/lib/action_view/testing/resolvers.rb +++ b/actionview/lib/action_view/testing/resolvers.rb @@ -23,7 +23,7 @@ module ActionView #:nodoc: private - def query(path, exts, _, _) + def query(path, exts, _, _, locals) query = +"" EXTENSIONS.each_key do |ext| query << "(" << exts[ext].map { |e| e && Regexp.escape(".#{e}") }.join("|") << "|)" @@ -34,11 +34,12 @@ module ActionView #:nodoc: @hash.each do |_path, array| source, updated_at = array next unless query.match?(_path) - handler, format, variant = extract_handler_and_format_and_variant(_path, :html) + handler, format, variant = extract_handler_and_format_and_variant(_path) templates << Template.new(source, _path, handler, virtual_path: path.virtual, format: format, variant: variant, + locals: locals, updated_at: updated_at ) end @@ -48,9 +49,9 @@ module ActionView #:nodoc: end class NullResolver < PathResolver - def query(path, exts, _, _) - handler, format, variant = extract_handler_and_format_and_variant(path, :html) - [ActionView::Template.new("Template generated by Null Resolver", path.virtual, handler, virtual_path: path.virtual, format: format, variant: variant)] + def query(path, exts, _, _, locals) + handler, format, variant = extract_handler_and_format_and_variant(path) + [ActionView::Template.new("Template generated by Null Resolver", path.virtual, handler, virtual_path: path.virtual, format: format, variant: variant, locals: locals)] end end end diff --git a/actionview/test/abstract_unit.rb b/actionview/test/abstract_unit.rb index e0a1f59755..fe317bf39e 100644 --- a/actionview/test/abstract_unit.rb +++ b/actionview/test/abstract_unit.rb @@ -60,7 +60,7 @@ module RenderERBUtils string.strip, "test template", ActionView::Template.handler_for_extension(:erb), - format: :html) + format: :html, locals: []) view = ActionView::Base.with_empty_template_cache template.render(view.empty, {}).strip diff --git a/actionview/test/actionpack/controller/view_paths_test.rb b/actionview/test/actionpack/controller/view_paths_test.rb index a7a58a78cc..4449d08496 100644 --- a/actionview/test/actionpack/controller/view_paths_test.rb +++ b/actionview/test/actionpack/controller/view_paths_test.rb @@ -143,8 +143,9 @@ class ViewLoadPathsTest < ActionController::TestCase "Decorated body", template.identifier, template.handler, - virtual_path: template.virtual_path, - format: template.format + virtual_path: template.virtual_path, + format: template.format, + locals: template.locals ) end end diff --git a/actionview/test/template/lookup_context_test.rb b/actionview/test/template/lookup_context_test.rb index a763e24226..62935620e9 100644 --- a/actionview/test/template/lookup_context_test.rb +++ b/actionview/test/template/lookup_context_test.rb @@ -121,11 +121,11 @@ class LookupContextTest < ActiveSupport::TestCase assert_equal "Hello texty phone!", template.source end - test "found templates respects given formats if one cannot be found from template or handler" do + test "found templates have nil format if one cannot be found from template or handler" do assert_called(ActionView::Template::Handlers::Builder, :default_format, returns: nil) do @lookup_context.formats = [:text] template = @lookup_context.find("hello", %w(test)) - assert_equal :text, template.format + assert_nil template.format end end diff --git a/actionview/test/template/resolver_patterns_test.rb b/actionview/test/template/resolver_patterns_test.rb index 38357aa10b..8122de779f 100644 --- a/actionview/test/template/resolver_patterns_test.rb +++ b/actionview/test/template/resolver_patterns_test.rb @@ -19,7 +19,7 @@ class ResolverPatternsTest < ActiveSupport::TestCase assert_equal 1, templates.size, "expected one template" assert_equal "Hello custom patterns!", templates.first.source assert_equal "custom_pattern/path", templates.first.virtual_path - assert_equal :html, templates.first.format + assert_nil templates.first.format end def test_should_return_all_templates_when_ambiguous_pattern diff --git a/actionview/test/template/template_test.rb b/actionview/test/template/template_test.rb index c74305fb42..71fb99115b 100644 --- a/actionview/test/template/template_test.rb +++ b/actionview/test/template/template_test.rb @@ -39,7 +39,8 @@ class TestERBTemplate < ActiveSupport::TestCase "partial", ERBHandler, virtual_path: "partial", - format: :html + format: :html, + locals: [] ) end @@ -57,8 +58,8 @@ class TestERBTemplate < ActiveSupport::TestCase end def new_template(body = "<%= hello %>", details = {}) - details = { format: :html }.merge details - ActionView::Template.new(body.dup, "hello template", details.fetch(:handler) { ERBHandler }, { virtual_path: "hello" }.merge!(details)) + details = { format: :html, locals: [] }.merge details + ActionView::Template.new(body.dup, "hello template", details.delete(:handler) || ERBHandler, { virtual_path: "hello" }.merge!(details)) end def render(locals = {}) @@ -103,8 +104,7 @@ class TestERBTemplate < ActiveSupport::TestCase end def test_locals - @template = new_template("<%= my_local %>") - @template.locals = [:my_local] + @template = new_template("<%= my_local %>", locals: [:my_local]) assert_equal "I am a local", render(my_local: "I am a local") end @@ -122,16 +122,14 @@ class TestERBTemplate < ActiveSupport::TestCase end def test_refresh_with_templates - @template = new_template("Hello", virtual_path: "test/foo/bar") - @template.locals = [:key] + @template = new_template("Hello", virtual_path: "test/foo/bar", locals: [:key]) assert_called_with(@context.lookup_context, :find_template, ["bar", %w(test/foo), false, [:key]], returns: "template") do assert_equal "template", @template.refresh(@context) end end def test_refresh_with_partials - @template = new_template("Hello", virtual_path: "test/_foo") - @template.locals = [:key] + @template = new_template("Hello", virtual_path: "test/_foo", locals: [:key]) assert_called_with(@context.lookup_context, :find_template, ["foo", %w(test), true, [:key]], returns: "partial") do assert_equal "partial", @template.refresh(@context) end diff --git a/actionview/test/template/testing/fixture_resolver_test.rb b/actionview/test/template/testing/fixture_resolver_test.rb index 4361824a71..afb6686dac 100644 --- a/actionview/test/template/testing/fixture_resolver_test.rb +++ b/actionview/test/template/testing/fixture_resolver_test.rb @@ -15,6 +15,6 @@ class FixtureResolverTest < ActiveSupport::TestCase assert_equal 1, templates.size, "expected one template" assert_equal "this text", templates.first.source assert_equal "arbitrary/path", templates.first.virtual_path - assert_equal :html, templates.first.format + assert_nil templates.first.format end end diff --git a/actionview/test/template/testing/null_resolver_test.rb b/actionview/test/template/testing/null_resolver_test.rb index dad8d0966d..c7c78804c0 100644 --- a/actionview/test/template/testing/null_resolver_test.rb +++ b/actionview/test/template/testing/null_resolver_test.rb @@ -9,6 +9,6 @@ class NullResolverTest < ActiveSupport::TestCase assert_equal 1, templates.size, "expected one template" assert_equal "Template generated by Null Resolver", templates.first.source assert_equal "arbitrary/path.erb", templates.first.virtual_path.to_s - assert_equal :html, templates.first.format + assert_nil templates.first.format end end diff --git a/actionview/test/template/text_test.rb b/actionview/test/template/text_test.rb index e7e3b6aae8..c837c53587 100644 --- a/actionview/test/template/text_test.rb +++ b/actionview/test/template/text_test.rb @@ -3,7 +3,7 @@ require "abstract_unit" class TextTest < ActiveSupport::TestCase - test "formats always return :text" do + test "format always return :text" do assert_equal :text, ActionView::Template::Text.new("").format end |