This is a continuation of https://github.com/rails/rails/pull/46875
The behavior of looking up the class method when `to: :class` is passed
is a bit error prone because it silently degrades.
By passing the expected owner of the delegated method, we can be more
strict, and also generate a delegator in a module rather than having
to do it at inclusion time.
I made this argument private API because we want it in Rails, but
I'm worried it might be a bit too sharp for public API. I can
be convinced otherwise though.
Define the `ActionView::Helpers::FormBuilder` methods that wrap the
`@template` instance methods inside an
`ActiveSupport::CodeGenerator.batch` call so that the underlying `class`
extensions aren't invoked more than once.
This portrays the return values more accurately and, in some cases,
fixes syntax highlighting when using the upcoming version of SDoc
(thanks to its heuristic for detecting Ruby code vs HTML code).
According to the [HTML5 Spec][1]
> Void elements can't have any contents (since there's no end tag, no
> content can be put between the start tag and the end tag).
Up to this point, the only special handling of void elements has been to
use ">" to close them instead of "/>" (which is optional but valid
according to the spec)
> Then, if the element is one of the void elements, ... , then there may
> be a single U+002F SOLIDUS character (/), ... . On void elements, it
> does not mark the start tag as self-closing but instead is unnecessary
> and has no effect of any kind.
This commit deprecates the ability to pass content (either through the
positional "content" parameter or a block) to a void element since it is
not valid according to the Spec. This has the benefit of both
encouraging more correct HTML generation, and also simplifying the
method definition of void elements once the deprecation has been
removed.
This commit additionally tweaks the signature of "void_tag_string"
slightly with two changes. The first change is renaming it to be
"self_closing_tag_string". This is more accurate than "void_tag_string"
because the definition of "void element" is more specific and has more
requirements than "self closing element". For example, tags in the SVG
namespace _can_ be self closing but the "/" at the end of the start tag
is _not_ optional because they are not void elements. The second change
to this method is swapping from a boolean "self_closing" parameter to a
string "tag_suffix" parameter. This enables the void element method
definition to specialize the tag_suffix (to just ">") without either
void elements or self closing elements having to pay the runtime cost of
the self_closing conditional since we know at method definition time
which suffix each type of tag should use.
[1]: https://html.spec.whatwg.org/multipage/syntax.html#void-elements
Just a wee bit of visualization changes to make it more explicitly clear what is being passed (a quoted number)
Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>
This fixes a few issues with the formatting of the API documentation for
`ActionView::TestCase::Behavior::ClassMethods#register_parser`:
* Use `h4` headings instead of `h3` headings. SDoc renders method
signatures as `h3` headings, so subheadings such as "Examples" should
use `h4`.
* Replace "Arguments" heading with "Parameters". "Parameters" are
elements of function's signature, whereas "arguments" are elements of
a function call. The API documentation for other methods follows this
convention.
* Format parameters as term list.
* Fix indentation of pre-registered parser lists to render them as lists
instead of as code.
* Miscellaneous rewording, reorganization, and additional monospace
formatting.
Previously, both the `@rendered_format` and
`@marked_for_same_origin_verification` instance variables would be
assigned to instances of `ActionView::Base`, making them accessible in
view templates. However, these instance variables are really internal to
the controller and result in extra string allocations because the `@`
gets stripped and readded when going through the assignment.
This commit prefixes the variables with an underscore to help indicate
that they are internal, and then adds them to the list of
`_protected_ivars` to prevent assigning them when rendering templates.
Co-authored-by: Jean Boussier <jean.boussier@gmail.com>
Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>
Fix: https://github.com/rails/rails/pull/49780
Fix: https://github.com/rails/rails/issues/49761
`CollectionRenderer` implictly inject `<name>_counter` and `<name>_iteration`
locals, but in the overwhelming majority of cases they aren't used.
So when the rendered template has strict locals we shouldn't require
these to be declared, and if they aren't we should simply not pass them.
Co-Authored-By: HolyWalley <yakau@hey.com>
Ref: https://github.com/rails/rails/pull/47194#issuecomment-1760334146
They both give the buffer as return value of the capture block
which confuses the `capture` helper.
Ideally we wouldn't have to check for that, but it's
an acceptable tradeoff for backward compatibility.
The `ActionView::Helpers::TagHelper::TagBuilder` class renders HTML
elements based on the methods invoked on it. For example, `tag.input`
will render an `<input>` element, while `tag.turbo_frame` will render a
`<turbo-frame>` element.
The magic of the class is rooted in its definition of `#method_missing`.
The current implementation bakes-in special treatment of void HTML
elements (for example: `<input>`) and self-closing SVG elements (for
example: `<use />`). Despite its ahead-of-time knowledge of these
element names, calls to corresponding methods `tag.input` and `tag.use`
still rely on `#method_missing`.
This has performance implications.
This commit defines a new `TagBuilder.define_void_element` class method
to dynamically define methods for each known element. Then, the class
invokes the method for each element name in `HTML_VOID_ELEMENTS` and
`SVG_SELF_CLOSING_ELEMENTS`.
Calls to `.define_void_element` and `.define_self_closing_element` makr
`HTML_VOID_ELEMENTS` and `SVG_SELF_CLOSING_ELEMENTS` unnecessary, so
this commit removes those constant definitions.
Additionally, this call removes calls to
`TagHelper.ensure_valid_html5_tag_name` from the `TagBuilder` defined
element methods, since they're known ahead of time to be valid HTML.
Instead, only call `ensure_valid_html5_tag_name` from within the
`method_missing` calls, since those are determined at runtime and are
uncontrolled.
By cutting out the reliance on method missing, calls to `TagBuilder`
methods (like `tag.input`) become comparable to calls to the `tag` view
helper with positional arguments (like `tag(:input)`).
```
❯ ruby bench.rb
Warming up --------------------------------------
tag 73.438k i/100ms
tag_builder 79.910k i/100ms
Calculating -------------------------------------
tag 732.467k (± 0.9%) i/s - 3.672M in 5.013504s
tag_builder 810.981k (± 0.8%) i/s - 4.075M in 5.025632s
Comparison:
tag_builder: 810981.5 i/s
tag: 732467.4 i/s - 1.11x (± 0.00) slower
```
The results were from the following benchmark rendering void (`<input>`)
elements:
```ruby
# frozen_string_literal: true
require "bundler/setup"
require "action_view"
require "minitest/autorun"
require "benchmark/ips"
class Foo
include ActionView::Helpers
end
helpers = Foo.new
Benchmark.ips do |x|
x.report("tag") { helpers.tag("input", value: "foo") }
x.report("tag_builder") { helpers.tag.input(value: "foo") }
x.compare!
end
```
Similar to `tag.input` (a void HTML element), calls to `tag.div` are
become somewhat comparable to `tag("div")`:
```
❯ ruby bench.rb
Warming up --------------------------------------
content_tag 59.548k i/100ms
tag_builder 51.215k i/100ms
Calculating -------------------------------------
content_tag 595.067k (± 0.5%) i/s - 2.977M in 5.003570s
tag_builder 505.553k (± 2.2%) i/s - 2.561M in 5.067704s
Comparison:
content_tag: 595067.5 i/s
tag_builder: 505552.6 i/s - 1.18x (± 0.00) slower
```
The following benchmarks were collected to compare `tag("turbo-frame")`
(an unknown custom HTML element) and `tag.turbo_frame`:
```
❯ ruby bench.rb
Warming up --------------------------------------
content_tag 56.152k i/100ms
tag_builder 49.207k i/100ms
Calculating -------------------------------------
content_tag 561.134k (± 0.5%) i/s - 2.808M in 5.003567s
tag_builder 491.178k (± 0.3%) i/s - 2.460M in 5.009140s
Comparison:
content_tag: 561133.8 i/s
tag_builder: 491177.7 i/s - 1.14x (± 0.00) slower
```
Added validation for HTML tag names in the `tag` and `content_tag` helper method. The `tag` and
`content_tag` method now checks that the provided tag name adheres to the HTML specification. If
an invalid HTML tag name is provided, the method raises an `ArgumentError` with an appropriate error
message.
Examples:
```ruby
content_tag("12p") # Starting with a number
content_tag("") # Empty tag name
tag("div/") # Contains a solidus
tag("image file") # Contains a space
```
Register a callable to decode rendered content for a given MIME type
Each registered decoder will also define a `#rendered.$MIME` helper
method, where `$MIME` corresponds to the value of the `mime` argument.
=== Arguments
`mime` - Symbol the MIME Type name for the rendered content
`callable` - Callable to decode the String. Accepts the String
value as its only argument
`block` - Block serves as the decoder when the
`callable` is omitted
By default, ActionView::TestCase defines a decoder for:
* :html - returns an instance of Nokogiri::XML::Node
* :json - returns an instance of ActiveSupport::HashWithIndifferentAccess
Each pre-registered decoder also defines a corresponding helper:
* :html - defines `rendered.html`
* :json - defines `rendered.json`
=== Examples
To parse the rendered content into RSS, register a call to `RSS::Parser.parse`:
```ruby
register_decoder :rss, -> rendered { RSS::Parser.parse(rendered) }
test "renders RSS" do
article = Article.create!(title: "Hello, world")
render formats: :rss, partial: article
assert_equal "Hello, world", rendered.rss.items.last.title
end
```
To parse the rendered content into a Capybara::Simple::Node,
re-register an `:html` decoder with a call to
`Capybara.string`:
```ruby
register_decoder :html, -> rendered { Capybara.string(rendered) }
test "renders HTML" do
article = Article.create!(title: "Hello, world")
render partial: article
rendered.html.assert_css "h1", text: "Hello, world"
end
```
Update `include_hidden: false` to `include_hidden: true`
Update the language to be more accurate.
Update actionview/lib/action_view/helpers/form_helper.rb
Co-authored-by: Hartley McGuire <skipkayhil@gmail.com>
Change mentions of `app/views/shared` in the guides to be
`app/views/application` instead. View partials rely on the same
[Template Inheritance][] as their template counterparts, so the guides
should encourage end-users to benefit from that inheritance.
> This makes `app/views/application/` a great place for your shared
> partials, which can then be rendered in your ERB as such:
>
```html+erb
<%# app/views/admin/products/index.html.erb %>
<%= render @products || "empty_list" %>
<%# app/views/application/_empty_list.html.erb %>
There are no items in this list <em>yet</em>.
```
To enforce that template resolution, this commit also replaces
references to `shared/` with `application/` in the Rails test suite.
[Template Inheritance]: https://guides.rubyonrails.org/layouts_and_rendering.html#template-inheritance
Motivation / Background
---
While the `ActionView::TestCase` class isn't marked with a `:nodoc:`
comment to indicate that it's internal to Rails, there isn't much
content in the guides that explains how to test view partials.
Libraries like
[view_component](https://github.com/ViewComponent/view_component/) have
[built-in support for
testing](https://viewcomponent.org/guide/testing.html), including
Capybara integration.
While `ActionView::TestCase` already integrates with
`rails-dom-testing`, that integration could be better documented.
Additionally, it wouldn't take much for consuming applications to mimic
the ViewComponent testing experience for their Action View Partials.
Details
---
First, link to the "Testing Rails Applications" page from the
`ActionView::TestCase` class documentation.
Next, add a "Testing View Partials" section to the guides that expands
upon the variety of tooling available to tests that inherit from
`ActionView::TestCase`. In that section, cover topics like:
* the `render` helper method
* the `rendered` helper attribute reader
* calls to `assert_select` with attribute placeholders
* the `document_root_element` helper method
* integration with Ruby's Pattern Matching
* opportunities to integrate with Capybara
Additional Information
---
Additionally, add test coverage that exercise the examples shared in the
new section, including:
* Calls to `assert_select` that utilize attribute placeholders
* Ruby 3.0's Pattern Matching
* Integration with Capybara
By default `simple_format` method returns the text wrapped with `<p>`. But if we explicitly specify
the `wrapper_tag: nil` in the options, it returns the text wrapped with `<></>` tag.
Before:
```ruby
simple_format("Hello World", {}, { wrapper_tag: nil })
# <>Hello World</>
```
After:
```ruby
simple_format("Hello World", {}, { wrapper_tag: nil })
# <p>Hello World</p>
```
Co-authored-by: Junichi Ito <jit@sonicgarden.jp>
* Document the :ignore_date option on time_select
* split documentation comment into two lines
Co-authored-by: Rafael Mendonça França <rafael@franca.dev>
Before this would raise: `NoMethodError: undefined method `to_key' for nil:NilClass`
After it raises `ArumentError: dom_id must be passed a record_or_class as the first parameter, you passed 'nil'`
Rails 7 ships with Turbo enabled by default. Instead of using
`data-method` and `data-confirm`, Turbo now uses `data-turbo-method` and
`data-turbo-confirm` to perform a link visit with the specified HTTP
method and show confirmation dialog respective.
The deprecated legacy options are documented but the new options are
not.
This commit documents the `data-turbo-method` and `data-turbo-confirm`
for the `link_to` method.
The `button_to` documentation has also been updated to reference the
new `link_to` options.