view_component 3.7.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/app/helpers/preview_helper.rb +20 -0
- data/app/views/view_components/_preview_source.html.erb +2 -2
- data/docs/CHANGELOG.md +89 -0
- data/lib/rails/generators/locale/component_generator.rb +3 -3
- data/lib/view_component/base.rb +44 -5
- data/lib/view_component/compiler.rb +12 -4
- data/lib/view_component/engine.rb +23 -1
- data/lib/view_component/errors.rb +18 -3
- 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 +24 -7
- data/lib/view_component/test_helpers.rb +24 -3
- data/lib/view_component/translatable.rb +10 -2
- data/lib/view_component/use_helpers.rb +20 -0
- data/lib/view_component/version.rb +1 -1
- metadata +8 -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
|
@@ -1,6 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module PreviewHelper
|
4
|
+
# :nocov:
|
5
|
+
include ActionView::Helpers::AssetUrlHelper if Rails.version.to_f < 6.1
|
6
|
+
# :nocov:
|
7
|
+
|
4
8
|
AVAILABLE_PRISM_LANGUAGES = %w[ruby erb haml]
|
5
9
|
FALLBACK_LANGUAGE = "ruby"
|
6
10
|
|
@@ -10,6 +14,14 @@ module PreviewHelper
|
|
10
14
|
render "preview_source"
|
11
15
|
end
|
12
16
|
|
17
|
+
def prism_css_source_url
|
18
|
+
serve_static_preview_assets? ? asset_path("prism.css", skip_pipeline: true) : "https://cdn.jsdelivr.net/npm/prismjs@1.28.0/themes/prism.min.css"
|
19
|
+
end
|
20
|
+
|
21
|
+
def prism_js_source_url
|
22
|
+
serve_static_preview_assets? ? asset_path("prism.min.js", skip_pipeline: true) : "https://cdn.jsdelivr.net/npm/prismjs@1.28.0/prism.min.js"
|
23
|
+
end
|
24
|
+
|
13
25
|
def find_template_data(lookup_context:, template_identifier:)
|
14
26
|
template = lookup_context.find_template(template_identifier)
|
15
27
|
|
@@ -18,6 +30,7 @@ module PreviewHelper
|
|
18
30
|
source: template.source,
|
19
31
|
prism_language_name: prism_language_name_by_template(template: template)
|
20
32
|
}
|
33
|
+
# :nocov:
|
21
34
|
else
|
22
35
|
# Fetch template source via finding it through preview paths
|
23
36
|
# to accomodate source view when exclusively using templates
|
@@ -43,6 +56,7 @@ module PreviewHelper
|
|
43
56
|
prism_language_name: prism_language_name
|
44
57
|
}
|
45
58
|
end
|
59
|
+
# :nocov:
|
46
60
|
end
|
47
61
|
|
48
62
|
private
|
@@ -55,6 +69,7 @@ module PreviewHelper
|
|
55
69
|
language
|
56
70
|
end
|
57
71
|
|
72
|
+
# :nocov:
|
58
73
|
def prism_language_name_by_template_path(template_file_path:)
|
59
74
|
language = template_file_path.gsub(".html", "").split(".").last
|
60
75
|
|
@@ -62,4 +77,9 @@ module PreviewHelper
|
|
62
77
|
|
63
78
|
language
|
64
79
|
end
|
80
|
+
# :nocov:
|
81
|
+
|
82
|
+
def serve_static_preview_assets?
|
83
|
+
ViewComponent::Base.config.show_previews && Rails.application.config.public_file_server.enabled
|
84
|
+
end
|
65
85
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<link href="<%=
|
1
|
+
<link href="<%= prism_css_source_url %>" media="screen" rel="stylesheet" type="text/css">
|
2
2
|
<div class="view-component-source-example">
|
3
3
|
<h2>Source:</h2>
|
4
4
|
<pre class="source">
|
@@ -14,4 +14,4 @@
|
|
14
14
|
<% end %>
|
15
15
|
</pre>
|
16
16
|
</div>
|
17
|
-
<script type="text/javascript" src="<%=
|
17
|
+
<script type="text/javascript" src="<%= prism_js_source_url %>"></script>
|
data/docs/CHANGELOG.md
CHANGED
@@ -10,6 +10,84 @@ 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
|
+
|
47
|
+
## 3.8.0
|
48
|
+
|
49
|
+
* Use correct value for the `config.action_dispatch.show_exceptions` config option for edge Rails.
|
50
|
+
|
51
|
+
*Cameron Dutro*
|
52
|
+
|
53
|
+
* Remove unsupported versions of Rails & Ruby from CI matrix.
|
54
|
+
|
55
|
+
*Reegan Viljoen*
|
56
|
+
|
57
|
+
* Raise error when uncountable slot names are used in `renders_many`
|
58
|
+
|
59
|
+
*Hugo Chantelauze*
|
60
|
+
*Reegan Viljoen*
|
61
|
+
|
62
|
+
* Replace usage of `String#ends_with?` with `String#end_with?` to reduce the dependency on ActiveSupport core extensions.
|
63
|
+
|
64
|
+
*halo*
|
65
|
+
|
66
|
+
* Don't add ActionDispatch::Static middleware unless `public_file_server.enabled`.
|
67
|
+
|
68
|
+
*Daniel Gonzalez*
|
69
|
+
*Reegan Viljoen*
|
70
|
+
|
71
|
+
* Resolve an issue where slots starting with `call` would cause a `NameError`
|
72
|
+
|
73
|
+
*Blake Williams*
|
74
|
+
|
75
|
+
* Add `use_helper` API.
|
76
|
+
|
77
|
+
*Reegan Viljoen*
|
78
|
+
|
79
|
+
* Fix bug where the `Rails` module wasn't being searched from the root namespace.
|
80
|
+
|
81
|
+
*Zenéixe*
|
82
|
+
|
83
|
+
* Fix bug where `#with_request_url`, set the incorrect `request.fullpath`.
|
84
|
+
|
85
|
+
*Nachiket Pusalkar*
|
86
|
+
|
87
|
+
* Allow setting method when using the `with_request_url` test helper.
|
88
|
+
|
89
|
+
*Andrew Duthie*
|
90
|
+
|
13
91
|
## 3.7.0
|
14
92
|
|
15
93
|
* Support Rails 7.1 in CI.
|
@@ -181,6 +259,17 @@ This release makes the following breaking changes, many of which have long been
|
|
181
259
|
|
182
260
|
*Joel Hawksley*
|
183
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
|
+
|
184
273
|
* BREAKING: Remove deprecated SlotsV1 in favor of current SlotsV2.
|
185
274
|
|
186
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
@@ -12,6 +12,7 @@ require "view_component/preview"
|
|
12
12
|
require "view_component/slotable"
|
13
13
|
require "view_component/translatable"
|
14
14
|
require "view_component/with_content_helper"
|
15
|
+
require "view_component/use_helpers"
|
15
16
|
|
16
17
|
module ViewComponent
|
17
18
|
class Base < ActionView::Base
|
@@ -103,7 +104,12 @@ module ViewComponent
|
|
103
104
|
before_render
|
104
105
|
|
105
106
|
if render?
|
106
|
-
|
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
|
107
113
|
else
|
108
114
|
""
|
109
115
|
end
|
@@ -154,7 +160,7 @@ module ViewComponent
|
|
154
160
|
#
|
155
161
|
# @return [String]
|
156
162
|
def output_postamble
|
157
|
-
""
|
163
|
+
@@default_output_postamble ||= "".html_safe
|
158
164
|
end
|
159
165
|
|
160
166
|
# Called before rendering the component. Override to perform operations that
|
@@ -219,7 +225,8 @@ module ViewComponent
|
|
219
225
|
@__vc_helpers ||= __vc_original_view_context || controller.view_context
|
220
226
|
end
|
221
227
|
|
222
|
-
if Rails.env.development? || Rails.env.test?
|
228
|
+
if ::Rails.env.development? || ::Rails.env.test?
|
229
|
+
# @private
|
223
230
|
def method_missing(method_name, *args) # rubocop:disable Style/MissingRespondToMissing
|
224
231
|
super
|
225
232
|
rescue => e # rubocop:disable Style/RescueStandardError
|
@@ -300,6 +307,38 @@ module ViewComponent
|
|
300
307
|
defined?(@__vc_content_evaluated) && @__vc_content_evaluated
|
301
308
|
end
|
302
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
|
+
|
303
342
|
# Set the controller used for testing components:
|
304
343
|
#
|
305
344
|
# ```ruby
|
@@ -615,7 +654,7 @@ module ViewComponent
|
|
615
654
|
|
616
655
|
# @private
|
617
656
|
def collection_counter_parameter
|
618
|
-
"#{collection_parameter}_counter"
|
657
|
+
:"#{collection_parameter}_counter"
|
619
658
|
end
|
620
659
|
|
621
660
|
# @private
|
@@ -625,7 +664,7 @@ module ViewComponent
|
|
625
664
|
|
626
665
|
# @private
|
627
666
|
def collection_iteration_parameter
|
628
|
-
"#{collection_parameter}_iteration"
|
667
|
+
:"#{collection_parameter}_iteration"
|
629
668
|
end
|
630
669
|
|
631
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?
|
@@ -219,12 +225,12 @@ module ViewComponent
|
|
219
225
|
component_class.included_modules
|
220
226
|
)
|
221
227
|
|
222
|
-
view_component_ancestors.flat_map { |ancestor| ancestor.instance_methods(false).grep(/^call/) }.uniq
|
228
|
+
view_component_ancestors.flat_map { |ancestor| ancestor.instance_methods(false).grep(/^call(_|$)/) }.uniq
|
223
229
|
end
|
224
230
|
end
|
225
231
|
|
226
232
|
def inline_calls_defined_on_self
|
227
|
-
@inline_calls_defined_on_self ||= component_class.instance_methods(false).grep(/^call/)
|
233
|
+
@inline_calls_defined_on_self ||= component_class.instance_methods(false).grep(/^call(_|$)/)
|
228
234
|
end
|
229
235
|
|
230
236
|
def variants
|
@@ -258,6 +264,7 @@ module ViewComponent
|
|
258
264
|
|
259
265
|
if handler.method(:call).parameters.length > 1
|
260
266
|
handler.call(component_class, template)
|
267
|
+
# :nocov:
|
261
268
|
else
|
262
269
|
handler.call(
|
263
270
|
OpenStruct.new(
|
@@ -267,6 +274,7 @@ module ViewComponent
|
|
267
274
|
)
|
268
275
|
)
|
269
276
|
end
|
277
|
+
# :nocov:
|
270
278
|
end
|
271
279
|
|
272
280
|
def call_method_name(variant)
|
@@ -80,6 +80,9 @@ module ViewComponent
|
|
80
80
|
initializer "view_component.monkey_patch_render" do |app|
|
81
81
|
next if Rails.version.to_f >= 6.1 || !app.config.view_component.render_monkey_patch_enabled
|
82
82
|
|
83
|
+
# :nocov:
|
84
|
+
ViewComponent::Deprecation.deprecation_warning("Monkey patching `render`", "ViewComponent 4.0 will remove the `render` monkey patch")
|
85
|
+
|
83
86
|
ActiveSupport.on_load(:action_view) do
|
84
87
|
require "view_component/render_monkey_patch"
|
85
88
|
ActionView::Base.prepend ViewComponent::RenderMonkeyPatch
|
@@ -91,11 +94,15 @@ module ViewComponent
|
|
91
94
|
ActionController::Base.prepend ViewComponent::RenderingMonkeyPatch
|
92
95
|
ActionController::Base.prepend ViewComponent::RenderToStringMonkeyPatch
|
93
96
|
end
|
97
|
+
# :nocov:
|
94
98
|
end
|
95
99
|
|
96
100
|
initializer "view_component.include_render_component" do |_app|
|
97
101
|
next if Rails.version.to_f >= 6.1
|
98
102
|
|
103
|
+
# :nocov:
|
104
|
+
ViewComponent::Deprecation.deprecation_warning("using `render_component`", "ViewComponent 4.0 will remove `render_component`")
|
105
|
+
|
99
106
|
ActiveSupport.on_load(:action_view) do
|
100
107
|
require "view_component/render_component_helper"
|
101
108
|
ActionView::Base.include ViewComponent::RenderComponentHelper
|
@@ -107,14 +114,19 @@ module ViewComponent
|
|
107
114
|
ActionController::Base.include ViewComponent::RenderingComponentHelper
|
108
115
|
ActionController::Base.include ViewComponent::RenderComponentToStringHelper
|
109
116
|
end
|
117
|
+
# :nocov:
|
110
118
|
end
|
111
119
|
|
112
120
|
initializer "static assets" do |app|
|
113
|
-
if app.config
|
121
|
+
if serve_static_preview_assets?(app.config)
|
114
122
|
app.middleware.use(::ActionDispatch::Static, "#{root}/app/assets/vendor")
|
115
123
|
end
|
116
124
|
end
|
117
125
|
|
126
|
+
def serve_static_preview_assets?(app_config)
|
127
|
+
app_config.view_component.show_previews && app_config.public_file_server.enabled
|
128
|
+
end
|
129
|
+
|
118
130
|
initializer "compiler mode" do |_app|
|
119
131
|
ViewComponent::Compiler.mode = if Rails.env.development? || Rails.env.test?
|
120
132
|
ViewComponent::Compiler::DEVELOPMENT_MODE
|
@@ -152,6 +164,16 @@ module ViewComponent
|
|
152
164
|
end
|
153
165
|
end
|
154
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
|
+
|
155
177
|
app.executor.to_run :before do
|
156
178
|
CompileCache.invalidate! unless ActionView::Base.cache_template_loading
|
157
179
|
end
|
@@ -104,7 +104,10 @@ module ViewComponent
|
|
104
104
|
"string, or callable (that is proc, lambda, etc)"
|
105
105
|
end
|
106
106
|
|
107
|
-
class
|
107
|
+
class InvalidSlotNameError < StandardError
|
108
|
+
end
|
109
|
+
|
110
|
+
class SlotPredicateNameError < InvalidSlotNameError
|
108
111
|
MESSAGE =
|
109
112
|
"COMPONENT declares a slot named SLOT_NAME, which ends with a question mark.\n\n" \
|
110
113
|
"This isn't allowed because the ViewComponent framework already provides predicate " \
|
@@ -126,7 +129,7 @@ module ViewComponent
|
|
126
129
|
end
|
127
130
|
end
|
128
131
|
|
129
|
-
class ReservedSingularSlotNameError <
|
132
|
+
class ReservedSingularSlotNameError < InvalidSlotNameError
|
130
133
|
MESSAGE =
|
131
134
|
"COMPONENT declares a slot named SLOT_NAME, which is a reserved word in the ViewComponent framework.\n\n" \
|
132
135
|
"To fix this issue, choose a different name."
|
@@ -136,7 +139,7 @@ module ViewComponent
|
|
136
139
|
end
|
137
140
|
end
|
138
141
|
|
139
|
-
class ReservedPluralSlotNameError <
|
142
|
+
class ReservedPluralSlotNameError < InvalidSlotNameError
|
140
143
|
MESSAGE =
|
141
144
|
"COMPONENT declares a slot named SLOT_NAME, which is a reserved word in the ViewComponent framework.\n\n" \
|
142
145
|
"To fix this issue, choose a different name."
|
@@ -146,6 +149,16 @@ module ViewComponent
|
|
146
149
|
end
|
147
150
|
end
|
148
151
|
|
152
|
+
class UncountableSlotNameError < InvalidSlotNameError
|
153
|
+
MESSAGE =
|
154
|
+
"COMPONENT declares a slot named SLOT_NAME, which is an uncountable word\n\n" \
|
155
|
+
"To fix this issue, choose a different name."
|
156
|
+
|
157
|
+
def initialize(klass_name, slot_name)
|
158
|
+
super(MESSAGE.gsub("COMPONENT", klass_name.to_s).gsub("SLOT_NAME", slot_name.to_s))
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
149
162
|
class ContentAlreadySetForPolymorphicSlotError < StandardError
|
150
163
|
MESSAGE = "Content for slot SLOT_NAME has already been provided."
|
151
164
|
|
@@ -187,6 +200,7 @@ module ViewComponent
|
|
187
200
|
"`#controller` to a [`#before_render` method](https://viewcomponent.org/api.html#before_render--void)."
|
188
201
|
end
|
189
202
|
|
203
|
+
# :nocov:
|
190
204
|
class NoMatchingTemplatesForPreviewError < StandardError
|
191
205
|
MESSAGE = "Found 0 matches for templates for TEMPLATE_IDENTIFIER."
|
192
206
|
|
@@ -202,6 +216,7 @@ module ViewComponent
|
|
202
216
|
super(MESSAGE.gsub("TEMPLATE_IDENTIFIER", template_identifier))
|
203
217
|
end
|
204
218
|
end
|
219
|
+
# :nocov:
|
205
220
|
|
206
221
|
class SystemTestControllerOnlyAllowedInTestError < BaseError
|
207
222
|
MESSAGE = "ViewComponent SystemTest controller must only be called in a test environment for security reasons."
|
@@ -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
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/concern"
|
4
|
+
require "active_support/inflector/inflections"
|
4
5
|
require "view_component/slot"
|
5
6
|
|
6
7
|
module ViewComponent
|
@@ -92,11 +93,11 @@ module ViewComponent
|
|
92
93
|
get_slot(slot_name)
|
93
94
|
end
|
94
95
|
|
95
|
-
define_method "#{slot_name}?" do
|
96
|
+
define_method :"#{slot_name}?" do
|
96
97
|
get_slot(slot_name).present?
|
97
98
|
end
|
98
99
|
|
99
|
-
define_method "with_#{slot_name}_content" do |content|
|
100
|
+
define_method :"with_#{slot_name}_content" do |content|
|
100
101
|
send(setter_method_name) { content.to_s }
|
101
102
|
|
102
103
|
self
|
@@ -159,7 +160,7 @@ module ViewComponent
|
|
159
160
|
end
|
160
161
|
ruby2_keywords(setter_method_name) if respond_to?(:ruby2_keywords, true)
|
161
162
|
|
162
|
-
define_method "with_#{singular_name}_content" do |content|
|
163
|
+
define_method :"with_#{singular_name}_content" do |content|
|
163
164
|
send(setter_method_name) { content.to_s }
|
164
165
|
|
165
166
|
self
|
@@ -179,7 +180,7 @@ module ViewComponent
|
|
179
180
|
get_slot(slot_name)
|
180
181
|
end
|
181
182
|
|
182
|
-
define_method "#{slot_name}?" do
|
183
|
+
define_method :"#{slot_name}?" do
|
183
184
|
get_slot(slot_name).present?
|
184
185
|
end
|
185
186
|
|
@@ -210,7 +211,7 @@ module ViewComponent
|
|
210
211
|
get_slot(slot_name)
|
211
212
|
end
|
212
213
|
|
213
|
-
define_method("#{slot_name}?") do
|
214
|
+
define_method(:"#{slot_name}?") do
|
214
215
|
get_slot(slot_name).present?
|
215
216
|
end
|
216
217
|
|
@@ -245,7 +246,7 @@ module ViewComponent
|
|
245
246
|
end
|
246
247
|
ruby2_keywords(setter_method_name) if respond_to?(:ruby2_keywords, true)
|
247
248
|
|
248
|
-
define_method "with_#{poly_slot_name}_content" do |content|
|
249
|
+
define_method :"with_#{poly_slot_name}_content" do |content|
|
249
250
|
send(setter_method_name) { content.to_s }
|
250
251
|
|
251
252
|
self
|
@@ -295,6 +296,8 @@ module ViewComponent
|
|
295
296
|
raise ReservedPluralSlotNameError.new(name, slot_name)
|
296
297
|
end
|
297
298
|
|
299
|
+
raise_if_slot_name_uncountable(slot_name)
|
300
|
+
raise_if_slot_conflicts_with_call(slot_name)
|
298
301
|
raise_if_slot_ends_with_question_mark(slot_name)
|
299
302
|
raise_if_slot_registered(slot_name)
|
300
303
|
end
|
@@ -308,6 +311,7 @@ module ViewComponent
|
|
308
311
|
raise ReservedSingularSlotNameError.new(name, slot_name)
|
309
312
|
end
|
310
313
|
|
314
|
+
raise_if_slot_conflicts_with_call(slot_name)
|
311
315
|
raise_if_slot_ends_with_question_mark(slot_name)
|
312
316
|
raise_if_slot_registered(slot_name)
|
313
317
|
end
|
@@ -320,7 +324,20 @@ module ViewComponent
|
|
320
324
|
end
|
321
325
|
|
322
326
|
def raise_if_slot_ends_with_question_mark(slot_name)
|
323
|
-
raise SlotPredicateNameError.new(name, slot_name) if slot_name.to_s.
|
327
|
+
raise SlotPredicateNameError.new(name, slot_name) if slot_name.to_s.end_with?("?")
|
328
|
+
end
|
329
|
+
|
330
|
+
def raise_if_slot_conflicts_with_call(slot_name)
|
331
|
+
if slot_name.start_with?("call_")
|
332
|
+
raise InvalidSlotNameError, "Slot cannot start with 'call_'. Please rename #{slot_name}"
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
def raise_if_slot_name_uncountable(slot_name)
|
337
|
+
slot_name = slot_name.to_s
|
338
|
+
if slot_name.pluralize == slot_name.singularize
|
339
|
+
raise UncountableSlotNameError.new(name, slot_name)
|
340
|
+
end
|
324
341
|
end
|
325
342
|
end
|
326
343
|
|
@@ -48,10 +48,14 @@ module ViewComponent
|
|
48
48
|
@rendered_content =
|
49
49
|
if Rails.version.to_f >= 6.1
|
50
50
|
vc_test_controller.view_context.render(component, args, &block)
|
51
|
+
|
52
|
+
# :nocov:
|
51
53
|
else
|
52
54
|
vc_test_controller.view_context.render_component(component, &block)
|
53
55
|
end
|
54
56
|
|
57
|
+
# :nocov:
|
58
|
+
|
55
59
|
Nokogiri::HTML.fragment(@rendered_content)
|
56
60
|
end
|
57
61
|
|
@@ -163,30 +167,47 @@ module ViewComponent
|
|
163
167
|
# end
|
164
168
|
# ```
|
165
169
|
#
|
166
|
-
#
|
170
|
+
# To specify a request method, pass the method param:
|
171
|
+
#
|
172
|
+
# ```ruby
|
173
|
+
# with_request_url("/users/42", method: "POST") do
|
174
|
+
# render_inline(MyComponent.new)
|
175
|
+
# end
|
176
|
+
# ```
|
177
|
+
#
|
178
|
+
# @param full_path [String] The path to set for the current request.
|
167
179
|
# @param host [String] The host to set for the current request.
|
168
|
-
|
180
|
+
# @param method [String] The request method to set for the current request.
|
181
|
+
def with_request_url(full_path, host: nil, method: nil, format: :html)
|
169
182
|
old_request_host = vc_test_request.host
|
183
|
+
old_request_method = vc_test_request.request_method
|
170
184
|
old_request_path_info = vc_test_request.path_info
|
171
185
|
old_request_path_parameters = vc_test_request.path_parameters
|
172
186
|
old_request_query_parameters = vc_test_request.query_parameters
|
173
187
|
old_request_query_string = vc_test_request.query_string
|
188
|
+
old_request_format = vc_test_request.format.symbol
|
174
189
|
old_controller = defined?(@vc_test_controller) && @vc_test_controller
|
175
190
|
|
176
|
-
path, query =
|
191
|
+
path, query = full_path.split("?", 2)
|
192
|
+
vc_test_request.instance_variable_set(:@fullpath, full_path)
|
193
|
+
vc_test_request.instance_variable_set(:@original_fullpath, full_path)
|
177
194
|
vc_test_request.host = host if host
|
195
|
+
vc_test_request.request_method = method if method
|
178
196
|
vc_test_request.path_info = path
|
179
197
|
vc_test_request.path_parameters = Rails.application.routes.recognize_path_with_request(vc_test_request, path, {})
|
180
198
|
vc_test_request.set_header("action_dispatch.request.query_parameters",
|
181
199
|
Rack::Utils.parse_nested_query(query).with_indifferent_access)
|
182
200
|
vc_test_request.set_header(Rack::QUERY_STRING, query)
|
201
|
+
vc_test_request.format = format
|
183
202
|
yield
|
184
203
|
ensure
|
185
204
|
vc_test_request.host = old_request_host
|
205
|
+
vc_test_request.request_method = old_request_method
|
186
206
|
vc_test_request.path_info = old_request_path_info
|
187
207
|
vc_test_request.path_parameters = old_request_path_parameters
|
188
208
|
vc_test_request.set_header("action_dispatch.request.query_parameters", old_request_query_parameters)
|
189
209
|
vc_test_request.set_header(Rack::QUERY_STRING, old_request_query_string)
|
210
|
+
vc_test_request.format = old_request_format
|
190
211
|
@vc_test_controller = old_controller
|
191
212
|
end
|
192
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
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ViewComponent::UseHelpers
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
class_methods do
|
7
|
+
def use_helpers(*args)
|
8
|
+
args.each do |helper_method|
|
9
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
10
|
+
def #{helper_method}(*args, &block)
|
11
|
+
raise HelpersCalledBeforeRenderError if view_context.nil?
|
12
|
+
__vc_original_view_context.#{helper_method}(*args, &block)
|
13
|
+
end
|
14
|
+
RUBY
|
15
|
+
|
16
|
+
ruby2_keywords(helper_method) if respond_to?(:ruby2_keywords, true)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
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
|
@@ -395,6 +395,7 @@ files:
|
|
395
395
|
- lib/view_component/test_case.rb
|
396
396
|
- lib/view_component/test_helpers.rb
|
397
397
|
- lib/view_component/translatable.rb
|
398
|
+
- lib/view_component/use_helpers.rb
|
398
399
|
- lib/view_component/version.rb
|
399
400
|
- lib/view_component/with_content_helper.rb
|
400
401
|
- lib/yard/mattr_accessor_handler.rb
|
@@ -420,7 +421,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
420
421
|
- !ruby/object:Gem::Version
|
421
422
|
version: '0'
|
422
423
|
requirements: []
|
423
|
-
rubygems_version: 3.
|
424
|
+
rubygems_version: 3.5.3
|
424
425
|
signing_key:
|
425
426
|
specification_version: 4
|
426
427
|
summary: A framework for building reusable, testable & encapsulated view components
|