actionview 7.1.0.beta1 → 7.1.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1dae47c55b843591b586cc22405cad1110d92dbcb5883a26d9bc2df75326c544
4
- data.tar.gz: 024efa59dc7a7775bdf3b8df5d52a639f8ac346b3438c071b135852a303598ad
3
+ metadata.gz: 801a14daea55551db924b943eba4b8fda7efcb2f95cfa5fc5e661f103400d455
4
+ data.tar.gz: 51b377932ac6ee1515e20af8d20da0c0f6ab296dbce50745b744a83bc626fdd7
5
5
  SHA512:
6
- metadata.gz: 9b024827ea0d1978c802f5fa4bc93ea0a8e5bf42324214fa8137c59a6106c1ead4dcff214f6b0a747b730905d4343902085763098db270b681a38372ec8dc5e5
7
- data.tar.gz: d20373508c43bc6d564841113f608d8e008aefb72c7ad58cc35ede559afdb9cd7c0ae5fbd5609020b2fb1a4c62631c0b6989b6428b7c0b1b0a5351c9a73b76aa
6
+ metadata.gz: ebbde2c0523e9d77e7510ce00f6e5961329130b6f6d2daf3326e62f99d73b8f8f0b830a9fb975c265c6a6c5bed7f7bdeed5c3a827e3d88b2896688c9ea917c47
7
+ data.tar.gz: aa071eccb5d24f5be5604ffac911420001b3b7c7b8c70134b4094bdf4c347f23f53efeaadc739d35ea6d6f6fc49d3521c29f9233b1c80113f4f66619f8bfc0aa
data/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ ## Rails 7.1.0.rc1 (September 27, 2023) ##
2
+
3
+ * Introduce `ActionView::TestCase.register_parser`
4
+
5
+ ```ruby
6
+ register_parser :rss, -> rendered { RSS::Parser.parse(rendered) }
7
+
8
+ test "renders RSS" do
9
+ article = Article.create!(title: "Hello, world")
10
+
11
+ render formats: :rss, partial: article
12
+
13
+ assert_equal "Hello, world", rendered.rss.items.last.title
14
+ end
15
+ ```
16
+
17
+ By default, register parsers for `:html` and `:json`.
18
+
19
+ *Sean Doyle*
20
+
21
+
1
22
  ## Rails 7.1.0.beta1 (September 13, 2023) ##
2
23
 
3
24
  * Fix `simple_format` with blank `wrapper_tag` option returns plain html tag
@@ -44,9 +44,9 @@ module ActionView # :nodoc:
44
44
  # Using sub templates allows you to sidestep tedious replication and extract common display structures in shared templates. The
45
45
  # classic example is the use of a header and footer (even though the Action Pack-way would be to use Layouts):
46
46
  #
47
- # <%= render "shared/header" %>
47
+ # <%= render "application/header" %>
48
48
  # Something really specific and terrific
49
- # <%= render "shared/footer" %>
49
+ # <%= render "application/footer" %>
50
50
  #
51
51
  # As you see, we use the output embeddings for the render methods. The render call itself will just return a string holding the
52
52
  # result of the rendering. The output embedding writes it to the current template.
@@ -55,7 +55,7 @@ module ActionView # :nodoc:
55
55
  # variables defined using the regular embedding tags. Like this:
56
56
  #
57
57
  # <% @page_title = "A Wonderful Hello" %>
58
- # <%= render "shared/header" %>
58
+ # <%= render "application/header" %>
59
59
  #
60
60
  # Now the header can pick up on the <tt>@page_title</tt> variable and use it for outputting a title tag:
61
61
  #
@@ -65,9 +65,9 @@ module ActionView # :nodoc:
65
65
  #
66
66
  # You can pass local variables to sub templates by using a hash with the variable names as keys and the objects as values:
67
67
  #
