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 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)