view_component 3.8.0 → 3.9.0
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 +4 -4
- data/docs/CHANGELOG.md +45 -0
- data/lib/rails/generators/locale/component_generator.rb +3 -3
- data/lib/view_component/base.rb +42 -4
- data/lib/view_component/compiler.rb +8 -2
- data/lib/view_component/engine.rb +14 -0
- data/lib/view_component/preview.rb +5 -1
- data/lib/view_component/rails/tasks/view_component.rake +2 -1
- data/lib/view_component/slotable.rb +6 -6
- data/lib/view_component/test_helpers.rb +5 -2
- data/lib/view_component/translatable.rb +10 -2
- data/lib/view_component/version.rb +1 -1
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7d167b54bcd09ae6e5a8ace98cdb2cfac7386a29029cd8b4b34d1e5aa00093fd
|
4
|
+
data.tar.gz: 5ea5073913c8c6026dbf5d5e6d04671bc28a42564a9788988dfe0ee18d03af9d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2232e4237c3d851577dc10cb9054c03a7614113761e2f6b2b33e3005ad05e665dd1031d29c84f10fcc7bef1a72fa3529866d11082c4ddbc9dd395e415ae5b2b9
|
7
|
+
data.tar.gz: 67d9aa02848b03e68b5eb56c2bb37d27b39d757d53b4a30b466c9f7e73db29fe5771f2732f6447a135a3d1a120abc387350451b8d875923ce3dae6db06f5ce24
|
data/docs/CHANGELOG.md
CHANGED
@@ -10,6 +10,40 @@ nav_order: 5
|
|
10
10
|
|
11
11
|
## main
|
12
12
|
|
13
|
+
## 3.9.0
|
14
|
+
|
15
|
+
* Don’t break `rails stats` if ViewComponent path is missing.
|
16
|
+
|
17
|
+
*Claudio Baccigalupo*
|
18
|
+
|
19
|
+
* Add deprecation warnings for EOL ruby and Rails versions and patches associated with them.
|
20
|
+
|
21
|
+
*Reegan Viljoen*
|
22
|
+
|
23
|
+
* Add support for Ruby 3.3.
|
24
|
+
|
25
|
+
*Reegan Viljoen*
|
26
|
+
|
27
|
+
* Allow translations to be inherited and overridden in subclasses.
|
28
|
+
|
29
|
+
*Elia Schito*
|
30
|
+
|
31
|
+
* Resolve console warnings when running test suite.
|
32
|
+
|
33
|
+
*Joel Hawksley*
|
34
|
+
|
35
|
+
* Fix spelling in a local variable.
|
36
|
+
|
37
|
+
*Olle Jonsson*
|
38
|
+
|
39
|
+
* Avoid duplicating rendered string when `output_postamble` is blank.
|
40
|
+
|
41
|
+
*Mitchell Henke*
|
42
|
+
|
43
|
+
* Ensure HTML output safety.
|
44
|
+
|
45
|
+
*Cameron Dutro*
|
46
|
+
|
13
47
|
## 3.8.0
|
14
48
|
|
15
49
|
* Use correct value for the `config.action_dispatch.show_exceptions` config option for edge Rails.
|
@@ -225,6 +259,17 @@ This release makes the following breaking changes, many of which have long been
|
|
225
259
|
|
226
260
|
*Joel Hawksley*
|
227
261
|
|
262
|
+
For example:
|
263
|
+
|
264
|
+
```diff
|
265
|
+
<%= render BlogComponent.new do |component| %>
|
266
|
+
- <% component.header do %>
|
267
|
+
+ <% component.with_header do %>
|
268
|
+
<%= link_to "My blog", root_path %>
|
269
|
+
<% end %>
|
270
|
+
<% end %>
|
271
|
+
```
|
272
|
+
|
228
273
|
* BREAKING: Remove deprecated SlotsV1 in favor of current SlotsV2.
|
229
274
|
|
230
275
|
*Joel Hawksley*
|
@@ -34,11 +34,11 @@ module Locale
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def destination(locale = nil)
|
37
|
-
|
37
|
+
extension = ".#{locale}" if locale
|
38
38
|
if sidecar?
|
39
|
-
File.join(component_path, class_path, "#{file_name}_component", "#{file_name}_component#{
|
39
|
+
File.join(component_path, class_path, "#{file_name}_component", "#{file_name}_component#{extension}.yml")
|
40
40
|
else
|
41
|
-
File.join(component_path, class_path, "#{file_name}_component#{
|
41
|
+
File.join(component_path, class_path, "#{file_name}_component#{extension}.yml")
|
42
42
|
end
|
43
43
|
end
|
44
44
|
end
|
data/lib/view_component/base.rb
CHANGED
@@ -104,7 +104,12 @@ module ViewComponent
|
|
104
104
|
before_render
|
105
105
|
|
106
106
|
if render?
|
107
|
-
|
107
|
+
# Avoid allocating new string when output_postamble is blank
|
108
|
+
if output_postamble.blank?
|
109
|
+
safe_render_template_for(@__vc_variant).to_s
|
110
|
+
else
|
111
|
+
safe_render_template_for(@__vc_variant).to_s + safe_output_postamble
|
112
|
+
end
|
108
113
|
else
|
109
114
|
""
|
110
115
|
end
|
@@ -155,7 +160,7 @@ module ViewComponent
|
|
155
160
|
#
|
156
161
|
# @return [String]
|
157
162
|
def output_postamble
|
158
|
-
""
|
163
|
+
@@default_output_postamble ||= "".html_safe
|
159
164
|
end
|
160
165
|
|
161
166
|
# Called before rendering the component. Override to perform operations that
|
@@ -221,6 +226,7 @@ module ViewComponent
|
|
221
226
|
end
|
222
227
|
|
223
228
|
if ::Rails.env.development? || ::Rails.env.test?
|
229
|
+
# @private
|
224
230
|
def method_missing(method_name, *args) # rubocop:disable Style/MissingRespondToMissing
|
225
231
|
super
|
226
232
|
rescue => e # rubocop:disable Style/RescueStandardError
|
@@ -301,6 +307,38 @@ module ViewComponent
|
|
301
307
|
defined?(@__vc_content_evaluated) && @__vc_content_evaluated
|
302
308
|
end
|
303
309
|
|
310
|
+
def maybe_escape_html(text)
|
311
|
+
return text if request && !request.format.html?
|
312
|
+
return text if text.nil? || text.empty?
|
313
|
+
|
314
|
+
if text.html_safe?
|
315
|
+
text
|
316
|
+
else
|
317
|
+
yield
|
318
|
+
html_escape(text)
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
def safe_render_template_for(variant)
|
323
|
+
if compiler.renders_template_for_variant?(variant)
|
324
|
+
render_template_for(variant)
|
325
|
+
else
|
326
|
+
maybe_escape_html(render_template_for(variant)) do
|
327
|
+
Kernel.warn("WARNING: The #{self.class} component rendered HTML-unsafe output. The output will be automatically escaped, but you may want to investigate.")
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
def safe_output_postamble
|
333
|
+
maybe_escape_html(output_postamble) do
|
334
|
+
Kernel.warn("WARNING: The #{self.class} component was provided an HTML-unsafe postamble. The postamble will be automatically escaped, but you may want to investigate.")
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
def compiler
|
339
|
+
@compiler ||= self.class.compiler
|
340
|
+
end
|
341
|
+
|
304
342
|
# Set the controller used for testing components:
|
305
343
|
#
|
306
344
|
# ```ruby
|
@@ -616,7 +654,7 @@ module ViewComponent
|
|
616
654
|
|
617
655
|
# @private
|
618
656
|
def collection_counter_parameter
|
619
|
-
"#{collection_parameter}_counter"
|
657
|
+
:"#{collection_parameter}_counter"
|
620
658
|
end
|
621
659
|
|
622
660
|
# @private
|
@@ -626,7 +664,7 @@ module ViewComponent
|
|
626
664
|
|
627
665
|
# @private
|
628
666
|
def collection_iteration_parameter
|
629
|
-
"#{collection_parameter}_iteration"
|
667
|
+
:"#{collection_parameter}_iteration"
|
630
668
|
end
|
631
669
|
|
632
670
|
# @private
|
@@ -16,6 +16,7 @@ module ViewComponent
|
|
16
16
|
def initialize(component_class)
|
17
17
|
@component_class = component_class
|
18
18
|
@redefinition_lock = Mutex.new
|
19
|
+
@variants_rendering_templates = Set.new
|
19
20
|
end
|
20
21
|
|
21
22
|
def compiled?
|
@@ -56,7 +57,7 @@ module ViewComponent
|
|
56
57
|
RUBY
|
57
58
|
# rubocop:enable Style/EvalWithLocation
|
58
59
|
|
59
|
-
component_class.define_method("_call_#{safe_class_name}", component_class.instance_method(:call))
|
60
|
+
component_class.define_method(:"_call_#{safe_class_name}", component_class.instance_method(:call))
|
60
61
|
|
61
62
|
component_class.silence_redefinition_of_method("render_template_for")
|
62
63
|
component_class.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
@@ -68,6 +69,7 @@ module ViewComponent
|
|
68
69
|
else
|
69
70
|
templates.each do |template|
|
70
71
|
method_name = call_method_name(template[:variant])
|
72
|
+
@variants_rendering_templates << template[:variant]
|
71
73
|
|
72
74
|
redefinition_lock.synchronize do
|
73
75
|
component_class.silence_redefinition_of_method(method_name)
|
@@ -89,6 +91,10 @@ module ViewComponent
|
|
89
91
|
CompileCache.register(component_class)
|
90
92
|
end
|
91
93
|
|
94
|
+
def renders_template_for_variant?(variant)
|
95
|
+
@variants_rendering_templates.include?(variant)
|
96
|
+
end
|
97
|
+
|
92
98
|
private
|
93
99
|
|
94
100
|
attr_reader :component_class, :redefinition_lock
|
@@ -101,7 +107,7 @@ module ViewComponent
|
|
101
107
|
"elsif variant.to_sym == :'#{variant}'\n #{safe_name}"
|
102
108
|
end.join("\n")
|
103
109
|
|
104
|
-
component_class.define_method("_call_#{safe_class_name}", component_class.instance_method(:call))
|
110
|
+
component_class.define_method(:"_call_#{safe_class_name}", component_class.instance_method(:call))
|
105
111
|
|
106
112
|
body = <<-RUBY
|
107
113
|
if variant.nil?
|
@@ -81,6 +81,8 @@ module ViewComponent
|
|
81
81
|
next if Rails.version.to_f >= 6.1 || !app.config.view_component.render_monkey_patch_enabled
|
82
82
|
|
83
83
|
# :nocov:
|
84
|
+
ViewComponent::Deprecation.deprecation_warning("Monkey patching `render`", "ViewComponent 4.0 will remove the `render` monkey patch")
|
85
|
+
|
84
86
|
ActiveSupport.on_load(:action_view) do
|
85
87
|
require "view_component/render_monkey_patch"
|
86
88
|
ActionView::Base.prepend ViewComponent::RenderMonkeyPatch
|
@@ -99,6 +101,8 @@ module ViewComponent
|
|
99
101
|
next if Rails.version.to_f >= 6.1
|
100
102
|
|
101
103
|
# :nocov:
|
104
|
+
ViewComponent::Deprecation.deprecation_warning("using `render_component`", "ViewComponent 4.0 will remove `render_component`")
|
105
|
+
|
102
106
|
ActiveSupport.on_load(:action_view) do
|
103
107
|
require "view_component/render_component_helper"
|
104
108
|
ActionView::Base.include ViewComponent::RenderComponentHelper
|
@@ -160,6 +164,16 @@ module ViewComponent
|
|
160
164
|
end
|
161
165
|
end
|
162
166
|
|
167
|
+
# :nocov:
|
168
|
+
if RUBY_VERSION < "3.0.0"
|
169
|
+
ViewComponent::Deprecation.deprecation_warning("Support for Ruby versions < 3.0.0", "ViewComponent 4.0 will remove support for Ruby versions < 3.0.0 ")
|
170
|
+
end
|
171
|
+
|
172
|
+
if Rails.version.to_f < 6.1
|
173
|
+
ViewComponent::Deprecation.deprecation_warning("Support for Rails versions < 6.1", "ViewComponent 4.0 will remove support for Rails versions < 6.1 ")
|
174
|
+
end
|
175
|
+
# :nocov:
|
176
|
+
|
163
177
|
app.executor.to_run :before do
|
164
178
|
CompileCache.invalidate! unless ActionView::Base.cache_template_loading
|
165
179
|
end
|
@@ -4,7 +4,11 @@ require "active_support/descendants_tracker"
|
|
4
4
|
|
5
5
|
module ViewComponent # :nodoc:
|
6
6
|
class Preview
|
7
|
-
|
7
|
+
if defined?(Rails.application.routes.url_helpers)
|
8
|
+
# Workaround from https://stackoverflow.com/questions/20853526/make-yard-ignore-certain-class-extensions to appease YARD
|
9
|
+
send(:include, Rails.application.routes.url_helpers)
|
10
|
+
end
|
11
|
+
|
8
12
|
include ActionView::Helpers::TagHelper
|
9
13
|
include ActionView::Helpers::AssetTagHelper
|
10
14
|
extend ActiveSupport::DescendantsTracker
|
@@ -7,7 +7,8 @@ namespace :view_component do
|
|
7
7
|
# :nocov:
|
8
8
|
require "rails/code_statistics"
|
9
9
|
|
10
|
-
|
10
|
+
dir = ViewComponent::Base.view_component_path
|
11
|
+
::STATS_DIRECTORIES << ["ViewComponents", dir] if File.directory?(Rails.root + dir)
|
11
12
|
# :nocov:
|
12
13
|
end
|
13
14
|
end
|
@@ -93,11 +93,11 @@ module ViewComponent
|
|
93
93
|
get_slot(slot_name)
|
94
94
|
end
|
95
95
|
|
96
|
-
define_method "#{slot_name}?" do
|
96
|
+
define_method :"#{slot_name}?" do
|
97
97
|
get_slot(slot_name).present?
|
98
98
|
end
|
99
99
|
|
100
|
-
define_method "with_#{slot_name}_content" do |content|
|
100
|
+
define_method :"with_#{slot_name}_content" do |content|
|
101
101
|
send(setter_method_name) { content.to_s }
|
102
102
|
|
103
103
|
self
|
@@ -160,7 +160,7 @@ module ViewComponent
|
|
160
160
|
end
|
161
161
|
ruby2_keywords(setter_method_name) if respond_to?(:ruby2_keywords, true)
|
162
162
|
|
163
|
-
define_method "with_#{singular_name}_content" do |content|
|
163
|
+
define_method :"with_#{singular_name}_content" do |content|
|
164
164
|
send(setter_method_name) { content.to_s }
|
165
165
|
|
166
166
|
self
|
@@ -180,7 +180,7 @@ module ViewComponent
|
|
180
180
|
get_slot(slot_name)
|
181
181
|
end
|
182
182
|
|
183
|
-
define_method "#{slot_name}?" do
|
183
|
+
define_method :"#{slot_name}?" do
|
184
184
|
get_slot(slot_name).present?
|
185
185
|
end
|
186
186
|
|
@@ -211,7 +211,7 @@ module ViewComponent
|
|
211
211
|
get_slot(slot_name)
|
212
212
|
end
|
213
213
|
|
214
|
-
define_method("#{slot_name}?") do
|
214
|
+
define_method(:"#{slot_name}?") do
|
215
215
|
get_slot(slot_name).present?
|
216
216
|
end
|
217
217
|
|
@@ -246,7 +246,7 @@ module ViewComponent
|
|
246
246
|
end
|
247
247
|
ruby2_keywords(setter_method_name) if respond_to?(:ruby2_keywords, true)
|
248
248
|
|
249
|
-
define_method "with_#{poly_slot_name}_content" do |content|
|
249
|
+
define_method :"with_#{poly_slot_name}_content" do |content|
|
250
250
|
send(setter_method_name) { content.to_s }
|
251
251
|
|
252
252
|
self
|
@@ -175,16 +175,17 @@ module ViewComponent
|
|
175
175
|
# end
|
176
176
|
# ```
|
177
177
|
#
|
178
|
-
# @param
|
178
|
+
# @param full_path [String] The path to set for the current request.
|
179
179
|
# @param host [String] The host to set for the current request.
|
180
180
|
# @param method [String] The request method to set for the current request.
|
181
|
-
def with_request_url(full_path, host: nil, method: nil)
|
181
|
+
def with_request_url(full_path, host: nil, method: nil, format: :html)
|
182
182
|
old_request_host = vc_test_request.host
|
183
183
|
old_request_method = vc_test_request.request_method
|
184
184
|
old_request_path_info = vc_test_request.path_info
|
185
185
|
old_request_path_parameters = vc_test_request.path_parameters
|
186
186
|
old_request_query_parameters = vc_test_request.query_parameters
|
187
187
|
old_request_query_string = vc_test_request.query_string
|
188
|
+
old_request_format = vc_test_request.format.symbol
|
188
189
|
old_controller = defined?(@vc_test_controller) && @vc_test_controller
|
189
190
|
|
190
191
|
path, query = full_path.split("?", 2)
|
@@ -197,6 +198,7 @@ module ViewComponent
|
|
197
198
|
vc_test_request.set_header("action_dispatch.request.query_parameters",
|
198
199
|
Rack::Utils.parse_nested_query(query).with_indifferent_access)
|
199
200
|
vc_test_request.set_header(Rack::QUERY_STRING, query)
|
201
|
+
vc_test_request.format = format
|
200
202
|
yield
|
201
203
|
ensure
|
202
204
|
vc_test_request.host = old_request_host
|
@@ -205,6 +207,7 @@ module ViewComponent
|
|
205
207
|
vc_test_request.path_parameters = old_request_path_parameters
|
206
208
|
vc_test_request.set_header("action_dispatch.request.query_parameters", old_request_query_parameters)
|
207
209
|
vc_test_request.set_header(Rack::QUERY_STRING, old_request_query_string)
|
210
|
+
vc_test_request.format = old_request_format
|
208
211
|
@vc_test_controller = old_controller
|
209
212
|
end
|
210
213
|
|
@@ -10,6 +10,7 @@ module ViewComponent
|
|
10
10
|
extend ActiveSupport::Concern
|
11
11
|
|
12
12
|
HTML_SAFE_TRANSLATION_KEY = /(?:_|\b)html\z/
|
13
|
+
TRANSLATION_EXTENSIONS = %w[yml yaml].freeze
|
13
14
|
|
14
15
|
included do
|
15
16
|
class_attribute :i18n_backend, instance_writer: false, instance_predicate: false
|
@@ -23,9 +24,16 @@ module ViewComponent
|
|
23
24
|
def build_i18n_backend
|
24
25
|
return if compiled?
|
25
26
|
|
26
|
-
|
27
|
-
|
27
|
+
# We need to load the translations files from the ancestors so a component
|
28
|
+
# can inherit translations from its parent and is able to overwrite them.
|
29
|
+
translation_files = ancestors.reverse_each.with_object([]) do |ancestor, files|
|
30
|
+
if ancestor.is_a?(Class) && ancestor < ViewComponent::Base
|
31
|
+
files.concat(ancestor.sidecar_files(TRANSLATION_EXTENSIONS))
|
32
|
+
end
|
33
|
+
end
|
28
34
|
|
35
|
+
# In development it will become nil if the translations file is removed
|
36
|
+
self.i18n_backend = if translation_files.any?
|
29
37
|
I18nBackend.new(
|
30
38
|
i18n_scope: i18n_scope,
|
31
39
|
load_paths: translation_files
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: view_component
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ViewComponent Team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -78,14 +78,14 @@ dependencies:
|
|
78
78
|
requirements:
|
79
79
|
- - "~>"
|
80
80
|
- !ruby/object:Gem::Version
|
81
|
-
version: 2.
|
81
|
+
version: 2.13.0
|
82
82
|
type: :development
|
83
83
|
prerelease: false
|
84
84
|
version_requirements: !ruby/object:Gem::Requirement
|
85
85
|
requirements:
|
86
86
|
- - "~>"
|
87
87
|
- !ruby/object:Gem::Version
|
88
|
-
version: 2.
|
88
|
+
version: 2.13.0
|
89
89
|
- !ruby/object:Gem::Dependency
|
90
90
|
name: better_html
|
91
91
|
requirement: !ruby/object:Gem::Requirement
|
@@ -302,14 +302,14 @@ dependencies:
|
|
302
302
|
requirements:
|
303
303
|
- - "~>"
|
304
304
|
- !ruby/object:Gem::Version
|
305
|
-
version: 0.9.
|
305
|
+
version: 0.9.34
|
306
306
|
type: :development
|
307
307
|
prerelease: false
|
308
308
|
version_requirements: !ruby/object:Gem::Requirement
|
309
309
|
requirements:
|
310
310
|
- - "~>"
|
311
311
|
- !ruby/object:Gem::Version
|
312
|
-
version: 0.9.
|
312
|
+
version: 0.9.34
|
313
313
|
- !ruby/object:Gem::Dependency
|
314
314
|
name: yard-activesupport-concern
|
315
315
|
requirement: !ruby/object:Gem::Requirement
|
@@ -421,7 +421,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
421
421
|
- !ruby/object:Gem::Version
|
422
422
|
version: '0'
|
423
423
|
requirements: []
|
424
|
-
rubygems_version: 3.
|
424
|
+
rubygems_version: 3.5.3
|
425
425
|
signing_key:
|
426
426
|
specification_version: 4
|
427
427
|
summary: A framework for building reusable, testable & encapsulated view components
|