68
- # <%= render "shared/header", { headline: "Welcome", person: person } %>
68
+ # <%= render "application/header", { headline: "Welcome", person: person } %>
69
69
  #
70
- # These can now be accessed in <tt>shared/header</tt> with:
70
+ # These can now be accessed in <tt>application/header</tt> with:
71
71
  #
72
72
  # Headline: <%= headline %>
73
73
  # First name: <%= person.first_name %>
@@ -10,7 +10,7 @@ module ActionView
10
10
  MAJOR = 7
11
11
  MINOR = 1
12
12
  TINY = 0
13
- PRE = "beta1"
13
+ PRE = "rc1"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -2521,7 +2521,7 @@ module ActionView
2521
2521
  # * Creates standard HTML attributes for the tag.
2522
2522
  # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
2523
2523
  # * <tt>:multiple</tt> - If set to true, *in most updated browsers* the user will be allowed to select multiple files.
2524
- # * <tt>:include_hidden</tt> - When <tt>multiple: true</tt> and <tt>include_hidden: true</tt>, the field will be prefixed with an <tt><input type="hidden"></tt> field with an empty value to support submitting an empty collection of files.
2524
+ # * <tt>:include_hidden</tt> - When <tt>multiple: true</tt> and <tt>include_hidden: true</tt>, the field will be prefixed with an <tt><input type="hidden"></tt> field with an empty value to support submitting an empty collection of files. Since <tt>include_hidden</tt> will default to <tt>config.active_storage.multiple_file_field_include_hidden</tt> if you don't specify <tt>include_hidden</tt>, you will need to pass <tt>include_hidden: false</tt> to prevent submitting an empty collection of files when passing <tt>multiple: true</tt>.
2525
2525
  # * <tt>:accept</tt> - If set to one or multiple mime-types, the user will be suggested a filter when choosing a file. You still need to set up model validations.
2526
2526
  #
2527
2527
  # ==== Examples
@@ -9,9 +9,9 @@ module ActionView
9
9
  # Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in
10
10
  # repeated setups. The inclusion pattern has pages that look like this:
11
11
  #
12
- # <%= render "shared/header" %>
12
+ # <%= render "application/header" %>
13
13
  # Hello World
14
- # <%= render "shared/footer" %>
14
+ # <%= render "application/footer" %>
15
15
  #
16
16
  # This approach is a decent way of keeping common structures isolated from the changing content, but it's verbose
17
17
  # and if you ever want to change the structure of these two includes, you'll have to change all the templates.
@@ -96,11 +96,58 @@ module ActionView
96
96
  #
97
97
  # Given this sub template rendering:
98
98
  #
99
- # <%= render "shared/header", { headline: "Welcome", person: person } %>
99
+ # <%= render "application/header", { headline: "Welcome", person: person } %>
100
100
  #
101
101
  # You can use +local_assigns+ in the sub templates to access the local variables:
102
102
  #
103
103
  # local_assigns[:headline] # => "Welcome"
