view_component 2.49.0 → 2.51.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.
Potentially problematic release.
This version of view_component might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/app/helpers/preview_helper.rb +2 -2
- data/docs/CHANGELOG.md +82 -0
- data/lib/rails/generators/abstract_generator.rb +1 -1
- data/lib/rails/generators/component/component_generator.rb +4 -2
- data/lib/rails/generators/locale/component_generator.rb +1 -1
- data/lib/rails/generators/preview/component_generator.rb +7 -0
- data/lib/rails/generators/preview/templates/component_preview.rb.tt +3 -1
- data/lib/rails/generators/rspec/templates/component_spec.rb.tt +2 -0
- data/lib/rails/generators/test_unit/templates/component_test.rb.tt +2 -0
- data/lib/view_component/base.rb +66 -37
- data/lib/view_component/compile_cache.rb +2 -1
- data/lib/view_component/compiler.rb +13 -10
- data/lib/view_component/content_areas.rb +1 -1
- data/lib/view_component/deprecation.rb +8 -0
- data/lib/view_component/docs_builder_component.html.erb +18 -0
- data/lib/view_component/docs_builder_component.rb +77 -0
- data/lib/view_component/engine.rb +4 -2
- data/lib/view_component/polymorphic_slots.rb +8 -5
- data/lib/view_component/preview.rb +2 -2
- data/lib/view_component/slotable.rb +1 -1
- data/lib/view_component/slotable_v2.rb +30 -4
- data/lib/view_component/translatable.rb +31 -1
- data/lib/view_component/version.rb +1 -1
- data/lib/view_component.rb +3 -2
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 163414625d8e2df6f2afddc821a6c849aef71cc8cd1ba458a533d90840e545cc
|
4
|
+
data.tar.gz: 3518269c96d4e62d3bcedae22871c7b260d2abf42e0c1e32703115185e69eeea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 531785b833a4d1a83b6929095b6a6a34076cb6576820884487a247d70c37bde2fedc47914633674c59a888dc7270526660e121f4980f2bcb85625ed6009607c4
|
7
|
+
data.tar.gz: e4c8b2694c7f70c529d56dcced18f66d511e909e1f36abc604a58402d1332caecde59d40c065668454af8abf6d53578469539ac1f4366650ad286856b50776fa
|
@@ -27,8 +27,8 @@ module PreviewHelper
|
|
27
27
|
end.flatten
|
28
28
|
|
29
29
|
# Search for templates the contain `html`.
|
30
|
-
matching_templates = all_template_paths.find_all do |
|
31
|
-
|
30
|
+
matching_templates = all_template_paths.find_all do |path|
|
31
|
+
path =~ /#{template_identifier}*.(html)/
|
32
32
|
end
|
33
33
|
|
34
34
|
# In-case of a conflict due to multiple template files with
|
data/docs/CHANGELOG.md
CHANGED
@@ -7,6 +7,82 @@ title: Changelog
|
|
7
7
|
|
8
8
|
## main
|
9
9
|
|
10
|
+
## 2.51.0
|
11
|
+
|
12
|
+
* Update the docs only when releasing a new version.
|
13
|
+
|
14
|
+
*Hans Lemuet*
|
15
|
+
|
16
|
+
* Alphabetize companies using ViewComponent and add Brightline to the list.
|
17
|
+
|
18
|
+
*Jack Schuss*
|
19
|
+
|
20
|
+
* Add CMYK value for ViewComponent Red color on logo page.
|
21
|
+
|
22
|
+
*Dylan Smith*
|
23
|
+
|
24
|
+
* Improve performance by moving template compilation from `#render_in` to `#render_template_for`.
|
25
|
+
|
26
|
+
*Cameron Dutro*
|
27
|
+
|
28
|
+
## 2.50.0
|
29
|
+
|
30
|
+
* Add tests for `layout` usage when rendering via controller.
|
31
|
+
|
32
|
+
*Felipe Sateler*
|
33
|
+
|
34
|
+
* Support returning Arrays from i18n files, and support marking them as HTML-safe translations.
|
35
|
+
|
36
|
+
*foca*
|
37
|
+
|
38
|
+
* Add Cometeer and Framework to users list.
|
39
|
+
|
40
|
+
*Elia Schito*
|
41
|
+
|
42
|
+
* Update Microsoft Vale styles.
|
43
|
+
|
44
|
+
*Simon Fish*
|
45
|
+
|
46
|
+
* Fix example in testing guide for how to setup default Rails tests.
|
47
|
+
|
48
|
+
*Steven Hansen*
|
49
|
+
|
50
|
+
* Update benchmark script to render multiple components/partials instead of a single instance per-run.
|
51
|
+
|
52
|
+
*Blake Williams*
|
53
|
+
|
54
|
+
* Add predicate methods `#{slot_name}?` to slots.
|
55
|
+
|
56
|
+
*Hans Lemuet*
|
57
|
+
|
58
|
+
* Use a dedicated deprecation instance, silence it while testing.
|
59
|
+
|
60
|
+
*Max Beizer, Hans Lemuet, Elia Schito*
|
61
|
+
|
62
|
+
* Fix Ruby warnings.
|
63
|
+
|
64
|
+
*Hans Lemuet*
|
65
|
+
|
66
|
+
* Place all generator options under `config.generate` namespace.
|
67
|
+
|
68
|
+
*Simon Fish*
|
69
|
+
|
70
|
+
* Allow preview generator to use provided component attributes.
|
71
|
+
* Add config option `config.view_component.generate.preview` to enable project-wide preview generation.
|
72
|
+
* Ensure all generated `.rb` files include `# frozen_string_literal: true` statement.
|
73
|
+
|
74
|
+
*Bob Maerten*
|
75
|
+
|
76
|
+
* Add Shogun to users list.
|
77
|
+
|
78
|
+
*Bernie Chiu*
|
79
|
+
|
80
|
+
## 2.49.1
|
81
|
+
|
82
|
+
* Patch XSS vulnerability in `ViewComponent::Translatable` module caused by improperly escaped interpolation arguments.
|
83
|
+
|
84
|
+
*Cameron Dutro*
|
85
|
+
|
10
86
|
## 2.49.0
|
11
87
|
|
12
88
|
* Fix path handling for evaluated view components that broke in Ruby 3.1.
|
@@ -613,6 +689,12 @@ title: Changelog
|
|
613
689
|
|
614
690
|
*Joel Hawksley*
|
615
691
|
|
692
|
+
## 2.31.2
|
693
|
+
|
694
|
+
* Patch XSS vulnerability in `ViewComponent::Translatable` module caused by improperly escaped interpolation arguments.
|
695
|
+
|
696
|
+
*Cameron Dutro*
|
697
|
+
|
616
698
|
## 2.31.1
|
617
699
|
|
618
700
|
* Fix `DEPRECATION WARNING: before_render_check` when compiling `ViewComponent::Base`
|
@@ -11,11 +11,13 @@ module Rails
|
|
11
11
|
|
12
12
|
argument :attributes, type: :array, default: [], banner: "attribute"
|
13
13
|
check_class_collision suffix: "Component"
|
14
|
+
|
14
15
|
class_option :inline, type: :boolean, default: false
|
16
|
+
class_option :locale, type: :boolean, default: ViewComponent::Base.generate.locale
|
15
17
|
class_option :parent, type: :string, desc: "The parent class for the generated component"
|
16
|
-
class_option :
|
18
|
+
class_option :preview, type: :boolean, default: ViewComponent::Base.generate.preview
|
17
19
|
class_option :sidecar, type: :boolean, default: false
|
18
|
-
class_option :
|
20
|
+
class_option :stimulus, type: :boolean, default: ViewComponent::Base.generate.stimulus_controller
|
19
21
|
|
20
22
|
def create_component_file
|
21
23
|
template "component.rb", File.join(component_path, class_path, "#{file_name}_component.rb")
|
@@ -12,7 +12,7 @@ module Locale
|
|
12
12
|
class_option :sidecar, type: :boolean, default: false
|
13
13
|
|
14
14
|
def create_locale_file
|
15
|
-
if ViewComponent::Base.
|
15
|
+
if ViewComponent::Base.generate.distinct_locale_files
|
16
16
|
I18n.available_locales.each do |locale|
|
17
17
|
create_file destination(locale), translations_hash([locale]).to_yaml
|
18
18
|
end
|
@@ -5,6 +5,7 @@ module Preview
|
|
5
5
|
class ComponentGenerator < ::Rails::Generators::NamedBase
|
6
6
|
source_root File.expand_path("templates", __dir__)
|
7
7
|
|
8
|
+
argument :attributes, type: :array, default: [], banner: "attribute"
|
8
9
|
check_class_collision suffix: "ComponentPreview"
|
9
10
|
|
10
11
|
def create_preview_file
|
@@ -20,6 +21,12 @@ module Preview
|
|
20
21
|
def file_name
|
21
22
|
@_file_name ||= super.sub(/_component\z/i, "")
|
22
23
|
end
|
24
|
+
|
25
|
+
def render_signature
|
26
|
+
return if attributes.blank?
|
27
|
+
|
28
|
+
attributes.map { |attr| %(#{attr.name}: "#{attr.name}") }.join(", ")
|
29
|
+
end
|
23
30
|
end
|
24
31
|
end
|
25
32
|
end
|
data/lib/view_component/base.rb
CHANGED
@@ -40,6 +40,23 @@ module ViewComponent
|
|
40
40
|
# noop
|
41
41
|
end
|
42
42
|
|
43
|
+
# @!macro [attach] deprecated_generate_mattr_accessor
|
44
|
+
# @method generate_$1
|
45
|
+
# @deprecated Use `#generate.$1` instead. Will be removed in v3.0.0.
|
46
|
+
def self._deprecated_generate_mattr_accessor(name)
|
47
|
+
define_singleton_method("generate_#{name}".to_sym) do
|
48
|
+
generate.public_send(name)
|
49
|
+
end
|
50
|
+
define_singleton_method("generate_#{name}=".to_sym) do |value|
|
51
|
+
generate.public_send("#{name}=".to_sym, value)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
_deprecated_generate_mattr_accessor :distinct_locale_files
|
56
|
+
_deprecated_generate_mattr_accessor :locale
|
57
|
+
_deprecated_generate_mattr_accessor :sidecar
|
58
|
+
_deprecated_generate_mattr_accessor :stimulus_controller
|
59
|
+
|
43
60
|
# Entrypoint for rendering components.
|
44
61
|
#
|
45
62
|
# - `view_context`: ActionView context from calling view
|
@@ -49,8 +66,6 @@ module ViewComponent
|
|
49
66
|
#
|
50
67
|
# @return [String]
|
51
68
|
def render_in(view_context, &block)
|
52
|
-
self.class.compile(raise_errors: true)
|
53
|
-
|
54
69
|
@view_context = view_context
|
55
70
|
self.__vc_original_view_context ||= view_context
|
56
71
|
|
@@ -95,6 +110,16 @@ module ViewComponent
|
|
95
110
|
@current_template = old_current_template
|
96
111
|
end
|
97
112
|
|
113
|
+
# :nocov:
|
114
|
+
def render_template_for(variant = nil)
|
115
|
+
# Force compilation here so the compiler always redefines render_template_for.
|
116
|
+
# This is mostly a safeguard to prevent infinite recursion.
|
117
|
+
self.class.compile(raise_errors: true, force: true)
|
118
|
+
# .compile replaces this method; call the new one
|
119
|
+
render_template_for(variant)
|
120
|
+
end
|
121
|
+
# :nocov:
|
122
|
+
|
98
123
|
# EXPERIMENTAL: Optional content to be returned after the rendered template.
|
99
124
|
#
|
100
125
|
# @return [String]
|
@@ -218,14 +243,11 @@ module ViewComponent
|
|
218
243
|
# @param variant [Symbol] The variant to be used by the component.
|
219
244
|
# @return [self]
|
220
245
|
def with_variant(variant)
|
221
|
-
ActiveSupport::Deprecation.warn(
|
222
|
-
"`with_variant` is deprecated and will be removed in ViewComponent v3.0.0."
|
223
|
-
)
|
224
|
-
|
225
246
|
@__vc_variant = variant
|
226
247
|
|
227
248
|
self
|
228
249
|
end
|
250
|
+
deprecate :with_variant, deprecator: ViewComponent::Deprecation
|
229
251
|
|
230
252
|
# The current request. Use sparingly as doing so introduces coupling that
|
231
253
|
# inhibits encapsulation & reuse, often making testing difficult.
|
@@ -271,56 +293,63 @@ module ViewComponent
|
|
271
293
|
#
|
272
294
|
mattr_accessor :render_monkey_patch_enabled, instance_writer: false, default: true
|
273
295
|
|
274
|
-
#
|
296
|
+
# Path for component files
|
275
297
|
#
|
276
|
-
# config.view_component.
|
298
|
+
# config.view_component.view_component_path = "app/my_components"
|
277
299
|
#
|
278
|
-
# Defaults to `
|
300
|
+
# Defaults to `app/components`.
|
279
301
|
#
|
280
|
-
mattr_accessor :
|
302
|
+
mattr_accessor :view_component_path, instance_writer: false, default: "app/components"
|
281
303
|
|
282
|
-
#
|
304
|
+
# Parent class for generated components
|
283
305
|
#
|
284
|
-
# config.view_component.
|
306
|
+
# config.view_component.component_parent_class = "MyBaseComponent"
|
285
307
|
#
|
286
|
-
# Defaults to
|
308
|
+
# Defaults to nil. If this is falsy, generators will use
|
309
|
+
# "ApplicationComponent" if defined, "ViewComponent::Base" otherwise.
|
287
310
|
#
|
288
|
-
mattr_accessor :
|
311
|
+
mattr_accessor :component_parent_class, instance_writer: false
|
289
312
|
|
290
|
-
#
|
313
|
+
# Configuration for generators.
|
291
314
|
#
|
292
|
-
#
|
315
|
+
# All options under this namespace default to `false` unless otherwise
|
316
|
+
# stated.
|
293
317
|
#
|
294
|
-
#
|
318
|
+
# #### #sidecar
|
295
319
|
#
|
296
|
-
#
|
297
|
-
# Fallback on `[:en]` when no available_locales is defined.
|
320
|
+
# Always generate a component with a sidecar directory:
|
298
321
|
#
|
299
|
-
|
300
|
-
|
301
|
-
# Path for component files
|
322
|
+
# config.view_component.generate.sidecar = true
|
302
323
|
#
|
303
|
-
#
|
324
|
+
# #### #stimulus_controller
|
304
325
|
#
|
305
|
-
#
|
326
|
+
# Always generate a Stimulus controller alongside the component:
|
306
327
|
#
|
307
|
-
|
308
|
-
|
309
|
-
# Parent class for generated components
|
328
|
+
# config.view_component.generate.stimulus_controller = true
|
310
329
|
#
|
311
|
-
#
|
330
|
+
# #### #locale
|
312
331
|
#
|
313
|
-
#
|
332
|
+
# Always generate translations file alongside the component:
|
314
333
|
#
|
315
|
-
|
316
|
-
|
317
|
-
#
|
334
|
+
# config.view_component.generate.locale = true
|
335
|
+
#
|
336
|
+
# #### #distinct_locale_files
|
337
|
+
#
|
338
|
+
# Always generate as many translations files as available locales:
|
339
|
+
#
|
340
|
+
# config.view_component.generate.distinct_locale_files = true
|
341
|
+
#
|
342
|
+
# One file will be generated for each configured `I18n.available_locales`,
|
343
|
+
# falling back to `[:en]` when no `available_locales` is defined.
|
344
|
+
#
|
345
|
+
# #### #preview
|
318
346
|
#
|
319
|
-
#
|
347
|
+
# Always generate preview alongside the component:
|
320
348
|
#
|
321
|
-
#
|
349
|
+
# config.view_component.generate.preview = true
|
322
350
|
#
|
323
|
-
|
351
|
+
# Defaults to `false`.
|
352
|
+
mattr_accessor :generate, instance_writer: false, default: ActiveSupport::OrderedOptions.new(false)
|
324
353
|
|
325
354
|
class << self
|
326
355
|
# @private
|
@@ -424,8 +453,8 @@ module ViewComponent
|
|
424
453
|
# Do as much work as possible in this step, as doing so reduces the amount
|
425
454
|
# of work done each time a component is rendered.
|
426
455
|
# @private
|
427
|
-
def compile(raise_errors: false)
|
428
|
-
compiler.compile(raise_errors: raise_errors)
|
456
|
+
def compile(raise_errors: false, force: false)
|
457
|
+
compiler.compile(raise_errors: raise_errors, force: force)
|
429
458
|
end
|
430
459
|
|
431
460
|
# @private
|
@@ -27,12 +27,11 @@ module ViewComponent
|
|
27
27
|
self.class.mode == DEVELOPMENT_MODE
|
28
28
|
end
|
29
29
|
|
30
|
-
def compile(raise_errors: false)
|
31
|
-
return if compiled?
|
30
|
+
def compile(raise_errors: false, force: false)
|
31
|
+
return if compiled? && !force
|
32
|
+
return if component_class == ViewComponent::Base
|
32
33
|
|
33
34
|
with_lock do
|
34
|
-
CompileCache.invalidate_class!(component_class)
|
35
|
-
|
36
35
|
subclass_instance_methods = component_class.instance_methods(false)
|
37
36
|
|
38
37
|
if subclass_instance_methods.include?(:with_content) && raise_errors
|
@@ -49,7 +48,7 @@ module ViewComponent
|
|
49
48
|
end
|
50
49
|
|
51
50
|
if subclass_instance_methods.include?(:before_render_check)
|
52
|
-
|
51
|
+
ViewComponent::Deprecation.warn(
|
53
52
|
"`#before_render_check` will be removed in v3.0.0.\n\n" \
|
54
53
|
"To fix this issue, use `#before_render` instead."
|
55
54
|
)
|
@@ -65,8 +64,8 @@ module ViewComponent
|
|
65
64
|
# as Ruby warns when redefining a method.
|
66
65
|
method_name = call_method_name(template[:variant])
|
67
66
|
|
68
|
-
if component_class.instance_methods.include?(method_name.to_sym)
|
69
|
-
component_class.send(:
|
67
|
+
if component_class.instance_methods(false).include?(method_name.to_sym)
|
68
|
+
component_class.send(:remove_method, method_name.to_sym)
|
70
69
|
end
|
71
70
|
|
72
71
|
component_class.class_eval <<-RUBY, template[:path], -1
|
@@ -93,14 +92,18 @@ module ViewComponent
|
|
93
92
|
end
|
94
93
|
end
|
95
94
|
|
95
|
+
def reset_render_template_for
|
96
|
+
if component_class.instance_methods(false).include?(:render_template_for)
|
97
|
+
component_class.send(:remove_method, :render_template_for)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
96
101
|
private
|
97
102
|
|
98
103
|
attr_reader :component_class
|
99
104
|
|
100
105
|
def define_render_template_for
|
101
|
-
|
102
|
-
component_class.send(:undef_method, :render_template_for)
|
103
|
-
end
|
106
|
+
reset_render_template_for
|
104
107
|
|
105
108
|
variant_elsifs = variants.compact.uniq.map do |variant|
|
106
109
|
"elsif variant.to_sym == :#{variant}\n #{call_method_name(variant)}"
|
@@ -31,7 +31,7 @@ module ViewComponent
|
|
31
31
|
|
32
32
|
class_methods do
|
33
33
|
def with_content_areas(*areas)
|
34
|
-
|
34
|
+
ViewComponent::Deprecation.warn(
|
35
35
|
"`with_content_areas` is deprecated and will be removed in ViewComponent v3.0.0.\n\n" \
|
36
36
|
"Use slots (https://viewcomponent.org/guide/slots.html) instead."
|
37
37
|
)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
---
|
2
|
+
layout: default
|
3
|
+
title: API
|
4
|
+
nav_order: 3
|
5
|
+
---
|
6
|
+
|
7
|
+
<!-- Warning: AUTO-GENERATED file, don't edit. Add code comments to your Ruby instead <3 -->
|
8
|
+
|
9
|
+
# API
|
10
|
+
|
11
|
+
<% @sections.each do |section| %>
|
12
|
+
## <%= section.heading %>
|
13
|
+
|
14
|
+
<% section.methods.each do |method| %>
|
15
|
+
### <%== render ViewComponent::DocsBuilderComponent::MethodDoc.new(method) %>
|
16
|
+
|
17
|
+
<% end %>
|
18
|
+
<% end %>
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ViewComponent
|
4
|
+
class DocsBuilderComponent < Base
|
5
|
+
class Section < Struct.new(:heading, :methods, :show_types, keyword_init: true)
|
6
|
+
def initialize(heading: nil, methods: [], show_types: true)
|
7
|
+
methods.sort_by! { |method| method[:name] }
|
8
|
+
super
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class MethodDoc < ViewComponent::Base
|
13
|
+
def initialize(method, section: Section.new(show_types: true))
|
14
|
+
@method = method
|
15
|
+
@section = section
|
16
|
+
end
|
17
|
+
|
18
|
+
def show_types?
|
19
|
+
@section.show_types
|
20
|
+
end
|
21
|
+
|
22
|
+
def deprecated?
|
23
|
+
@method.tag(:deprecated).present?
|
24
|
+
end
|
25
|
+
|
26
|
+
def suffix
|
27
|
+
" (Deprecated)" if deprecated?
|
28
|
+
end
|
29
|
+
|
30
|
+
def types
|
31
|
+
" → [#{@method.tag(:return).types.join(',')}]" if @method.tag(:return)&.types && show_types?
|
32
|
+
end
|
33
|
+
|
34
|
+
def signature_or_name
|
35
|
+
@method.signature ? @method.signature.gsub("def ", "") : @method.name
|
36
|
+
end
|
37
|
+
|
38
|
+
def separator
|
39
|
+
@method.sep
|
40
|
+
end
|
41
|
+
|
42
|
+
def docstring
|
43
|
+
@method.docstring
|
44
|
+
end
|
45
|
+
|
46
|
+
def deprecation_text
|
47
|
+
@method.tag(:deprecated)&.text
|
48
|
+
end
|
49
|
+
|
50
|
+
def docstring_and_deprecation_text
|
51
|
+
<<~DOCS.strip
|
52
|
+
#{docstring}
|
53
|
+
|
54
|
+
#{"_#{deprecation_text}_" if deprecated?}
|
55
|
+
DOCS
|
56
|
+
end
|
57
|
+
|
58
|
+
def call
|
59
|
+
<<~DOCS.chomp
|
60
|
+
#{separator}#{signature_or_name}#{types}#{suffix}
|
61
|
+
|
62
|
+
#{docstring_and_deprecation_text}
|
63
|
+
DOCS
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# { heading: String, public_only: Boolean, show_types: Boolean}
|
68
|
+
def initialize(sections: [])
|
69
|
+
@sections = sections
|
70
|
+
end
|
71
|
+
|
72
|
+
# deprecation
|
73
|
+
# return
|
74
|
+
# only public methods
|
75
|
+
# sig with types or name
|
76
|
+
end
|
77
|
+
end
|
@@ -27,7 +27,7 @@ module ViewComponent
|
|
27
27
|
)
|
28
28
|
|
29
29
|
if options.preview_path.present?
|
30
|
-
|
30
|
+
ViewComponent::Deprecation.warn(
|
31
31
|
"`preview_path` will be removed in v3.0.0. Use `preview_paths` instead."
|
32
32
|
)
|
33
33
|
options.preview_paths << options.preview_path
|
@@ -155,7 +155,9 @@ end
|
|
155
155
|
|
156
156
|
# :nocov:
|
157
157
|
unless defined?(ViewComponent::Base)
|
158
|
-
|
158
|
+
require "view_component/deprecation"
|
159
|
+
|
160
|
+
ViewComponent::Deprecation.warn(
|
159
161
|
"This manually engine loading is deprecated and will be removed in v3.0.0. " \
|
160
162
|
"Remove `require \"view_component/engine\"`."
|
161
163
|
)
|
@@ -25,12 +25,19 @@ module ViewComponent
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def register_polymorphic_slot(slot_name, types, collection:)
|
28
|
+
unless types.empty?
|
29
|
+
getter_name = slot_name
|
30
|
+
|
31
|
+
define_method(getter_name) do
|
32
|
+
get_slot(slot_name)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
28
36
|
renderable_hash = types.each_with_object({}) do |(poly_type, poly_callable), memo|
|
29
37
|
memo[poly_type] = define_slot(
|
30
38
|
"#{slot_name}_#{poly_type}", collection: collection, callable: poly_callable
|
31
39
|
)
|
32
40
|
|
33
|
-
getter_name = slot_name
|
34
41
|
setter_name =
|
35
42
|
if collection
|
36
43
|
"#{ActiveSupport::Inflector.singularize(slot_name)}_#{poly_type}"
|
@@ -38,10 +45,6 @@ module ViewComponent
|
|
38
45
|
"#{slot_name}_#{poly_type}"
|
39
46
|
end
|
40
47
|
|
41
|
-
define_method(getter_name) do
|
42
|
-
get_slot(slot_name)
|
43
|
-
end
|
44
|
-
|
45
48
|
define_method(setter_name) do |*args, &block|
|
46
49
|
set_polymorphic_slot(slot_name, poly_type, *args, &block)
|
47
50
|
end
|
@@ -73,8 +73,8 @@ module ViewComponent # :nodoc:
|
|
73
73
|
# Returns the relative path (from preview_path) to the preview example template if the template exists
|
74
74
|
def preview_example_template_path(example)
|
75
75
|
preview_path =
|
76
|
-
Array(preview_paths).detect do |
|
77
|
-
Dir["#{
|
76
|
+
Array(preview_paths).detect do |path|
|
77
|
+
Dir["#{path}/#{preview_name}_preview/#{example}.html.*"].first
|
78
78
|
end
|
79
79
|
|
80
80
|
if preview_path.nil?
|
@@ -23,7 +23,7 @@ module ViewComponent
|
|
23
23
|
# class_name: "Header" # class name string, used to instantiate Slot
|
24
24
|
# )
|
25
25
|
def with_slot(*slot_names, collection: false, class_name: nil)
|
26
|
-
|
26
|
+
ViewComponent::Deprecation.warn(
|
27
27
|
"`with_slot` is deprecated and will be removed in ViewComponent v3.0.0.\n" \
|
28
28
|
"Use the new slots API (https://viewcomponent.org/guide/slots.html) instead."
|
29
29
|
)
|
@@ -7,6 +7,11 @@ module ViewComponent
|
|
7
7
|
module SlotableV2
|
8
8
|
extend ActiveSupport::Concern
|
9
9
|
|
10
|
+
RESERVED_NAMES = {
|
11
|
+
singular: %i[content render].freeze,
|
12
|
+
plural: %i[contents renders].freeze,
|
13
|
+
}.freeze
|
14
|
+
|
10
15
|
# Setup component slot state
|
11
16
|
included do
|
12
17
|
# Hash of registered Slots
|
@@ -75,6 +80,10 @@ module ViewComponent
|
|
75
80
|
end
|
76
81
|
ruby2_keywords(slot_name.to_sym) if respond_to?(:ruby2_keywords, true)
|
77
82
|
|
83
|
+
define_method "#{slot_name}?" do
|
84
|
+
get_slot(slot_name).present?
|
85
|
+
end
|
86
|
+
|
78
87
|
register_slot(slot_name, collection: false, callable: callable)
|
79
88
|
end
|
80
89
|
|
@@ -140,6 +149,10 @@ module ViewComponent
|
|
140
149
|
end
|
141
150
|
end
|
142
151
|
|
152
|
+
define_method "#{slot_name}?" do
|
153
|
+
get_slot(slot_name).present?
|
154
|
+
end
|
155
|
+
|
143
156
|
register_slot(slot_name, collection: true, callable: callable)
|
144
157
|
end
|
145
158
|
|
@@ -197,24 +210,26 @@ module ViewComponent
|
|
197
210
|
end
|
198
211
|
|
199
212
|
def validate_plural_slot_name(slot_name)
|
200
|
-
if slot_name.to_sym
|
213
|
+
if RESERVED_NAMES[:plural].include?(slot_name.to_sym)
|
201
214
|
raise ArgumentError.new(
|
202
215
|
"#{self} declares a slot named #{slot_name}, which is a reserved word in the ViewComponent framework.\n\n" \
|
203
216
|
"To fix this issue, choose a different name."
|
204
217
|
)
|
205
218
|
end
|
206
219
|
|
220
|
+
raise_if_slot_ends_with_question_mark(slot_name)
|
207
221
|
raise_if_slot_registered(slot_name)
|
208
222
|
end
|
209
223
|
|
210
224
|
def validate_singular_slot_name(slot_name)
|
211
|
-
if slot_name.to_sym
|
225
|
+
if RESERVED_NAMES[:singular].include?(slot_name.to_sym)
|
212
226
|
raise ArgumentError.new(
|
213
227
|
"#{self} declares a slot named #{slot_name}, which is a reserved word in the ViewComponent framework.\n\n" \
|
214
228
|
"To fix this issue, choose a different name."
|
215
229
|
)
|
216
230
|
end
|
217
231
|
|
232
|
+
raise_if_slot_ends_with_question_mark(slot_name)
|
218
233
|
raise_if_slot_registered(slot_name)
|
219
234
|
end
|
220
235
|
|
@@ -227,6 +242,17 @@ module ViewComponent
|
|
227
242
|
)
|
228
243
|
end
|
229
244
|
end
|
245
|
+
|
246
|
+
def raise_if_slot_ends_with_question_mark(slot_name)
|
247
|
+
if slot_name.to_s.ends_with?("?")
|
248
|
+
raise ArgumentError.new(
|
249
|
+
"#{self} declares a slot named #{slot_name}, which ends with a question mark.\n\n"\
|
250
|
+
"This is not allowed because the ViewComponent framework already provides predicate "\
|
251
|
+
"methods ending in `?`.\n\n" \
|
252
|
+
"To fix this issue, choose a different name."
|
253
|
+
)
|
254
|
+
end
|
255
|
+
end
|
230
256
|
end
|
231
257
|
|
232
258
|
def get_slot(slot_name)
|
@@ -276,8 +302,8 @@ module ViewComponent
|
|
276
302
|
renderable_function = slot_definition[:renderable_function].bind(self)
|
277
303
|
renderable_value =
|
278
304
|
if block_given?
|
279
|
-
renderable_function.call(*args) do |*
|
280
|
-
view_context.capture(*
|
305
|
+
renderable_function.call(*args) do |*rargs|
|
306
|
+
view_context.capture(*rargs, &block)
|
281
307
|
end
|
282
308
|
else
|
283
309
|
renderable_function.call(*args)
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "erb"
|
3
4
|
require "set"
|
4
5
|
require "i18n"
|
5
6
|
require "action_view/helpers/translation_helper"
|
@@ -70,6 +71,10 @@ module ViewComponent
|
|
70
71
|
key = key&.to_s unless key.is_a?(String)
|
71
72
|
key = "#{i18n_scope}#{key}" if key.start_with?(".")
|
72
73
|
|
74
|
+
if HTML_SAFE_TRANSLATION_KEY.match?(key)
|
75
|
+
html_escape_translation_options!(options)
|
76
|
+
end
|
77
|
+
|
73
78
|
if key.start_with?(i18n_scope + ".")
|
74
79
|
translated =
|
75
80
|
catch(:exception) do
|
@@ -82,7 +87,7 @@ module ViewComponent
|
|
82
87
|
end
|
83
88
|
|
84
89
|
if HTML_SAFE_TRANSLATION_KEY.match?(key)
|
85
|
-
translated = translated
|
90
|
+
translated = html_safe_translation(translated)
|
86
91
|
end
|
87
92
|
|
88
93
|
translated
|
@@ -96,5 +101,30 @@ module ViewComponent
|
|
96
101
|
def i18n_scope
|
97
102
|
self.class.i18n_scope
|
98
103
|
end
|
104
|
+
|
105
|
+
def html_safe_translation(translation)
|
106
|
+
if translation.respond_to?(:map)
|
107
|
+
translation.map { |element| html_safe_translation(element) }
|
108
|
+
else
|
109
|
+
# It's assumed here that objects loaded by the i18n backend will respond to `#html_safe?`.
|
110
|
+
# It's reasonable that if we're in Rails, `active_support/core_ext/string/output_safety.rb`
|
111
|
+
# will provide this to `Object`.
|
112
|
+
translation.html_safe # rubocop:disable Rails/OutputSafety
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
def html_escape_translation_options!(options)
|
119
|
+
options.each do |name, value|
|
120
|
+
unless i18n_option?(name) || (name == :count && value.is_a?(Numeric))
|
121
|
+
options[name] = ERB::Util.html_escape(value.to_s)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def i18n_option?(name)
|
127
|
+
(@i18n_option_names ||= I18n::RESERVED_KEYS.to_set).include?(name)
|
128
|
+
end
|
99
129
|
end
|
100
130
|
end
|
data/lib/view_component.rb
CHANGED
@@ -10,6 +10,7 @@ module ViewComponent
|
|
10
10
|
autoload :Compiler
|
11
11
|
autoload :CompileCache
|
12
12
|
autoload :ComponentError
|
13
|
+
autoload :Deprecation
|
13
14
|
autoload :Instrumentation
|
14
15
|
autoload :Preview
|
15
16
|
autoload :PreviewTemplateError
|
@@ -21,8 +22,8 @@ end
|
|
21
22
|
|
22
23
|
# :nocov:
|
23
24
|
if defined?(ViewComponent::Engine)
|
24
|
-
|
25
|
-
"
|
25
|
+
ViewComponent::Deprecation.warn(
|
26
|
+
"Manually loading the engine is deprecated and will be removed in v3.0.0. " \
|
26
27
|
"Remove `require \"view_component/engine\"`."
|
27
28
|
)
|
28
29
|
elsif defined?(Rails::Engine)
|
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: 2.
|
4
|
+
version: 2.51.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitHub Open Source
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-03-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -329,6 +329,9 @@ files:
|
|
329
329
|
- lib/view_component/compiler.rb
|
330
330
|
- lib/view_component/component_error.rb
|
331
331
|
- lib/view_component/content_areas.rb
|
332
|
+
- lib/view_component/deprecation.rb
|
333
|
+
- lib/view_component/docs_builder_component.html.erb
|
334
|
+
- lib/view_component/docs_builder_component.rb
|
332
335
|
- lib/view_component/engine.rb
|
333
336
|
- lib/view_component/instrumentation.rb
|
334
337
|
- lib/view_component/polymorphic_slots.rb
|
@@ -373,7 +376,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
373
376
|
- !ruby/object:Gem::Version
|
374
377
|
version: '0'
|
375
378
|
requirements: []
|
376
|
-
rubygems_version: 3.
|
379
|
+
rubygems_version: 3.2.32
|
377
380
|
signing_key:
|
378
381
|
specification_version: 4
|
379
382
|
summary: View components for Rails
|