Minitest::Test does not support all of the same options as
ActiveSupport::TestCase, such as running bin/test <filename>:<lineno>
and -n /regex/. Trying to use these options on this file would just run
all of the Minitest::Tests no matter what options were passed.
This commit fixes the ability to use those options by using
ActiveSupport::TestCase (like every other test in the repo).
Before:
```
$ bin/test test/template/dependency_tracker_test.rb:217
Running 59 tests in parallel using 8 processes
Run options: --seed 42725
.........................................................
Finished in 0.322759s, 176.6024 runs/s, 176.6024 assertions/s.
57 runs, 57 assertions, 0 failures, 0 errors, 0 skips
```
After:
```
$ bin/test test/template/dependency_tracker_test.rb:217
Running 59 tests in parallel using 8 processes
Run options: --seed 15213
...
Finished in 0.359162s, 8.3528 runs/s, 8.3528 assertions/s.
3 runs, 3 assertions, 0 failures, 0 errors, 0 skips
```
Motivation / Background
---
Strict Locals support was introduced in [#45727][] and announced as part
of the [7.1 Release][]. There are several mentions across the Guides,
but support is rarely mentioned in the API documentation.
Detail
----
Mention the template short identifier (the pathname, in most cases) as
part of the `ArgumentError` message.
This commit adds two test cases to ensure support for splatting
additional arguments, and for forbidding block and positional arguments.
It also makes mention of strict locals in more places, and links to the
guides.
[#45727]: https://github.com/rails/rails/pull/45727
[7.1 Release]: https://edgeguides.rubyonrails.org/7_1_release_notes.html#allow-templates-to-set-strict-locals
Previously, only the PrismRenderParser or RipperRenderParser would be
tested depending on if the Prism gem is available. This meant that
PrismRenderParser was being tested on Ruby 3.3 and RipperRenderParser
was tested on Ruby < 3.3. Additionally, if someone were to add prism to
the rails/rails Gemfile because they wrote a tool that uses it then the
RipperRenderParser would end up completely untested.
This commit is a small refactor to enable testing both RenderParsers in
all Ruby versions so that the prism gem can be added to the Gemfile.
The Rails documentation uses the `:include:` directive to inline the
README of the framework into the main documentation page. As the
README's aren't in the root directory from where SDoc is run we need to
add the framework path to the include:
# :include: activesupport/README.md
This results in a warning when installing the gems as generating the rdoc for the gem is run from the gem/framework root:
Couldn't find file to include 'activesupport/README.rdoc' from lib/active_support.rb
The `:include:` RDoc directive supports includes relative to the current
file as well:
# :include: ../README.md
This makes sure it works for the Rails API docs and the separate gems.
Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>
Fixes#50774
When the server boots up, 2 threads hit the same `UnboundTemplate`
instance before it has set up `@templates`. Both threads get past the
`unless template = @templates[locals]` check because
`@templates[locals]` isn't set yet. However, with `@write_lock`, one
thread waits while the other one proceeds, setting `@templates` to a
frozen hash. The second thread then gets the write lock and tries to
modify `@templates` but it has been frozen.
Currently there's about a 35% difference between tags generated using
the `TagBuilder` and tags generated by passing a positional argument to
`#tag`.
This commit optimizes `TagBuilder` to reduce that difference down to 13%.
The first change is to perform less hash allocations by not splatting
the options twice in the `TagBuilder` (one at the `tag.a` invocation,
and one at `tag_string`). The extra splat for `tag_string` was moved
into `method_missing` since that is the only other caller of this
private method.
The other change is to only escape the content in `tag_string` if it a
non-empty.
Additionally, a test was tweaked to ensure that passing `options` to a
`self_closing_element` is tested as it was previously not.
Benchmark:
```
require "action_view"
require "benchmark/ips"
class Foo
include ActionView::Helpers
end
helpers = Foo.new
Benchmark.ips do |x|
x.report("tag") { helpers.tag("a", href: "foo") }
x.report("tag_builder") { helpers.tag.a(href: "foo") }
x.compare!
end
```
Before:
```
ruby 3.2.2 (2023-03-30 revision e51014f9c0) [arm64-darwin22]
Warming up --------------------------------------
tag 67.180k i/100ms
tag_builder 50.267k i/100ms
Calculating -------------------------------------
tag 673.064k (± 0.4%) i/s - 3.426M in 5.090520s
tag_builder 504.971k (± 0.4%) i/s - 2.564M in 5.076842s
Comparison:
tag: 673063.7 i/s
tag_builder: 504971.4 i/s - 1.33x slower
```
After:
```
ruby 3.2.2 (2023-03-30 revision e51014f9c0) [arm64-darwin22]
Warming up --------------------------------------
tag 67.374k i/100ms
tag_builder 59.702k i/100ms
Calculating -------------------------------------
tag 670.837k (± 0.4%) i/s - 3.369M in 5.021714s
tag_builder 592.727k (± 1.3%) i/s - 2.985M in 5.037088s
Comparison:
tag: 670836.6 i/s
tag_builder: 592726.7 i/s - 1.13x slower
```
Co-authored-by: Sean Doyle <seanpdoyle@users.noreply.github.com>
Previously, the `render_template.action_view` and
`render_layout.action_view` events would always be handled as if they
had subscribers even if the log level of its subscribers result in
nothing being logged. For regular `LogSubscriber`s,
`subscribe_log_level` could be used to optimize these cases but the
Start subscriber is not a subclass of `LogSubscriber`.
This commit implements a `#subscribed?` method for the Start subscriber
so that it can also benefit from the `subscribe_log_level` optimization.
Follow-up to #50699.
This prevents a `NoMethodError` from being masked when the missing
method is itself named `render_in`.
Co-authored-by: Hartley McGuire <skipkayhil@gmail.com>
Follow-up to #50665.
Unconditionally converting `NoMethodError` to `ArgumentError` can mask a
legitimate `NoMethodError` from within the `render_in` method. This
commit adds a check to prevent that.
When calling `render` with a `:renderable` argument, ensure that the
object responds to `#render_in`. If it doesn't, raise an
`ArgumentError`.
This commit also adjusts the `ArgumentError` that when a `:partial`
argument isn't Active Model compatible. Prior to this commit, the
message used `:` as a prefix to `to_partial_path`. This commit replaces
that with a `#` prefix to denote that it's expected to be an instance
method on the object.
RDoc treats consecutive indented lines as a single code block. For code
examples that span multiple files / languages, this confuses the syntax
highlighter and makes the examples harder to read. Unfortunately, RDoc
doesn't provide syntax to prevent this, and it ignores multiple
consecutive blank lines. However, by inserting an empty tag such as
`<code></code>`, we can force RDoc to recognize separate code blocks.
`ActionView::Helpers::NumberHelper` methods are thin wrappers over
`ActiveSupport::NumberHelper` methods, which are well documented. Thus
this commit points `ActionView::Helpers::NumberHelper` method docs to
`ActiveSupport::NumberHelper` methods instead of duplicating those docs.
Provide examples for rendering objects that respond to `render_in`. Also
highlight that the object can also define a `#format` method to control
how the rendered String should be treated.
Add test coverage for both Action View's and Action Pack's support for
`render` with `:renderable` options.
Now that we dropped support for Ruby 2.7, we no longer
need to check if variables are defined before accessing them
to avoid the undefined variable warning.
Now that we no longer support Ruby 2.7, many `ruby2_keyword` calls
can be eliminated.
The ones that are left could be eliminated but would end up substantially
slower or more compliacated so I left them for now.
This provides a shortcut for setting a Content Security Policy nonce on
a stylesheet_link_tag.
Co-authored-by: AJ Esler <ajesler@users.noreply.github.com>
This seems to be making a pair with `#render_partial` but in reality
it's no longer refered to from anywhere. Since it is marked with
nodoc, I propose to get rid of it.
As far as I can tell from my non-comprehensive research,
this method was introduced in the commit b735761,
became practically private in f984907 and unused in 1bc0a59.
To integrate with [rails-dom-testing][] and its selector assertions,
`ActionView::TestCase` [defines a `#document_root_element`
method][document_root_element] that parses the HTML into a fully valid
HTML document and returns the "root".
In the case of most Action View partials rendered with `render partial:
"..."`, the resulting document would be invalid, so its constituent
parts (its `<html>`, `<head>`, and `<body>` elements) are synthesized in
during the parsing process. This results in a document whose _contents_
are equivalent to the original HTML string, but whose structure is not.
To share a concrete example:
```ruby
irb(main):002:0> rendered = "<h1>Hello world</h1><h2>Goodbye world</h2>"
=> "<h1>Hello world</h1><h2>Goodbye world</h2>"
irb(main):003:0> root = Rails::Dom::Testing.html_document.parse(rendered).root
=>
#(Element:0x57080 {
...
irb(main):004:0> rendered.to_s
=> "<h1>Hello world</h1><h2>Goodbye world</h2>"
irb(main):005:0> root.to_s
=> "<html><head></head><body><h1>Hello world</h1><h2>Goodbye world</h2></body></html>"
irb(main):006:0> rendered.to_s == root.to_s
=> false
```
Prior to this commit, the parsed HTML content returned from calling
`rendered.html` relied on the same mechanisms as
`#document_root_element`, and parsed the HTML fragment into a full
document, with a synthesized `<html>` element as its root. The
`rendered.html` value should reflect the content that was **rendered**
by the partial, and should not behave the same as
`#document_root_element`.
When the parsing class is changed from [Nokogiri::XML::Document][] to
[Nokogiri::XML::DocumentFragment][], the returned value reflects the
same **exact** content as what was rendered.
To elaborate on the previous example:
```ruby
irb(main):007:0> fragment = Rails::Dom::Testing.html_document_fragment.parse(rendered)
=>
#(DocumentFragment:0x62ee4 {
...
irb(main):008:0> fragment.to_s
=> "<h1>Hello world</h1><h2>Goodbye world</h2>"
irb(main):009:0> rendered.to_s == fragment.to_s
=> true
```
This commit changes the default `rendered.html` behavior to rely on
`Nokogiri::XML::DocumentFragment` instead of `Nokogiri::XML::Document`.
[Nokogiri::XML::Document]: https://nokogiri.org/rdoc/Nokogiri/XML/Document.html
[Nokogiri::XML::DocumentFragment]: https://nokogiri.org/rdoc/Nokogiri/XML/DocumentFragment.html
[document_root_element]: https://github.com/rails/rails-dom-testing/blob/v2.2.0/lib/rails/dom/testing/assertions/selector_assertions.rb#L75
Closes#49818
Renames `ActionView::TestCase::Behavior::Content` to
`RenderedViewContent`, with the goal of making it more of an internal
implementation detail that's unlikely to collide with an
application-side `::Content` class.
The `RenderedView`-prefix mirrors the module's `RenderedViewsCollection`
class. Since the intention is to treat it as a private implementation
detail, `RenderedViewContent` is marked with `:nodoc:`.
Along with the rename, this commit also modifies the class inheritance,
replacing the `SimpleDelegator` superclass with `String`. [String.new][]
accepts a `String` positional argument in the same way as
`SimpleDelegator.new` accepts a delegate object positional argument.
Sharing the `String` superclass also makes it a good candidate for being
passed to [Capybara.string][] (and [Capybara::Node::Simple.new][]) like
the documentation suggests.
[Capybara.string]: https://github.com/teamcapybara/capybara/blob/3.39.2/lib/capybara.rb#L212-L242
[Capybara::Node::Simple.new]: https://github.com/teamcapybara/capybara/blob/3.39.2/lib/capybara/node/simple.rb#L23
[String.new]: https://ruby-doc.org/core/String.html#method-c-new
Apparently it had not been clear what visibility they should assume
when they were initially created at https://github.com/rails/rails/pull/35265,
then a big refactoring took place at https://github.com/rails/rails/pull/38594
after which they remain intact.
So I think they can now become properly private as they are nodoc and
not refered to but from within the same class.
Also perform two autocorrects with `bundle exec rubocop -A`:
- fixes a new case of [`Style/RedundantReturn`][1]
- fixes a new case of [`Performance/StringInclude`][2]
[1]: 146b1c2e33
[2]: 3158bbb9f6
Co-authored-by: David Heinemeier Hansson <david@basecamp.com>
Until now, Rails only droped compatibility with older
rubies on new majors, but I propose to change this policy
because it causes us to either keep compatibility with long
EOLed rubies or to bump the Rails major more often, and to
drop multiple Ruby versions at once when we bump the major.
In my opinion it's a bad alignments of incentives. And we'd
be much better to just drop support in new minors whenever they
go EOL (so 3 years).
Also Ruby being an upstream dependency, it's not even
a semver violation AFAICT.
Since Rails 7.2 isn't planned before a few months, we
can already drop Ruby 3.0 as it will be EOL in March.
Since `ActionText::Content` wraps an `ActionText::Fragment`, and
`ActionText::Fragment` wraps a `Nokogiri::XML::DocumentFragment`, then
`ActionText::Content` should be able to rely on the newer Ruby pattern
matching introduced by [nokogiri@1.16.0][] (mainly the
[DocumentFragment#deconstruct][] method):
```ruby
content = ActionText::Content.new <<~HTML
<h1>Hello, world</h1>
<div>The body</div>
HTML
content => [h1, div]
assert_pattern { h1 => { content: "Hello, world" } }
assert_pattern { div => { content: "The body" } }
```
The implementation change relies on delegating from `Content` to
`Fragment`, and from `Fragment` to `DocumentFragment#elements` (to
deliberately exclude text nodes).
[nokogiri@1.16.0]: https://nokogiri.org/CHANGELOG.html?h=pattern
[DocumentFragment#deconstruct]: https://nokogiri.org/rdoc/Nokogiri/XML/DocumentFragment.html?h=deconstruct#method-i-deconstruct