104
+ #
105
+ # Each key in +local_assigns+ is available as a partial-local variable:
106
+ #
107
+ # local_assigns[:headline] # => "Welcome"
108
+ # headline # => "Welcome"
109
+ #
110
+ # Since +local_assigns+ is a +Hash+, it's compatible with Ruby 3.1's pattern
111
+ # matching assignment operator:
112
+ #
113
+ # local_assigns => { headline:, **options }
114
+ # headline # => "Welcome"
115
+ # options # => {}
116
+ #
117
+ # Pattern matching assignment also supports variable renaming:
118
+ #
119
+ # local_assigns => { headline: title }
120
+ # title # => "Welcome"
121
+ #
122
+ # If a template refers to a variable that isn't passed into the view as part
123
+ # of the <tt>locals: { ... }</tt> Hash, the template will raise an
124
+ # +ActionView::Template::Error+:
125
+ #
126
+ # <%# => raises ActionView::Template::Error %>
127
+ # <% alerts.each do |alert| %>
128
+ # <p><%= alert %></p>
129
+ # <% end %>
130
+ #
131
+ # Since +local_assigns+ returns a +Hash+ instance, you can conditionally
132
+ # read a variable, then fall back to a default value when
133
+ # the key isn't part of the <tt>locals: { ... }</tt> options:
134
+ #
135
+ # <% local_assigns.fetch(:alerts, []).each do |alert| %>
136
+ # <p><%= alert %></p>
137
+ # <% end %>
138
+ #
139
+ # Combining Ruby 3.1's pattern matching assignment with calls to
140
+ # +Hash#with_defaults+ enables compact partial-local variable
141
+ # assignments:
142
+ #
143
+ # <% local_assigns.with_defaults(alerts: []) => { headline:, alerts: } %>
144
+ #
145
+ # <h1><%= headline %></h1>
146
+ #
147
+ # <% alerts.each do |alert| %>
148
+ # <p><%= alert %></p>
149
+ # <% end %>
150
+ #
104
151
 
105
152
  eager_autoload do
106
153
  autoload :Error
@@ -9,6 +9,9 @@ require "rails-dom-testing"
9
9
 
10
10
  module ActionView
11
11
  # = Action View Test Case
12
+ #
13
+ # Read more about <tt>ActionView::TestCase</tt> in {Testing Rails Applications}[https://guides.rubyonrails.org/testing.html#testing-view-partials]
14
+ # in the guides.
12
15
  class TestCase < ActiveSupport::TestCase
13
16
  class TestController < ActionController::Base
14
17
  include ActionDispatch::TestProcess
@@ -57,9 +60,96 @@ module ActionView
57
60
  include ActiveSupport::Testing::ConstantLookup
58
61
 
59
62
  delegate :lookup_context, to: :controller
60
- attr_accessor :controller, :request, :output_buffer, :rendered
63
+ attr_accessor :controller, :request, :output_buffer
61
64
 
62
65
  module ClassMethods
66
+ def inherited(descendant) # :nodoc:
67
+ super
68
+
69
+ descendant_content_class = content_class.dup
70
+
71
+ if descendant_content_class.respond_to?(:set_temporary_name)
72
+ descendant_content_class.set_temporary_name("rendered_content")
73
+ end
74
+
75
+ descendant.content_class = descendant_content_class
76
+ end
77
+
78
+ # Register a callable to parse rendered content for a given template
79
+ # format.
80
+ #
81
+ # Each registered parser will also define a +#rendered.[FORMAT]+ helper
82
+ # method, where +[FORMAT]+ corresponds to the value of the
83
+ # +format+ argument.
84
+ #
85
+ # === Arguments
86
+ #
87
+ # <tt>format</tt> - Symbol the name of the format used to render view's content
88
+ # <tt>callable</tt> - Callable to parse the String. Accepts the String.
89
+ # value as its only argument.
90
+ # <tt>block</tt> - Block serves as the parser when the
91
+ # <tt>callable</tt> is omitted.
92
+ #
93
+ # By default, ActionView::TestCase defines a parser for:
94
+ #
95
+ # * :html - returns an instance of Nokogiri::XML::Node
96
+ # * :json - returns an instance of ActiveSupport::HashWithIndifferentAccess
97
+ #
98
+ # Each pre-registered parser also defines a corresponding helper:
99
+ #
100
+ # * :html - defines `rendered.html`
101
+ # * :json - defines `rendered.json`
102
+ #
103
+ # === Examples
104
+ #
105
+ # test "renders HTML" do
106
+ # article = Article.create!(title: "Hello, world")
107
+ #
108
+ # render partial: "articles/article", locals: { article: article }
109
+ #
110
+ # assert_pattern { rendered.html.at("main h1") => { content: "Hello, world" } }
111
+ # end
112
+ #
113
+ # test "renders JSON" do
114
+ # article = Article.create!(title: "Hello, world")
115
+ #
116
+ # render formats: :json, partial: "articles/article", locals: { article: article }
117
+ #
118
+ # assert_pattern { rendered.json => { title: "Hello, world" } }
119
+ # end
120
+ #
121
+ # To parse the rendered content into RSS, register a call to <tt>RSS::Parser.parse</tt>:
122
+ #
123
+ # register_parser :rss, -> rendered { RSS::Parser.parse(rendered) }
124
+ #
125
+ # test "renders RSS" do
126
+ # article = Article.create!(title: "Hello, world")
127
+ #
128
+ # render formats: :rss, partial: article
129
+ #
130
+ # assert_equal "Hello, world", rendered.rss.items.last.title
131
+ # end
132
+ #
133
+ # To parse the rendered content into a Capybara::Simple::Node,
134
+ # re-register an <tt>:html</tt> parser with a call to
135
+ # <tt>Capybara.string</tt>:
136
+ #
137
+ # register_parser :html, -> rendered { Capybara.string(rendered) }
138
+ #
139
+ # test "renders HTML" do
140
+ # article = Article.create!(title: "Hello, world")
141
+ #
142
+ # render partial: article
143
+ #
144
+ # rendered.html.assert_css "h1", text: "Hello, world"
145
+ # end
146
+ def register_parser(format, callable = nil, &block)
147
+ parser = callable || block || :itself.to_proc
148
+ content_class.redefine_method(format) do
149
+ parser.call(to_s)
150
+ end
151
+ end
152
+
63
153
  def tests(helper_class)
