view_component 2.82.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of view_component might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9bb20706245c43b9c5fa4d11087a2e98332bda53d659f7065874a3c0b8545547
4
- data.tar.gz: 13821a1e91da8a8dce8270962b99aa3b84fddb9731f99c2e2acfd143ad017836
3
+ metadata.gz: 639cd1c50a78567738c9d90363e98cb740f1f45ca70c9cb7ea20d22aacbce6e9
4
+ data.tar.gz: cbec2597e6193dcb43ddab199d1147977d505dd320b765ed38eb11f877917f23
5
5
  SHA512:
6
- metadata.gz: 5b0ebf7ac54fc2c82374b29f9cceadfe55af73516ad2e4c65dad69cd65a7c63de3e00ef9cdba8b359c1061309f9a0d2f90344374feec2b37211fbfb36282c96b
7
- data.tar.gz: 48e156dcc5fa1ac9a6cff64e4cb585d528103452938a515d1e3598b505b03188e89362c393c86c0a05437f9a8c28f22e937e3d706f3da17ae03a7c1371728505
6
+ metadata.gz: b26863964af13fd7c3bca798f4acd88c87709965badda42b02fe8125151f6e91b879e5118551d5b4c041509bbe26c2d463fcd4470bf6cbe5f946bc99c34f6a9c
7
+ data.tar.gz: e170eae333570563f5e068e8cbc1c4cfab05363671a9691169eaa2cbb30202076d38d8b59b8d7b28a73ab8777542c9173455e6f828f5f7344a3213424a69d049
@@ -1,7 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class ViewComponentsSystemTestController < ActionController::Base # :nodoc:
4
+ before_action :validate_test_env
5
+ before_action :validate_file_path
6
+
7
+ def self.temp_dir
8
+ @_tmpdir ||= FileUtils.mkdir_p("./tmp/view_components/").first
9
+ end
10
+
4
11
  def system_test_entrypoint
5
- render file: "./tmp/view_components/#{params.permit(:file)[:file]}"
12
+ render file: @path
13
+ end
14
+
15
+ private
16
+
17
+ def validate_test_env
18
+ raise ViewComponent::SystemTestControllerOnlyAllowedInTestError unless Rails.env.test?
19
+ end
20
+
21
+ # Ensure that the file path is valid and doesn't target files outside
22
+ # the expected directory (e.g. via a path traversal or symlink attack)
23
+ def validate_file_path
24
+ base_path = ::File.realpath(self.class.temp_dir)
25
+ @path = ::File.realpath(params.permit(:file)[:file], base_path)
26
+ unless @path.start_with?(base_path)
27
+ raise ViewComponent::SystemTestControllerNefariousPathError
28
+ end
6
29
  end
7
30
  end
@@ -31,10 +31,8 @@ module PreviewHelper
31
31
  path =~ /#{template_identifier}*.(html)/
32
32
  end
33
33
 
34
- # In-case of a conflict due to multiple template files with
35
- # the same name
36
- raise "found 0 matches for templates for #{template_identifier}." if matching_templates.empty?
37
- raise "found multiple templates for #{template_identifier}." if matching_templates.size > 1
34
+ raise ViewComponent::NoMatchingTemplatesForPreviewError.new(template_identifier) if matching_templates.empty?
35
+ raise ViewComponent::MultipleMatchingTemplatesForPreviewError.new(template_identifier) if matching_templates.size > 1
38
36
 
39
37
  template_file_path = matching_templates.first
40
38
  template_source = File.read(template_file_path)
data/docs/CHANGELOG.md CHANGED
@@ -10,6 +10,284 @@ nav_order: 5
10
10
 
11
11
  ## main
12
12
 
