view_component 2.82.0 → 3.0.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/controllers/view_components_system_test_controller.rb +24 -1
- data/app/helpers/preview_helper.rb +2 -4
- data/docs/CHANGELOG.md +278 -0
- data/lib/view_component/base.rb +41 -92
- data/lib/view_component/capture_compatibility.rb +44 -0
- data/lib/view_component/collection.rb +2 -5
- data/lib/view_component/compiler.rb +51 -28
- data/lib/view_component/config.rb +9 -13
- data/lib/view_component/deprecation.rb +1 -1
- data/lib/view_component/docs_builder_component.html.erb +5 -1
- data/lib/view_component/docs_builder_component.rb +28 -9
- data/lib/view_component/engine.rb +12 -22
- data/lib/view_component/errors.rb +213 -0
- data/lib/view_component/inline_template.rb +55 -0
- data/lib/view_component/preview.rb +1 -7
- data/lib/view_component/rails/tasks/view_component.rake +1 -1
- data/lib/view_component/slot.rb +107 -1
- data/lib/view_component/slotable.rb +356 -96
- data/lib/view_component/system_test_helpers.rb +5 -5
- data/lib/view_component/test_helpers.rb +67 -54
- data/lib/view_component/translatable.rb +35 -23
- data/lib/view_component/version.rb +4 -3
- data/lib/view_component/with_content_helper.rb +3 -8
- data/lib/view_component.rb +3 -12
- metadata +28 -17
- data/lib/view_component/content_areas.rb +0 -56
- data/lib/view_component/polymorphic_slots.rb +0 -103
- data/lib/view_component/preview_template_error.rb +0 -6
- data/lib/view_component/slot_v2.rb +0 -98
- data/lib/view_component/slotable_v2.rb +0 -391
- data/lib/view_component/template_error.rb +0 -9
@@ -28,17 +28,10 @@ module ViewComponent
|
|
28
28
|
# :nocov:
|
29
29
|
end
|
30
30
|
|
31
|
-
# @private
|
32
|
-
attr_reader :rendered_content
|
33
|
-
|
34
31
|
# Returns the result of a render_inline call.
|
35
32
|
#
|
36
|
-
# @return [
|
37
|
-
|
38
|
-
ViewComponent::Deprecation.deprecation_warning("`rendered_component`", :"`page`")
|
39
|
-
|
40
|
-
rendered_content
|
41
|
-
end
|
33
|
+
# @return [ActionView::OutputBuffer]
|
34
|
+
attr_reader :rendered_content
|
42
35
|
|
43
36
|
# Render a component inline. Internally sets `page` to be a `Capybara::Node::Simple`,
|
44
37
|
# allowing for Capybara assertions to be used:
|
@@ -54,9 +47,9 @@ module ViewComponent
|
|
54
47
|
@page = nil
|
55
48
|
@rendered_content =
|
56
49
|
if Rails.version.to_f >= 6.1
|
57
|
-
|
50
|
+
vc_test_controller.view_context.render(component, args, &block)
|
58
51
|
else
|
59
|
-
|
52
|
+
vc_test_controller.view_context.render_component(component, &block)
|
60
53
|
end
|
61
54
|
|
62
55
|
Nokogiri::HTML.fragment(@rendered_content)
|
@@ -81,8 +74,8 @@ module ViewComponent
|
|
81
74
|
# @param from [ViewComponent::Preview] The class of the preview to be rendered.
|
82
75
|
# @param params [Hash] Parameters to be passed to the preview.
|
83
76
|
# @return [Nokogiri::HTML]
|
84
|
-
def render_preview(name, from:
|
85
|
-
previews_controller =
|
77
|
+
def render_preview(name, from: __vc_test_helpers_preview_class, params: {})
|
78
|
+
previews_controller = __vc_test_helpers_build_controller(Rails.application.config.view_component.preview_controller.constantize)
|
86
79
|
|
87
80
|
# From what I can tell, it's not possible to overwrite all request parameters
|
88
81
|
# at once, so we set them individually here.
|
@@ -104,7 +97,7 @@ module ViewComponent
|
|
104
97
|
# Capybara assertions to be used. All arguments are forwarded to the block.
|
105
98
|
#
|
106
99
|
# ```ruby
|
107
|
-
# render_in_view_context(arg1, arg2:) do |arg1, arg2:|
|
100
|
+
# render_in_view_context(arg1, arg2: nil) do |arg1, arg2:|
|
108
101
|
# render(MyComponent.new(arg1, arg2))
|
109
102
|
# end
|
110
103
|
#
|
@@ -112,26 +105,11 @@ module ViewComponent
|
|
112
105
|
# ```
|
113
106
|
def render_in_view_context(*args, &block)
|
114
107
|
@page = nil
|
115
|
-
@rendered_content =
|
108
|
+
@rendered_content = vc_test_controller.view_context.instance_exec(*args, &block)
|
116
109
|
Nokogiri::HTML.fragment(@rendered_content)
|
117
110
|
end
|
118
111
|
ruby2_keywords(:render_in_view_context) if respond_to?(:ruby2_keywords, true)
|
119
112
|
|
120
|
-
# @private
|
121
|
-
def controller
|
122
|
-
@controller ||= build_controller(Base.test_controller.constantize)
|
123
|
-
end
|
124
|
-
|
125
|
-
# @private
|
126
|
-
def request
|
127
|
-
@request ||=
|
128
|
-
begin
|
129
|
-
request = ActionDispatch::TestRequest.create
|
130
|
-
request.session = ActionController::TestSession.new
|
131
|
-
request
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
113
|
# Set the Action Pack request variant for the given block:
|
136
114
|
#
|
137
115
|
# ```ruby
|
@@ -142,12 +120,12 @@ module ViewComponent
|
|
142
120
|
#
|
143
121
|
# @param variant [Symbol] The variant to be set for the provided block.
|
144
122
|
def with_variant(variant)
|
145
|
-
old_variants =
|
123
|
+
old_variants = vc_test_controller.view_context.lookup_context.variants
|
146
124
|
|
147
|
-
|
125
|
+
vc_test_controller.view_context.lookup_context.variants = variant
|
148
126
|
yield
|
149
127
|
ensure
|
150
|
-
|
128
|
+
vc_test_controller.view_context.lookup_context.variants = old_variants
|
151
129
|
end
|
152
130
|
|
153
131
|
# Set the controller to be used while executing the given block,
|
@@ -161,12 +139,12 @@ module ViewComponent
|
|
161
139
|
#
|
162
140
|
# @param klass [ActionController::Base] The controller to be used.
|
163
141
|
def with_controller_class(klass)
|
164
|
-
old_controller = defined?(@
|
142
|
+
old_controller = defined?(@vc_test_controller) && @vc_test_controller
|
165
143
|
|
166
|
-
@
|
144
|
+
@vc_test_controller = __vc_test_helpers_build_controller(klass)
|
167
145
|
yield
|
168
146
|
ensure
|
169
|
-
@
|
147
|
+
@vc_test_controller = old_controller
|
170
148
|
end
|
171
149
|
|
172
150
|
# Set the URL of the current request (such as when using request-dependent path helpers):
|
@@ -179,34 +157,69 @@ module ViewComponent
|
|
179
157
|
#
|
180
158
|
# @param path [String] The path to set for the current request.
|
181
159
|
def with_request_url(path)
|
182
|
-
old_request_path_info =
|
183
|
-
old_request_path_parameters =
|
184
|
-
old_request_query_parameters =
|
185
|
-
old_request_query_string =
|
186
|
-
old_controller = defined?(@
|
160
|
+
old_request_path_info = vc_test_request.path_info
|
161
|
+
old_request_path_parameters = vc_test_request.path_parameters
|
162
|
+
old_request_query_parameters = vc_test_request.query_parameters
|
163
|
+
old_request_query_string = vc_test_request.query_string
|
164
|
+
old_controller = defined?(@vc_test_controller) && @vc_test_controller
|
187
165
|
|
188
166
|
path, query = path.split("?", 2)
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
167
|
+
vc_test_request.path_info = path
|
168
|
+
vc_test_request.path_parameters = Rails.application.routes.recognize_path_with_request(vc_test_request, path, {})
|
169
|
+
vc_test_request.set_header("action_dispatch.request.query_parameters", Rack::Utils.parse_nested_query(query))
|
170
|
+
vc_test_request.set_header(Rack::QUERY_STRING, query)
|
193
171
|
yield
|
194
172
|
ensure
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
@
|
173
|
+
vc_test_request.path_info = old_request_path_info
|
174
|
+
vc_test_request.path_parameters = old_request_path_parameters
|
175
|
+
vc_test_request.set_header("action_dispatch.request.query_parameters", old_request_query_parameters)
|
176
|
+
vc_test_request.set_header(Rack::QUERY_STRING, old_request_query_string)
|
177
|
+
@vc_test_controller = old_controller
|
200
178
|
end
|
201
179
|
|
202
|
-
#
|
203
|
-
|
204
|
-
|
180
|
+
# Access the controller used by `render_inline`:
|
181
|
+
#
|
182
|
+
# ```ruby
|
183
|
+
# test "logged out user sees login link" do
|
184
|
+
# vc_test_controller.expects(:logged_in?).at_least_once.returns(false)
|
185
|
+
# render_inline(LoginComponent.new)
|
186
|
+
# assert_selector("[aria-label='You must be signed in']")
|
187
|
+
# end
|
188
|
+
# ```
|
189
|
+
#
|
190
|
+
# @return [ActionController::Base]
|
191
|
+
def vc_test_controller
|
192
|
+
@vc_test_controller ||= __vc_test_helpers_build_controller(Base.test_controller.constantize)
|
205
193
|
end
|
206
194
|
|
195
|
+
# Access the request used by `render_inline`:
|
196
|
+
#
|
197
|
+
# ```ruby
|
198
|
+
# test "component does not render in Firefox" do
|
199
|
+
# request.env["HTTP_USER_AGENT"] = "Mozilla/5.0"
|
200
|
+
# render_inline(NoFirefoxComponent.new)
|
201
|
+
# refute_component_rendered
|
202
|
+
# end
|
203
|
+
# ```
|
204
|
+
#
|
205
|
+
# @return [ActionDispatch::TestRequest]
|
206
|
+
def vc_test_request
|
207
|
+
@vc_test_request ||=
|
208
|
+
begin
|
209
|
+
out = ActionDispatch::TestRequest.create
|
210
|
+
out.session = ActionController::TestSession.new
|
211
|
+
out
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
# Note: We prefix private methods here to prevent collisions in consumer's tests.
|
207
216
|
private
|
208
217
|
|
209
|
-
def
|
218
|
+
def __vc_test_helpers_build_controller(klass)
|
219
|
+
klass.new.tap { |c| c.request = vc_test_request }.extend(Rails.application.routes.url_helpers)
|
220
|
+
end
|
221
|
+
|
222
|
+
def __vc_test_helpers_preview_class
|
210
223
|
result = if respond_to?(:described_class)
|
211
224
|
raise "`render_preview` expected a described_class, but it is nil." if described_class.nil?
|
212
225
|
|
@@ -21,7 +21,7 @@ module ViewComponent
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def build_i18n_backend
|
24
|
-
return if
|
24
|
+
return if compiled?
|
25
25
|
|
26
26
|
self.i18n_backend = if (translation_files = sidecar_files(%w[yml yaml])).any?
|
27
27
|
# Returning nil cleans up if translations file has been removed since the last compilation
|
@@ -32,6 +32,27 @@ module ViewComponent
|
|
32
32
|
)
|
33
33
|
end
|
34
34
|
end
|
35
|
+
|
36
|
+
def i18n_key(key, scope = nil)
|
37
|
+
scope = scope.join(".") if scope.is_a? Array
|
38
|
+
key = key&.to_s unless key.is_a?(String)
|
39
|
+
key = "#{scope}.#{key}" if scope
|
40
|
+
key = "#{i18n_scope}#{key}" if key.start_with?(".")
|
41
|
+
key
|
42
|
+
end
|
43
|
+
|
44
|
+
def translate(key = nil, **options)
|
45
|
+
return key.map { |k| translate(k, **options) } if key.is_a?(Array)
|
46
|
+
|
47
|
+
ensure_compiled
|
48
|
+
|
49
|
+
locale = options.delete(:locale) || ::I18n.locale
|
50
|
+
key = i18n_key(key, options.delete(:scope))
|
51
|
+
|
52
|
+
i18n_backend.translate(locale, key, options)
|
53
|
+
end
|
54
|
+
|
55
|
+
alias_method :t, :translate
|
35
56
|
end
|
36
57
|
|
37
58
|
class I18nBackend < ::I18n::Backend::Simple
|
@@ -60,19 +81,16 @@ module ViewComponent
|
|
60
81
|
end
|
61
82
|
|
62
83
|
def translate(key = nil, **options)
|
84
|
+
raise ViewComponent::TranslateCalledBeforeRenderError if view_context.nil?
|
85
|
+
|
63
86
|
return super unless i18n_backend
|
64
87
|
return key.map { |k| translate(k, **options) } if key.is_a?(Array)
|
65
88
|
|
66
89
|
locale = options.delete(:locale) || ::I18n.locale
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
key = "#{i18n_scope}#{key}" if key.start_with?(".")
|
72
|
-
|
73
|
-
if HTML_SAFE_TRANSLATION_KEY.match?(key)
|
74
|
-
html_escape_translation_options!(options)
|
75
|
-
end
|
90
|
+
key = self.class.i18n_key(key, options.delete(:scope))
|
91
|
+
as_html = HTML_SAFE_TRANSLATION_KEY.match?(key)
|
92
|
+
|
93
|
+
html_escape_translation_options!(options) if as_html
|
76
94
|
|
77
95
|
if key.start_with?(i18n_scope + ".")
|
78
96
|
translated =
|
@@ -85,10 +103,7 @@ module ViewComponent
|
|
85
103
|
return super(key, locale: locale, **options)
|
86
104
|
end
|
87
105
|
|
88
|
-
|
89
|
-
translated = html_safe_translation(translated)
|
90
|
-
end
|
91
|
-
|
106
|
+
translated = html_safe_translation(translated) if as_html
|
92
107
|
translated
|
93
108
|
else
|
94
109
|
super(key, locale: locale, **options)
|
@@ -101,6 +116,8 @@ module ViewComponent
|
|
101
116
|
self.class.i18n_scope
|
102
117
|
end
|
103
118
|
|
119
|
+
private
|
120
|
+
|
104
121
|
def html_safe_translation(translation)
|
105
122
|
if translation.respond_to?(:map)
|
106
123
|
translation.map { |element| html_safe_translation(element) }
|
@@ -112,18 +129,13 @@ module ViewComponent
|
|
112
129
|
end
|
113
130
|
end
|
114
131
|
|
115
|
-
private
|
116
|
-
|
117
132
|
def html_escape_translation_options!(options)
|
118
133
|
options.each do |name, value|
|
119
|
-
|
120
|
-
|
121
|
-
end
|
122
|
-
end
|
123
|
-
end
|
134
|
+
next if ::I18n.reserved_keys_pattern.match?(name)
|
135
|
+
next if name == :count && value.is_a?(Numeric)
|
124
136
|
|
125
|
-
|
126
|
-
|
137
|
+
options[name] = ERB::Util.html_escape(value.to_s)
|
138
|
+
end
|
127
139
|
end
|
128
140
|
end
|
129
141
|
end
|
@@ -3,14 +3,9 @@
|
|
3
3
|
module ViewComponent
|
4
4
|
module WithContentHelper
|
5
5
|
def with_content(value)
|
6
|
-
if value.nil?
|
7
|
-
|
8
|
-
|
9
|
-
"To fix this issue, pass a value."
|
10
|
-
)
|
11
|
-
else
|
12
|
-
@__vc_content_set_by_with_content = value
|
13
|
-
end
|
6
|
+
raise NilWithContentError if value.nil?
|
7
|
+
|
8
|
+
@__vc_content_set_by_with_content = value
|
14
9
|
|
15
10
|
self
|
16
11
|
end
|
data/lib/view_component.rb
CHANGED
@@ -7,29 +7,20 @@ module ViewComponent
|
|
7
7
|
extend ActiveSupport::Autoload
|
8
8
|
|
9
9
|
autoload :Base
|
10
|
+
autoload :CaptureCompatibility
|
10
11
|
autoload :Compiler
|
11
12
|
autoload :CompileCache
|
12
13
|
autoload :ComponentError
|
13
14
|
autoload :Config
|
14
15
|
autoload :Deprecation
|
16
|
+
autoload :InlineTemplate
|
15
17
|
autoload :Instrumentation
|
16
18
|
autoload :Preview
|
17
|
-
autoload :PreviewTemplateError
|
18
19
|
autoload :TestHelpers
|
19
20
|
autoload :SystemTestHelpers
|
20
21
|
autoload :TestCase
|
21
22
|
autoload :SystemTestCase
|
22
|
-
autoload :TemplateError
|
23
23
|
autoload :Translatable
|
24
24
|
end
|
25
25
|
|
26
|
-
|
27
|
-
if defined?(ViewComponent::Engine)
|
28
|
-
ViewComponent::Deprecation.deprecation_warning(
|
29
|
-
"Manually loading the engine",
|
30
|
-
"remove `require \"view_component/engine\"`"
|
31
|
-
)
|
32
|
-
elsif defined?(Rails::Engine)
|
33
|
-
require "view_component/engine"
|
34
|
-
end
|
35
|
-
# :nocov:
|
26
|
+
require "view_component/engine" if 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:
|
4
|
+
version: 3.0.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: 2023-
|
11
|
+
date: 2023-04-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -90,16 +90,16 @@ dependencies:
|
|
90
90
|
name: better_html
|
91
91
|
requirement: !ruby/object:Gem::Requirement
|
92
92
|
requirements:
|
93
|
-
- - "
|
93
|
+
- - ">="
|
94
94
|
- !ruby/object:Gem::Version
|
95
|
-
version: '
|
95
|
+
version: '0'
|
96
96
|
type: :development
|
97
97
|
prerelease: false
|
98
98
|
version_requirements: !ruby/object:Gem::Requirement
|
99
99
|
requirements:
|
100
|
-
- - "
|
100
|
+
- - ">="
|
101
101
|
- !ruby/object:Gem::Version
|
102
|
-
version: '
|
102
|
+
version: '0'
|
103
103
|
- !ruby/object:Gem::Dependency
|
104
104
|
name: bundler
|
105
105
|
requirement: !ruby/object:Gem::Requirement
|
@@ -118,16 +118,16 @@ dependencies:
|
|
118
118
|
name: erb_lint
|
119
119
|
requirement: !ruby/object:Gem::Requirement
|
120
120
|
requirements:
|
121
|
-
- - "
|
121
|
+
- - ">="
|
122
122
|
- !ruby/object:Gem::Version
|
123
|
-
version: 0
|
123
|
+
version: '0'
|
124
124
|
type: :development
|
125
125
|
prerelease: false
|
126
126
|
version_requirements: !ruby/object:Gem::Requirement
|
127
127
|
requirements:
|
128
|
-
- - "
|
128
|
+
- - ">="
|
129
129
|
- !ruby/object:Gem::Version
|
130
|
-
version: 0
|
130
|
+
version: '0'
|
131
131
|
- !ruby/object:Gem::Dependency
|
132
132
|
name: haml
|
133
133
|
requirement: !ruby/object:Gem::Requirement
|
@@ -212,6 +212,20 @@ dependencies:
|
|
212
212
|
- - "~>"
|
213
213
|
- !ruby/object:Gem::Version
|
214
214
|
version: '13.0'
|
215
|
+
- !ruby/object:Gem::Dependency
|
216
|
+
name: rubocop-md
|
217
|
+
requirement: !ruby/object:Gem::Requirement
|
218
|
+
requirements:
|
219
|
+
- - "~>"
|
220
|
+
- !ruby/object:Gem::Version
|
221
|
+
version: '1'
|
222
|
+
type: :development
|
223
|
+
prerelease: false
|
224
|
+
version_requirements: !ruby/object:Gem::Requirement
|
225
|
+
requirements:
|
226
|
+
- - "~>"
|
227
|
+
- !ruby/object:Gem::Version
|
228
|
+
version: '1'
|
215
229
|
- !ruby/object:Gem::Dependency
|
216
230
|
name: standard
|
217
231
|
requirement: !ruby/object:Gem::Requirement
|
@@ -353,20 +367,20 @@ files:
|
|
353
367
|
- lib/rails/generators/test_unit/templates/component_test.rb.tt
|
354
368
|
- lib/view_component.rb
|
355
369
|
- lib/view_component/base.rb
|
370
|
+
- lib/view_component/capture_compatibility.rb
|
356
371
|
- lib/view_component/collection.rb
|
357
372
|
- lib/view_component/compile_cache.rb
|
358
373
|
- lib/view_component/compiler.rb
|
359
374
|
- lib/view_component/component_error.rb
|
360
375
|
- lib/view_component/config.rb
|
361
|
-
- lib/view_component/content_areas.rb
|
362
376
|
- lib/view_component/deprecation.rb
|
363
377
|
- lib/view_component/docs_builder_component.html.erb
|
364
378
|
- lib/view_component/docs_builder_component.rb
|
365
379
|
- lib/view_component/engine.rb
|
380
|
+
- lib/view_component/errors.rb
|
381
|
+
- lib/view_component/inline_template.rb
|
366
382
|
- lib/view_component/instrumentation.rb
|
367
|
-
- lib/view_component/polymorphic_slots.rb
|
368
383
|
- lib/view_component/preview.rb
|
369
|
-
- lib/view_component/preview_template_error.rb
|
370
384
|
- lib/view_component/rails/tasks/view_component.rake
|
371
385
|
- lib/view_component/render_component_helper.rb
|
372
386
|
- lib/view_component/render_component_to_string_helper.rb
|
@@ -375,12 +389,9 @@ files:
|
|
375
389
|
- lib/view_component/rendering_component_helper.rb
|
376
390
|
- lib/view_component/rendering_monkey_patch.rb
|
377
391
|
- lib/view_component/slot.rb
|
378
|
-
- lib/view_component/slot_v2.rb
|
379
392
|
- lib/view_component/slotable.rb
|
380
|
-
- lib/view_component/slotable_v2.rb
|
381
393
|
- lib/view_component/system_test_case.rb
|
382
394
|
- lib/view_component/system_test_helpers.rb
|
383
|
-
- lib/view_component/template_error.rb
|
384
395
|
- lib/view_component/test_case.rb
|
385
396
|
- lib/view_component/test_helpers.rb
|
386
397
|
- lib/view_component/translatable.rb
|
@@ -402,7 +413,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
402
413
|
requirements:
|
403
414
|
- - ">="
|
404
415
|
- !ruby/object:Gem::Version
|
405
|
-
version: 2.
|
416
|
+
version: 2.7.0
|
406
417
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
407
418
|
requirements:
|
408
419
|
- - ">="
|
@@ -1,56 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "active_support/concern"
|
4
|
-
|
5
|
-
require "view_component/slot"
|
6
|
-
|
7
|
-
# DEPRECATED - ContentAreas is deprecated and will be removed in v3.0.0
|
8
|
-
module ViewComponent
|
9
|
-
module ContentAreas
|
10
|
-
extend ActiveSupport::Concern
|
11
|
-
|
12
|
-
# Assign the provided content to the content area accessor
|
13
|
-
#
|
14
|
-
# @private
|
15
|
-
def with(area, content = nil, &block)
|
16
|
-
unless content_areas.include?(area)
|
17
|
-
raise ArgumentError.new(
|
18
|
-
"Unknown content_area '#{area}' for #{self} - expected one of '#{content_areas}'.\n\n" \
|
19
|
-
"To fix this issue, add `with_content_area :#{area}` to #{self} or reference " \
|
20
|
-
"a valid content area."
|
21
|
-
)
|
22
|
-
end
|
23
|
-
|
24
|
-
if block
|
25
|
-
content = view_context.capture(&block)
|
26
|
-
end
|
27
|
-
|
28
|
-
instance_variable_set("@#{area}".to_sym, content)
|
29
|
-
nil
|
30
|
-
end
|
31
|
-
|
32
|
-
class_methods do
|
33
|
-
def with_content_areas(*areas)
|
34
|
-
ViewComponent::Deprecation.deprecation_warning(
|
35
|
-
"`with_content_areas`", "use slots (https://viewcomponent.org/guide/slots.html) instead"
|
36
|
-
)
|
37
|
-
|
38
|
-
if areas.include?(:content)
|
39
|
-
raise ArgumentError.new(
|
40
|
-
"#{self} defines a content area called :content, which is a reserved name. \n\n" \
|
41
|
-
"To fix this issue, use another name, such as `:body`."
|
42
|
-
)
|
43
|
-
end
|
44
|
-
|
45
|
-
areas.each do |area|
|
46
|
-
define_method area.to_sym do
|
47
|
-
content unless content_evaluated? # ensure content is loaded so content_areas will be defined
|
48
|
-
instance_variable_get(:"@#{area}") if instance_variable_defined?(:"@#{area}")
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
self.content_areas = areas
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
@@ -1,103 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ViewComponent
|
4
|
-
module PolymorphicSlots
|
5
|
-
# In older rails versions, using a concern isn't a good idea here because they appear to not work with
|
6
|
-
# Module#prepend and class methods.
|
7
|
-
def self.included(base)
|
8
|
-
if base != ViewComponent::Base
|
9
|
-
# :nocov:
|
10
|
-
location = Kernel.caller_locations(1, 1)[0]
|
11
|
-
|
12
|
-
warn(
|
13
|
-
"warning: ViewComponent::PolymorphicSlots is now included in ViewComponent::Base by default " \
|
14
|
-
"and can be removed from #{location.path}:#{location.lineno}"
|
15
|
-
)
|
16
|
-
# :nocov:
|
17
|
-
end
|
18
|
-
|
19
|
-
base.singleton_class.prepend(ClassMethods)
|
20
|
-
base.include(InstanceMethods)
|
21
|
-
end
|
22
|
-
|
23
|
-
module ClassMethods
|
24
|
-
def renders_one(slot_name, callable = nil)
|
25
|
-
return super unless callable.is_a?(Hash) && callable.key?(:types)
|
26
|
-
|
27
|
-
validate_singular_slot_name(slot_name)
|
28
|
-
register_polymorphic_slot(slot_name, callable[:types], collection: false)
|
29
|
-
end
|
30
|
-
|
31
|
-
def renders_many(slot_name, callable = nil)
|
32
|
-
return super unless callable.is_a?(Hash) && callable.key?(:types)
|
33
|
-
|
34
|
-
validate_plural_slot_name(slot_name)
|
35
|
-
register_polymorphic_slot(slot_name, callable[:types], collection: true)
|
36
|
-
end
|
37
|
-
|
38
|
-
def register_polymorphic_slot(slot_name, types, collection:)
|
39
|
-
unless types.empty?
|
40
|
-
getter_name = slot_name
|
41
|
-
|
42
|
-
define_method(getter_name) do
|
43
|
-
get_slot(slot_name)
|
44
|
-
end
|
45
|
-
|
46
|
-
define_method("#{getter_name}?") do
|
47
|
-
get_slot(slot_name).present?
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
renderable_hash = types.each_with_object({}) do |(poly_type, poly_callable), memo|
|
52
|
-
memo[poly_type] = define_slot(
|
53
|
-
"#{slot_name}_#{poly_type}", collection: collection, callable: poly_callable
|
54
|
-
)
|
55
|
-
|
56
|
-
setter_name =
|
57
|
-
if collection
|
58
|
-
"#{ActiveSupport::Inflector.singularize(slot_name)}_#{poly_type}"
|
59
|
-
else
|
60
|
-
"#{slot_name}_#{poly_type}"
|
61
|
-
end
|
62
|
-
|
63
|
-
define_method(setter_name) do |*args, &block|
|
64
|
-
if _warn_on_deprecated_slot_setter
|
65
|
-
ViewComponent::Deprecation.deprecation_warning(
|
66
|
-
"Using polymorphic slot setters like `#{setter_name}`",
|
67
|
-
:"`with_#{setter_name}`"
|
68
|
-
)
|
69
|
-
end
|
70
|
-
|
71
|
-
set_polymorphic_slot(slot_name, poly_type, *args, &block)
|
72
|
-
end
|
73
|
-
ruby2_keywords(setter_name.to_sym) if respond_to?(:ruby2_keywords, true)
|
74
|
-
|
75
|
-
define_method("with_#{setter_name}") do |*args, &block|
|
76
|
-
set_polymorphic_slot(slot_name, poly_type, *args, &block)
|
77
|
-
end
|
78
|
-
ruby2_keywords(:"with_#{setter_name}") if respond_to?(:ruby2_keywords, true)
|
79
|
-
end
|
80
|
-
|
81
|
-
registered_slots[slot_name] = {
|
82
|
-
collection: collection,
|
83
|
-
renderable_hash: renderable_hash
|
84
|
-
}
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
module InstanceMethods
|
89
|
-
def set_polymorphic_slot(slot_name, poly_type = nil, *args, &block)
|
90
|
-
slot_definition = self.class.registered_slots[slot_name]
|
91
|
-
|
92
|
-
if !slot_definition[:collection] && (defined?(@__vc_set_slots) && @__vc_set_slots[slot_name])
|
93
|
-
raise ArgumentError, "content for slot '#{slot_name}' has already been provided"
|
94
|
-
end
|
95
|
-
|
96
|
-
poly_def = slot_definition[:renderable_hash][poly_type]
|
97
|
-
|
98
|
-
set_slot(slot_name, poly_def, *args, &block)
|
99
|
-
end
|
100
|
-
ruby2_keywords(:set_polymorphic_slot) if respond_to?(:ruby2_keywords, true)
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|