64
154
  case helper_class
65
155
  when String, Symbol
@@ -105,6 +195,27 @@ module ActionView
105
195
  end
106
196
  end
107
197
 
198
+ included do
199
+ class_attribute :content_class, instance_accessor: false, default: Content
200
+
201
+ setup :setup_with_controller
202
+
203
+ register_parser :html, -> rendered { Rails::Dom::Testing.html_document.parse(rendered).root }
204
+ register_parser :json, -> rendered { JSON.parse(rendered, object_class: ActiveSupport::HashWithIndifferentAccess) }
205
+
206
+ ActiveSupport.run_load_hooks(:action_view_test_case, self)
207
+
208
+ helper do
209
+ def protect_against_forgery?
210
+ false
211
+ end
212
+
213
+ def _test_case
214
+ controller._test_case
215
+ end
216
+ end
217
+ end
218
+
108
219
  def setup_with_controller
109
220
  controller_class = Class.new(ActionView::TestCase::TestController)
110
221
  @controller = controller_class.new
@@ -131,10 +242,64 @@ module ActionView
131
242
  @_rendered_views ||= RenderedViewsCollection.new
132
243
  end
133
244
 
245
+ # Returns the content rendered by the last +render+ call.
246
+ #
247
+ # The returned object behaves like a string but also exposes a number of methods
248
+ # that allows you to parse the content string in formats registered using
249
+ # <tt>.register_parser</tt>.
250
+ #
251
+ # By default includes the following parsers:
252
+ #
253
+ # +.html+
254
+ #
255
+ # Parse the <tt>rendered</tt> content String into HTML. By default, this means
256
+ # a <tt>Nokogiri::XML::Node</tt>.
257
+ #
258
+ # test "renders HTML" do
259
+ # article = Article.create!(title: "Hello, world")
260
+ #
261
+ # render partial: "articles/article", locals: { article: article }
262
+ #
263
+ # assert_pattern { rendered.html.at("main h1") => { content: "Hello, world" } }
264
+ # end
265
+ #
266
+ # To parse the rendered content into a <tt>Capybara::Simple::Node</tt>,
267
+ # re-register an <tt>:html</tt> parser with a call to
268
+ # <tt>Capybara.string</tt>:
269
+ #
270
+ # register_parser :html, -> rendered { Capybara.string(rendered) }
271
+ #
272
+ # test "renders HTML" do
273
+ # article = Article.create!(title: "Hello, world")
274
+ #
275
+ # render partial: article
276
+ #
277
+ # rendered.html.assert_css "h1", text: "Hello, world"
278
+ # end
279
+ #
280
+ # +.json+
281
+ #
282
+ # Parse the <tt>rendered</tt> content String into JSON. By default, this means
283
+ # a <tt>ActiveSupport::HashWithIndifferentAccess</tt>.
284
+ #
285
+ # test "renders JSON" do
286
+ # article = Article.create!(title: "Hello, world")
287
+ #
288
+ # render formats: :json, partial: "articles/article", locals: { article: article }
289
+ #
290
+ # assert_pattern { rendered.json => { title: "Hello, world" } }
291
+ # end
292
+ def rendered
293
+ @_rendered ||= self.class.content_class.new(@rendered)
294
+ end
295
+
134
296
  def _routes