13
+ ### v3.0.0
14
+
15
+ 1,000+ days and 100+ releases later, the 200+ contributors to ViewComponent are proud to ship v3.0.0!
16
+
17
+ We're so grateful for all the work of community members to get us to this release. Whether it’s filing bug reports, designing APIs in long-winded discussion threads, or writing code itself, ViewComponent is built by the community, for the community. We couldn’t be more proud of what we’re building together :heart:
18
+
19
+ This release makes the following breaking changes, many of which have long been deprecated:
20
+
21
+ * BREAKING: Remove deprecated slots setter methods. Use `with_SLOT_NAME` instead.
22
+
23
+ *Joel Hawksley*
24
+
25
+ * BREAKING: Remove deprecated SlotsV1 in favor of current SlotsV2.
26
+
27
+ *Joel Hawksley*
28
+
29
+ * BREAKING: Remove deprecated `content_areas` feature. Use Slots instead.
30
+
31
+ *Joel Hawksley*
32
+
33
+ * BREAKING: Remove deprecated support for loading ViewComponent engine manually. Make sure `require "view_component/engine"` is removed from `Gemfile`.
34
+
35
+ *Joel Hawksley*
36
+
37
+ * BREAKING: Remove deprecated `generate_*` methods. Use `generate.*` instead.
38
+
39
+ *Joel Hawksley*
40
+
41
+ * BREAKING: Remove deprecated `with_variant` method.
42
+
43
+ *Joel Hawksley*
44
+
45
+ * BREAKING: Remove deprecated `rendered_component` in favor of `rendered_content`.
46
+
47
+ *Joel Hawksley*
48
+
49
+ * BREAKING: Remove deprecated `config.preview_path` in favor of `config.preview_paths`.
50
+
51
+ *Joel Hawksley*
52
+
53
+ * BREAKING: Support Ruby 2.7+ instead of 2.4+
54
+
55
+ *Joel Hawksley*
56
+
57
+ * BREAKING: Remove deprecated `before_render_check`.
58
+
59
+ *Joel Hawksley*
60
+
61
+ * BREAKING: Change counter variable to start iterating from `0` instead of `1`.
62
+
63
+ *Frank S*
64
+
65
+ * BREAKING: `#SLOT_NAME` getter no longer accepts arguments. This change was missed as part of the earlier deprecation in `3.0.0.rc1`.
66
+
67
+ *Joel Hawksley*
68
+
69
+ * BREAKING: Raise `TranslateCalledBeforeRenderError`, `ControllerCalledBeforeRenderError`, or `HelpersCalledBeforeRenderError` instead of `ViewContextCalledBeforeRenderError`.
70
+
71
+ *Joel Hawksley*
72
+
73
+ * BREAKING: Raise `SlotPredicateNameError`, `RedefinedSlotError`, `ReservedSingularSlotNameError`, `ContentSlotNameError`, `InvalidSlotDefinitionError`, `ReservedPluralSlotNameError`, `ContentAlreadySetForPolymorphicSlotErrror`, `SystemTestControllerOnlyAllowedInTestError`, `SystemTestControllerNefariousPathError`, `NoMatchingTemplatesForPreviewError`, `MultipleMatchingTemplatesForPreviewError`, `DuplicateContentError`, `EmptyOrInvalidInitializerError`, `MissingCollectionArgumentError`, `ReservedParameterError`, `InvalidCollectionArgumentError`, `MultipleInlineTemplatesError`, `MissingPreviewTemplateError`, `DuplicateSlotContentError` or `NilWithContentError` instead of generic error classes.
74
+
75
+ *Joel Hawksley*
76
+
77
+ * BREAKING: Rename `SlotV2` to `Slot` and `SlotableV2` to `Slotable`.
78
+
79
+ *Joel Hawksley*
80
+
81
+ * BREAKING: Incorporate `PolymorphicSlots` into `Slotable`. To migrate, remove any references to `PolymorphicSlots` as they are no longer necessary.
82
+
83
+ *Joel Hawksley*
84
+
85
+ * BREAKING: Rename private TestHelpers#controller, #build_controller, #request, and #preview_class to avoid conflicts. Note: While these methods were undocumented and marked as private, they were accessible in tests. As such, we're considering this to be a breaking change.
86
+
87
+ *Joel Hawksley*
88
+
89
+ ### v3.0.0.rc6
90
+
91
+ Run into an issue with this release candidate? [Let us know](https://github.com/ViewComponent/view_component/issues/1629). We hope to release v3.0.0 in the near future!
92
+
93
+ * BREAKING: `#SLOT_NAME` getter no longer accepts arguments. This change was missed as part of the earlier deprecation in `3.0.0.rc1`.
94
+
95
+ *Joel Hawksley*
96
+
97
+ * BREAKING: Raise `TranslateCalledBeforeRenderError`, `ControllerCalledBeforeRenderError`, or `HelpersCalledBeforeRenderError` instead of `ViewContextCalledBeforeRenderError`.
98
+
99
+ *Joel Hawksley*
100
+
101
+ * BREAKING: Raise `SlotPredicateNameError`, `RedefinedSlotError`, `ReservedSingularSlotNameError`, `ContentSlotNameError`, `InvalidSlotDefinitionError`, `ReservedPluralSlotNameError`, `ContentAlreadySetForPolymorphicSlotErrror`, `SystemTestControllerOnlyAllowedInTestError`, `SystemTestControllerNefariousPathError`, `NoMatchingTemplatesForPreviewError`, `MultipleMatchingTemplatesForPreviewError`, `DuplicateContentError`, `EmptyOrInvalidInitializerError`, `MissingCollectionArgumentError`, `ReservedParameterError`, `InvalidCollectionArgumentError`, `MultipleInlineTemplatesError`, `MissingPreviewTemplateError`, `DuplicateSlotContentError` or `NilWithContentError` instead of generic error classes.
102
+
103
+ *Joel Hawksley*
104
+
105
+ * Fix bug where `content?` and `with_content` didn't work reliably with slots.
106
+
107
+ *Derek Kniffin, Joel Hawksley*
108
+
109
+ * Add `with_SLOT_NAME_content` helper.
110
+
111
+ *Will Cosgrove*
112
+
113
+ * Allow ActiveRecord objects to be passed to `renders_many`.
114
+
115
+ *Leigh Halliday*
116
+
117
+ * Fix broken links in documentation.
118
+
119
+ *Ellen Keal*
120
+
121
+ * Run `standardrb` against markdown in docs.
122
+
123
+ *Joel Hawksley*
124
+
125
+ * Allow `.with_content` to be redefined by components.
126
+
127
+ *Joel Hawksley*
128
+
129
+ * Run `standardrb` against markdown in docs.
130
+
131
+ *Joel Hawksley*
132
+
133
+ * Raise error if translations are used in initializer.
134
+
135
+ *Joel Hawksley*
136
+
137
+ ## v3.0.0.rc5
138
+
139
+ Run into an issue with this release candidate? [Let us know](https://github.com/ViewComponent/view_component/issues/1629).
140
+
141
+ * Fix bug where `mkdir_p` failed due to incorrect permissions.
142
+
143
+ *Joel Hawksley*
144
+
145
+ * Check for inline `erb_template` calls when deciding whether to compile a component's superclass.
146
+
147
+ *Justin Kenyon*
148
+
149
+ * Protect against `SystemStackError` if `CaptureCompatibility` module is included more than once.
150
+
151
+ *Cameron Dutro*
152
+
153
+ ## v3.0.0.rc4
154
+
155
+ Run into an issue with this release candidate? [Let us know](https://github.com/ViewComponent/view_component/issues/1629).
156
+
157
+ * Add `TestHelpers#vc_test_request`.
158
+
159
+ *Joel Hawksley*
160
+
161
+ ## v3.0.0.rc3
162
+
163
+ Run into an issue with this release candidate? [Let us know](https://github.com/ViewComponent/view_component/issues/1629).
164
+
165
+ * Fix typos in generator docs.
166
+
167
+ *Sascha Karnatz*
168
+
169
+ * Add `TestHelpers#vc_test_controller`.
170
+
171
+ *Joel Hawksley*
172
+
173
+ * Document `config.view_component.capture_compatibility_patch_enabled` as option for the known incompatibilities with Rails form helpers.
174
+
175
+ *Tobias L. Maier*
176
+
177
+ * Add support for experimental inline templates.
178
+
179
+ *Blake Williams*
180
+
181
+ * Expose `translate` and `t` I18n methods on component classes.
182
+
183
+ *Elia Schito*
184
+
185
+ * Protect against Arbitrary File Read edge case in `ViewComponentsSystemTestController`.
186
+
187
+ *Nick Malcolm*
188
+
189
+ ## v3.0.0.rc2
190
+
191
+ Run into an issue with this release? [Let us know](https://github.com/ViewComponent/view_component/issues/1629).
192
+
193
+ * BREAKING: Rename `SlotV2` to `Slot` and `SlotableV2` to `Slotable`.
194
+
195
+ *Joel Hawksley*
196
+
197
+ * BREAKING: Incorporate `PolymorphicSlots` into `Slotable`. To migrate, remove any references to `PolymorphicSlots` as they are no longer necessary.
198
+
199
+ *Joel Hawksley*
200
+
201
+ * BREAKING: Rename private TestHelpers#controller, #build_controller, #request, and #preview_class to avoid conflicts. Note: While these methods were undocumented and marked as private, they were accessible in tests. As such, we're considering this to be a breaking change.
202
+
203
+ *Joel Hawksley*
204
+
205
+ * Avoid loading ActionView::Base during Rails initialization. Originally submitted in #1528.
206
+
207
+ *Jonathan del Strother*
208
+
209
+ * Improve documentation of known incompatibilities with Rails form helpers.
210
+
211
+ *Tobias L. Maier*
212
+
213
+ * Remove dependency on environment task from `view_component:statsetup`.
214
+
215
+ *Svetlin Simonyan*
216
+
217
+ * Add experimental `config.view_component.capture_compatibility_patch_enabled` option resolving rendering issues related to forms, capture, turbo frames, etc.
218
+
219
+ *Blake Williams*
220
+
221
+ * Add `#content?` method that indicates if content has been passed to component.
222
+
223
+ *Joel Hawksley*
224
+
225
+ * Added example of a custom preview controller.
226
+
227
+ *Graham Rogers*
228
+
229
+ * Add Krystal to list of companies using ViewComponent.
230
+
231
+ *Matt Bearman*
232
+
233
+ * Add Mon Ami to list of companies using ViewComponent.
234
+
235
+ *Ethan Lee-Tyson*
236
+
237
+ ## 3.0.0.rc1
238
+
239
+ 1,000+ days and 100+ releases later, the 200+ contributors to ViewComponent are proud to ship v3.0.0!
240
+
241
+ We're so grateful for all the work of community members to get us to this release. Whether it’s filing bug reports, designing APIs in long-winded discussion threads, or writing code itself, ViewComponent is built by the community, for the community. We couldn’t be more proud of what we’re building together :heart:
242
+
243
+ This release makes the following breaking changes, many of which have long been deprecated:
244
+
245
+ * BREAKING: Remove deprecated slots setter methods. Use `with_SLOT_NAME` instead.
246
+
247
+ *Joel Hawksley*
248
+
249
+ * BREAKING: Remove deprecated SlotsV1 in favor of current SlotsV2.
250
+
251
+ *Joel Hawksley*
252
+
253
+ * BREAKING: Remove deprecated `content_areas` feature. Use Slots instead.
254
+
255
+ *Joel Hawksley*
256
+
257
+ * BREAKING: Remove deprecated support for loading ViewComponent engine manually. Make sure `require "view_component/engine"` is removed from `Gemfile`.
258
+
259
+ *Joel Hawksley*
260
+
261
+ * BREAKING: Remove deprecated `generate_*` methods. Use `generate.*` instead.
262
+
263
+ *Joel Hawksley*
264
+
265
+ * BREAKING: Remove deprecated `with_variant` method.
266
+
267
+ *Joel Hawksley*
268
+
269
+ * BREAKING: Remove deprecated `rendered_component` in favor of `rendered_content`.
270
+
271
+ *Joel Hawksley*
272
+
273
+ * BREAKING: Remove deprecated `config.preview_path` in favor of `config.preview_paths`.
274
+
275
+ *Joel Hawksley*
276
+
277
+ * BREAKING: Support Ruby 2.7+ instead of 2.4+
278
+
279
+ *Joel Hawksley*
280
+
281
+ * BREAKING: Remove deprecated `before_render_check`.
282
+
283
+ *Joel Hawksley*
284
+
285
+ * BREAKING: Change counter variable to start iterating from `0` instead of `1`.
286
+
287
+ *Frank S*
288
+
289
+ Run into an issue with this release? [Let us know](https://github.com/ViewComponent/view_component/issues/1629).
290
+
13
291
  ## 2.82.0
14
292
 
15
293
  * Revert "Avoid loading ActionView::Base during initialization (#1528)"
@@ -6,11 +6,9 @@ require "view_component/collection"
6
6
  require "view_component/compile_cache"
7
7
  require "view_component/compiler"
8
8
  require "view_component/config"
9
- require "view_component/content_areas"
10
- require "view_component/polymorphic_slots"
9
+ require "view_component/errors"
11
10
  require "view_component/preview"
12
11
  require "view_component/slotable"
13
- require "view_component/slotable_v2"
14
12
  require "view_component/translatable"
15
13
  require "view_component/with_content_helper"
16
14
 
@@ -23,7 +21,7 @@ module ViewComponent
23
21
  #
24
22
  # @return [ViewComponent::Config]
25
23
  def config
26
- @config ||= ViewComponent::Config.defaults
24
+ @config ||= ActiveSupport::OrderedOptions.new
27
25
  end
28
26
 
29
27
  # Replaces the entire config. You shouldn't need to use this directly
@@ -31,22 +29,15 @@ module ViewComponent
31
29
  attr_writer :config
32
30
  end
33
31
 
34
- include ViewComponent::ContentAreas
35
- include ViewComponent::PolymorphicSlots
36
- include ViewComponent::SlotableV2
32
+ include ViewComponent::Slotable
37
33
  include ViewComponent::Translatable
38
34
  include ViewComponent::WithContentHelper
39
35
 
40
- ViewContextCalledBeforeRenderError = Class.new(StandardError)
41
-
42
36
  RESERVED_PARAMETER = :content
43
37
 
44
38
  # For CSRF authenticity tokens in forms
45
39
  delegate :form_authenticity_token, :protect_against_forgery?, :config, to: :helpers
46
40
 
47
- class_attribute :content_areas
48
- self.content_areas = [] # class_attribute:default doesn't work until Rails 5.2
49
-
50
41
  # Config option that strips trailing whitespace in templates before compiling them.
51
42
  class_attribute :__vc_strip_trailing_whitespace, instance_accessor: false, instance_predicate: false
52
43
  self.__vc_strip_trailing_whitespace = false # class_attribute:default doesn't work until Rails 5.2
@@ -66,23 +57,6 @@ module ViewComponent
66
57
  self.__vc_original_view_context = view_context
67
58
  end
68
59
 
69
- # @!macro [attach] deprecated_generate_mattr_accessor
70
- # @method generate_$1
71
- # @deprecated Use `#generate.$1` instead. Will be removed in v3.0.0.
72
- def self._deprecated_generate_mattr_accessor(name)
73
- define_singleton_method("generate_#{name}".to_sym) do
74
- generate.public_send(name)
75
- end
76
- define_singleton_method("generate_#{name}=".to_sym) do |value|
77
- generate.public_send("#{name}=".to_sym, value)
78
- end
79
- end
80
-
81
- _deprecated_generate_mattr_accessor :distinct_locale_files
82
- _deprecated_generate_mattr_accessor :locale
83
- _deprecated_generate_mattr_accessor :sidecar
84
- _deprecated_generate_mattr_accessor :stimulus_controller
85
-
86
60
  # Entrypoint for rendering components.
87
61
  #
88
62
  # - `view_context`: ActionView context from calling view
@@ -119,9 +93,7 @@ module ViewComponent
119
93
  @current_template = self
120
94
 
121
95
  if block && defined?(@__vc_content_set_by_with_content)
122
- raise ArgumentError, "It looks like a block was provided after calling `with_content` on #{self.class.name}, " \
123
- "which means that ViewComponent doesn't know which content to use.\n\n" \
124
- "To fix this issue, use either `with_content` or a block."
96
+ raise DuplicateContentError.new(self.class.name)
125
97
  end
126
98
 
127
99
  @__vc_content_evaluated = false
@@ -165,14 +137,6 @@ module ViewComponent
165
137
  #
166
138
  # @return [void]
167
139
  def before_render
168
- before_render_check
169
- end
170
-
171
- # Called after rendering the component.
172
- #
173
- # @deprecated Use `#before_render` instead. Will be removed in v3.0.0.
174
- # @return [void]
175
- def before_render_check
176
140
  # noop
177
141
  end
178
142
 
@@ -208,16 +172,7 @@ module ViewComponent
208
172
  #
209
173
  # @return [ActionController::Base]
210
174
  def controller
211
- if view_context.nil?
212
- raise(
213
- ViewContextCalledBeforeRenderError,
214
- "`#controller` can't be used during initialization, as it depends " \
215
- "on the view context that only exists once a ViewComponent is passed to " \
216
- "the Rails render pipeline.\n\n" \
217
- "It's sometimes possible to fix this issue by moving code dependent on " \
218
- "`#controller` to a `#before_render` method: https://viewcomponent.org/api.html#before_render--void."
219
- )
220
- end
175
+ raise ControllerCalledBeforeRenderError if view_context.nil?
221
176
 
222
177
  @__vc_controller ||= view_context.controller
223
178
  end
@@ -227,16 +182,7 @@ module ViewComponent
227
182
  #
228
183
  # @return [ActionView::Base]
229
184
  def helpers
230
- if view_context.nil?
231
- raise(
232
- ViewContextCalledBeforeRenderError,
233
- "`#helpers` can't be used during initialization, as it depends " \
234
- "on the view context that only exists once a ViewComponent is passed to " \
235
- "the Rails render pipeline.\n\n" \
236
- "It's sometimes possible to fix this issue by moving code dependent on " \
237
- "`#helpers` to a `#before_render` method: https://viewcomponent.org/api.html#before_render--void."
238
- )
239
- end
185
+ raise HelpersCalledBeforeRenderError if view_context.nil?
240
186
 
241
187
  # Attempt to re-use the original view_context passed to the first
242
188
  # component rendered in the rendering pipeline. This prevents the
@@ -265,22 +211,9 @@ module ViewComponent
265
211
  #
266
212
  # @private
267
213
  def format
268
- # Ruby 2.6 throws a warning without checking `defined?`, 2.7 doesn't
269
214
  @__vc_variant if defined?(@__vc_variant)
270
215
  end
271
216
 
272
- # Use the provided variant instead of the one determined by the current request.
273
- #
274
- # @deprecated Will be removed in v3.0.0.
275
- # @param variant [Symbol] The variant to be used by the component.
276
- # @return [self]
277
- def with_variant(variant)
278
- @__vc_variant = variant
279
-
280
- self
281
- end
282
- deprecate :with_variant, deprecator: ViewComponent::Deprecation
283
-
284
217
  # The current request. Use sparingly as doing so introduces coupling that
285
218
  # inhibits encapsulation & reuse, often making testing difficult.
286
219
  #
@@ -289,24 +222,42 @@ module ViewComponent
289
222
  @request ||= controller.request if controller.respond_to?(:request)
290
223
  end
291
224
 
292
- private
293
-
294
- attr_reader :view_context
295
-
225
+ # The content passed to the component instance as a block.
226
+ #
227
+ # @return [String]
296
228
  def content
297
229
  @__vc_content_evaluated = true
298
230
  return @__vc_content if defined?(@__vc_content)
299
231
 
300
232
  @__vc_content =
301
- if @view_context && @__vc_render_in_block
233
+ if __vc_render_in_block_provided?
302
234
  view_context.capture(self, &@__vc_render_in_block)
303
- elsif defined?(@__vc_content_set_by_with_content)
235
+ elsif __vc_content_set_by_with_content_defined?
304
236
  @__vc_content_set_by_with_content
305
237
  end
306
238
  end
307
239
 
240
+ # Whether `content` has been passed to the component.
241
+ #
242
+ # @return [Boolean]
243
+ def content?
244
+ __vc_render_in_block_provided? || __vc_content_set_by_with_content_defined?
245
+ end
246
+
247
+ private
248
+
249
+ attr_reader :view_context
250
+
251
+ def __vc_render_in_block_provided?
252
+ defined?(@view_context) && @view_context && @__vc_render_in_block
253
+ end
254
+
255
+ def __vc_content_set_by_with_content_defined?
256
+ defined?(@__vc_content_set_by_with_content)
257
+ end
258
+
308
259
  def content_evaluated?
309
- @__vc_content_evaluated
260
+ defined?(@__vc_content_evaluated) && @__vc_content_evaluated
310
261
  end
311
262
 
312
263
  # Set the controller used for testing components:
@@ -509,6 +460,11 @@ module ViewComponent
509
460
  compiler.compiled?
510
461
  end
511
462
 
463
+ # @private
464
+ def ensure_compiled
465
+ compile unless compiled?
466
+ end
467
+
512
468
  # Compile templates to instance methods, assuming they haven't been compiled already.
513
469
  #
514
470
  # Do as much work as possible in this step, as doing so reduces the amount
@@ -558,7 +514,7 @@ module ViewComponent
558
514
  # end
559
515
  # ```
560
516
  #
561
- # @param value [Boolean] Whether or not to strip newlines.
517
+ # @param value [Boolean] Whether to strip newlines.
562
518
  def strip_trailing_whitespace(value = true)
563
519
  self.__vc_strip_trailing_whitespace = value
564
520
  end
@@ -582,20 +538,14 @@ module ViewComponent
582
538
  return unless parameter
583
539
  return if initialize_parameter_names.include?(parameter) || splatted_keyword_argument_present?
584
540
 
585
- # If Ruby can't parse the component class, then the initalize
541
+ # If Ruby can't parse the component class, then the initialize
586
542
  # parameters will be empty and ViewComponent will not be able to render
587
543
  # the component.
588
544
  if initialize_parameters.empty?
589
- raise ArgumentError, "The #{self} initializer is empty or invalid." \
590
- "It must accept the parameter `#{parameter}` to render it as a collection.\n\n" \
591
- "To fix this issue, update the initializer to accept `#{parameter}`.\n\n" \
592
- "See https://viewcomponent.org/guide/collections.html for more information on rendering collections."
545
+ raise EmptyOrInvalidInitializerError.new(name, parameter)
593
546
  end
594
547
 
595
- raise ArgumentError, "The initializer for #{self} doesn't accept the parameter `#{parameter}`, " \
596
- "which is required in order to render it as a collection.\n\n" \
597
- "To fix this issue, update the initializer to accept `#{parameter}`.\n\n" \
598
- "See https://viewcomponent.org/guide/collections.html for more information on rendering collections."
548
+ raise MissingCollectionArgumentError.new(name, parameter)
599
549
  end
600
550
 
601
551
  # Ensure the component initializer doesn't define
@@ -605,8 +555,7 @@ module ViewComponent
605
555
  def validate_initialization_parameters!
606
556
  return unless initialize_parameter_names.include?(RESERVED_PARAMETER)
607
557
 
608
- raise ViewComponent::ComponentError, "#{self} initializer can't accept the parameter `#{RESERVED_PARAMETER}`, as it will override a " \
609
- "public ViewComponent method. To fix this issue, rename the parameter."
558
+ raise ReservedParameterError.new(name, RESERVED_PARAMETER)
610
559
  end
611
560
 
612
561
  # @private
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ # CaptureCompatibility is a module that patches #capture to fix issues
5
+ # related to ViewComponent and functionality that relies on `capture`
6
+ # like forms, capture itself, turbo frames, etc.
7
+ #
8
+ # This underlying incompatibility with ViewComponent and capture is
9
+ # that several features like forms keep a reference to the primary
10
+ # `ActionView::Base` instance which has its own @output_buffer. When
11
+ # `#capture` is called on the original `ActionView::Base` instance while
12
+ # evaluating a block from a ViewComponent the @output_buffer is overridden
13
+ # in the ActionView::Base instance, and *not* the component. This results
14
+ # in a double render due to `#capture` implementation details.
15
+ #
16
+ # To resolve the issue, we override `#capture` so that we can delegate
17
+ # the `capture` logic to the ViewComponent that created the block.
18
+ module CaptureCompatibility
19
+ def self.included(base)
20
+ return if base < InstanceMethods
21
+
22
+ base.class_eval do
23
+ alias_method :original_capture, :capture
24
+ end
25
+
26
+ base.prepend(InstanceMethods)
27
+ end
28
+
29
+ module InstanceMethods
30
+ def capture(*args, &block)
31
+ # Handle blocks that originate from C code and raise, such as `&:method`
32
+ return original_capture(*args, &block) if block.source_location.nil?
33
+
34
+ block_context = block.binding.receiver
35
+
36
+ if block_context != self && block_context.class < ActionView::Base
37
+ block_context.original_capture(*args, &block)
38
+ else
39
+ original_capture(*args, &block)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -53,16 +53,13 @@ module ViewComponent
53
53
  if object.respond_to?(:to_ary)
54
54
  object.to_ary
55
55
  else
56
- raise ArgumentError.new(
57
- "The value of the first argument passed to `with_collection` isn't a valid collection. " \
58
- "Make sure it responds to `to_ary`."
59
- )
56
+ raise InvalidCollectionArgumentError
60
57
  end
61
58
  end
62
59
 
63
60
  def component_options(item, iterator)
64
61
  item_options = {component.collection_parameter => item}
65
- item_options[component.collection_counter_parameter] = iterator.index + 1 if component.counter_argument_present?
62
+ item_options[component.collection_counter_parameter] = iterator.index if component.counter_argument_present?
66
63
  item_options[component.collection_iteration_parameter] = iterator.dup if component.iteration_argument_present?
67
64
 
68
65
  @options.merge(item_options)