135
297
  @controller._routes if @controller.respond_to?(:_routes)
136
298
  end
137
299
 
300
+ class Content < SimpleDelegator
301
+ end
302
+
138
303
  # Need to experiment if this priority is the best one: rendered => output_buffer
139
304
  class RenderedViewsCollection
140
305
  def initialize
@@ -161,21 +326,6 @@ module ActionView
161
326
  end
162
327
  end
163
328
 
164
- included do
165
- setup :setup_with_controller
166
- ActiveSupport.run_load_hooks(:action_view_test_case, self)
167
-
168
- helper do
169
- def protect_against_forgery?
170
- false
171
- end
172
-
173
- def _test_case
174
- controller._test_case
175
- end
176
- end
177
- end
178
-
179
329
  private
180
330
  # Need to experiment if this priority is the best one: rendered => output_buffer
181
331
  def document_root_element
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: actionview
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.1.0.beta1
4
+ version: 7.1.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-13 00:00:00.000000000 Z
11
+ date: 2023-09-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 7.1.0.beta1
19
+ version: 7.1.0.rc1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 7.1.0.beta1
26
+ version: 7.1.0.rc1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: builder
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -86,28 +86,28 @@ dependencies:
86
86
  requirements:
87
87
  - - '='
88
88
  - !ruby/object:Gem::Version
89
- version: 7.1.0.beta1
89
+ version: 7.1.0.rc1
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - '='
95
95
  - !ruby/object:Gem::Version
96
- version: 7.1.0.beta1
96
+ version: 7.1.0.rc1
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: activemodel
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - '='
102
102
  - !ruby/object:Gem::Version
103
- version: 7.1.0.beta1
103
+ version: 7.1.0.rc1
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - '='
109
109
  - !ruby/object:Gem::Version
110
- version: 7.1.0.beta1
110
+ version: 7.1.0.rc1
111
111
  description: Simple, battle-tested conventions and helpers for building web pages.
112
112
  email: david@loudthinking.com
113
113
  executables: []
@@ -246,10 +246,10 @@ licenses:
246
246
  - MIT
247
247
  metadata:
248
248
  bug_tracker_uri: https://github.com/rails/rails/issues
249
- changelog_uri: https://github.com/rails/rails/blob/v7.1.0.beta1/actionview/CHANGELOG.md
250
- documentation_uri: https://api.rubyonrails.org/v7.1.0.beta1/
249
+ changelog_uri: https://github.com/rails/rails/blob/v7.1.0.rc1/actionview/CHANGELOG.md
250
+ documentation_uri: https://api.rubyonrails.org/v7.1.0.rc1/
251
251
  mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
252
- source_code_uri: https://github.com/rails/rails/tree/v7.1.0.beta1/actionview
252
+ source_code_uri: https://github.com/rails/rails/tree/v7.1.0.rc1/actionview
253
253
  rubygems_mfa_required: 'true'
254
254
  post_install_message:
255
255
  rdoc_options: []