view_component 4.0.0.alpha5 → 4.0.0.alpha7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 544de384800ad5d0870666b9b1fdb3a74da9fd59d77f78e0d72ee5e134a3c307
4
- data.tar.gz: 430c3652f5abc6fef9815b937274f66ebac34de297318e1ca6839589fa290ddf
3
+ metadata.gz: 8be3a213c479ad3bc1cb82fad0a04b6b9a3f424a4995e0162e447c50cd6b0ef9
4
+ data.tar.gz: a7744949cc46b57628a55e78bb0bd766f048f140c8ca1769c271032e9d648176
5
5
  SHA512:
6
- metadata.gz: f30651af979f6c1c5fd43514d04cef3f6430982198e593375d61ce79190e83b8b2d61d7cccb67607248f0b791346b09db33b4705e81cb257b87bd8255c5b2d63
7
- data.tar.gz: 956af9c486961785821e9401c9c1ce6b187f3f21a0425d5d6d36602f384f3beb79629f9be2e743e13f525bdc15498480565989cbdb37952a7b0e1cbb2aaabbb0
6
+ metadata.gz: 13776ebd6447652ce0ad682a40bc70a4141b6b2db8690c32d7191828adbe9fd16ec8d30775007b29ff4c71bc559317cc56b60c94ab2af67ef4d259ea89ec3220
7
+ data.tar.gz: 2463587d1336f2b30e17e51f728526157c27752adb66bbc3703d20b68326d20eb734ee2990c7583866723ba3d927564d22ea6ddd9c0a70e515264043ce1d08d6
@@ -8,7 +8,7 @@ module ViewComponent
8
8
  prepend_view_path File.expand_path("../../../views", __dir__)
9
9
 
10
10
  around_action :set_locale, only: :previews
11
- before_action :require_local!, unless: :show_previews?
11
+ before_action :require_local!, unless: :previews_enabled?
12
12
 
13
13
  content_security_policy(false)
14
14
 
@@ -47,12 +47,12 @@ module ViewComponent
47
47
 
48
48
  # :doc:
49
49
  def default_preview_layout
50
- ViewComponent::Base.config.default_preview_layout
50
+ ViewComponent::Base.config.previews.default_layout
51
51
  end
52
52
 
53
53
  # :doc:
54
- def show_previews?
55
- ViewComponent::Base.config.show_previews
54
+ def previews_enabled?
55
+ ViewComponent::Base.config.previews.enabled
56
56
  end
57
57
 
58
58
  # :doc:
@@ -64,7 +64,11 @@ module ViewComponent
64
64
  if preview
65
65
  @preview = ViewComponent::Preview.find(preview)
66
66
  else
67
+ # TODO: This branch is covered in #test_returns_404_when_preview_does_not_exist,
68
+ # but Simplecov doesn't always mark it as covered.
69
+ # :nocov:
67
70
  raise AbstractController::ActionNotFound, "Component preview '#{params[:path]}' not found."
71
+ # :nocov:
68
72
  end
69
73
  end
70
74
 
@@ -95,7 +99,7 @@ module ViewComponent
95
99
  end
96
100
 
97
101
  def prepend_preview_examples_view_path
98
- prepend_view_path(ViewComponent::Base.preview_paths)
102
+ prepend_view_path(ViewComponent::Base.previews.paths)
99
103
  end
100
104
  end
101
105
  end
@@ -19,9 +19,7 @@ class ViewComponentsSystemTestController < ActionController::Base # :nodoc:
19
19
  def validate_file_path
20
20
  base_path = ::File.realpath(self.class.temp_dir)
21
21
  @path = ::File.realpath(params.permit(:file)[:file], base_path)
22
- unless @path.start_with?(base_path)
23
- raise ViewComponent::SystemTestControllerNefariousPathError
24
- end
22
+ raise ViewComponent::SystemTestControllerNefariousPathError unless @path.start_with?(base_path)
25
23
  end
26
24
  end
27
25
  end
@@ -0,0 +1 @@
1
+ <%= render(UrlForMailerComponent.new) %>
data/docs/CHANGELOG.md CHANGED
@@ -10,6 +10,70 @@ nav_order: 6
10
10
 
11
11
  ## main
12
12
 
13
+ ## 4.0.0.alpha7
14
+
15
+ * BREAKING: Remove deprecated `use_helper(s)`. Use `include MyHelper` or `helpers.` proxy instead.
16
+
17
+ *Joel Hawksley*
18
+
19
+ * BREAKING: Support compatibility with `Dry::Initializer`. As a result, `EmptyOrInvalidInitializerError` will no longer be raised.
20
+
21
+ *Joel Hawksley*
22
+
23
+ * BREAKING: Rename `config.generate.component_parent_class` to `config.generate.parent_class`.
24
+
25
+ *Joel Hawksley*
26
+
27
+ * Fix bug where `config.previews.enabled` did not function properly in production environments.
28
+
29
+ *Joel Hawksley*
30
+
31
+ * `config.previews.default_layout` should default to nil.
32
+
33
+ *Joel Hawksley*
34
+
35
+ * Add test case for absolute URL path helpers in mailers.
36
+
37
+ *Joel Hawksley*
38
+
39
+ * Fix bug where response format wasn't set, which caused issues with Turbo Frames.
40
+
41
+ *Joel Hawksley*
42
+
43
+ ## 4.0.0.alpha6
44
+
45
+ * BREAKING: Remove `config.test_controller` in favor of `vc_test_controller_class` test helper method.
46
+
47
+ *Joel Hawksley*
48
+
49
+ * BREAKING: `config.component_parent_class` is now `config.generate.component_parent_class`, moving the generator-specific option to the generator configuration namespace.
50
+
51
+ *Joel Hawksley*
52
+
53
+ * BREAKING: Move previews-related configuration (`enabled`, `route`, `paths`, `default_layout`, `controller`) to under `previews` namespace.
54
+
55
+ *Joel Hawksley*
56
+
57
+ * Add template annotations for components with `def call`.
58
+
59
+ *Joel Hawksley*
60
+
61
+ * Add support for including Turbo::StreamsHelper.
62
+
63
+ *Stephen Nelson*
64
+
65
+ * Update documentation on performance to reflect more representative benchmark showing 2-3x speed increase over partials.
66
+
67
+ *Joel Hawksley*
68
+
69
+ * Add documentation note about instrumentation negatively affecting performance.
70
+
71
+ *Joel Hawksley*
72
+
73
+ * Revert object shapes optimization due to lack of evidence of improvement.
74
+
75
+ *Joel Hawksley*
76
+
13
77
  ## 4.0.0.alpha5
14
78
 
15
79
  * BREAKING: `config.view_component_path` is now `config.generate.path`, as components have long since been able to exist in any directory.
@@ -20,10 +84,6 @@ nav_order: 6
20
84
 
21
85
  *Joel Hawksley*
22
86
 
23
- * Add internal optimization for Ruby object shapes.
24
-
25
- *Adam Hess*, *Joel Hawksley*
26
-
27
87
  ## 4.0.0.alpha4
28
88
 
29
89
  * BREAKING: Remove default initializer from `ViewComponent::Base`. Previously, `ViewComponent::Base` defined a catch-all initializer that allowed components without an initializer defined to be passed arbitrary arguments.
@@ -156,6 +216,28 @@ This release makes the following breaking changes:
156
216
 
157
217
  *Simon Fish*
158
218
 
219
+ * Deprecate `use_helper(s)`. Use `include MyHelper` or `helpers.` proxy instead.
220
+
221
+ *Joel Hawksley*
222
+
223
+ * Reduce string allocations during compilation.
224
+
225
+ *Jonathan del Strother*
226
+
227
+ ## 3.23.2
228
+
229
+ * Include .tt files in published gem. Fixes templates not being available when using generators.
230
+
231
+ *Florian Aßmann*
232
+
233
+ ## 3.23.1
234
+
235
+ * Restore Rake tasks in published gem.
236
+
237
+ *Franz Liedke*
238
+
239
+ ## 3.23.0
240
+
159
241
  * Add docs about Slack channel in Ruby Central workspace. (Join us! #oss-view-component). Email joelhawksley@github.com for an invite.
160
242
 
161
243
  *Joel Hawksley
@@ -5,7 +5,6 @@ require "active_support/configurable"
5
5
  require "view_component/collection"
6
6
  require "view_component/compile_cache"
7
7
  require "view_component/compiler"
8
- require "view_component/component_local_config"
9
8
  require "view_component/config"
10
9
  require "view_component/errors"
11
10
  require "view_component/inline_template"
@@ -15,7 +14,6 @@ require "view_component/slotable"
15
14
  require "view_component/template"
16
15
  require "view_component/translatable"
17
16
  require "view_component/with_content_helper"
18
- require "view_component/use_helpers"
19
17
 
20
18
  module ActionView
21
19
  class OutputBuffer
@@ -35,15 +33,6 @@ module ViewComponent
35
33
  class << self
36
34
  delegate(*ViewComponent::Config.defaults.keys, to: :config)
37
35
 
38
- # Redefine `new` so we can pre-allocate instance variables to optimize
39
- # for Ruby object shapes.
40
- def new(...)
41
- instance = allocate
42
- instance.__vc_pre_allocate_instance_variables
43
- instance.send(:initialize, ...)
44
- instance
45
- end
46
-
47
36
  # Returns the current config.
48
37
  #
49
38
  # @return [ActiveSupport::OrderedOptions]
@@ -57,39 +46,15 @@ module ViewComponent
57
46
  end
58
47
  end
59
48
 
60
- def __vc_pre_allocate_instance_variables
61
- @__vc_parent_render_level = 0
62
- @__vc_set_slots = {}
63
- @__vc_content_evaluated = false
64
- @current_template = nil
65
- @output_buffer = nil
66
- @lookup_context = nil
67
- @view_flow = nil
68
- @view_context = nil
69
- @virtual_path = nil
70
- @__vc_ancestor_calls = nil
71
- @__vc_controller = nil
72
- @__vc_content = :unset # some behaviors depend on checking for nil
73
- @__vc_content_set_by_with_content = nil
74
- @__vc_helpers = nil
75
- @__vc_inline_template = nil
76
- @__vc_inline_template_defined = nil
77
- @__vc_render_in_block = nil
78
- @__vc_request = nil
79
- @__vc_requested_details = nil
80
- @__vc_original_view_context = nil
81
- end
82
-
83
49
  include ActionView::Helpers
50
+ include Rails.application.routes.url_helpers if defined?(Rails) && Rails.application
84
51
  include ERB::Escape
85
52
  include ActiveSupport::CoreExt::ERBUtil
86
53
 
87
54
  include ViewComponent::InlineTemplate
88
- include ViewComponent::UseHelpers
89
55
  include ViewComponent::Slotable
90
56
  include ViewComponent::Translatable
91
57
  include ViewComponent::WithContentHelper
92
- include ViewComponent::ComponentLocalConfig
93
58
 
94
59
  # For CSRF authenticity tokens in forms
95
60
  delegate :form_authenticity_token, :protect_against_forgery?, :config, to: :helpers
@@ -97,9 +62,17 @@ module ViewComponent
97
62
  # HTML construction methods
98
63
  delegate :output_buffer, :lookup_context, :view_renderer, :view_flow, to: :helpers
99
64
 
65
+ # For Turbo::StreamsHelper
66
+ delegate :formats, :formats=, to: :helpers
67
+
100
68
  # For Content Security Policy nonces
101
69
  delegate :content_security_policy_nonce, to: :helpers
102
70
 
71
+ # Config option that strips trailing whitespace in templates before compiling them.
72
+ class_attribute :__vc_strip_trailing_whitespace, instance_accessor: false, instance_predicate: false, default: false
73
+
74
+ class_attribute :__vc_response_format, instance_accessor: false, instance_predicate: false, default: nil
75
+
103
76
  attr_accessor :__vc_original_view_context
104
77
  attr_reader :current_template
105
78
 
@@ -118,6 +91,11 @@ module ViewComponent
118
91
 
119
92
  using RequestDetails
120
93
 
94
+ # Including `Rails.application.routes.url_helpers` defines an initializer that accepts (...),
95
+ # so we have to define our own empty initializer to overwrite it.
96
+ def initialize
97
+ end
98
+
121
99
  # Entrypoint for rendering components.
122
100
  #
123
101
  # - `view_context`: ActionView context from calling view
@@ -146,12 +124,14 @@ module ViewComponent
146
124
  @__vc_requested_details ||= @lookup_context.vc_requested_details
147
125
 
148
126
  # For caching, such as #cache_if
127
+ @current_template = nil unless defined?(@current_template)
149
128
  old_current_template = @current_template
150
129
 
151
- if block && __vc_content_set_by_with_content?
130
+ if block && defined?(@__vc_content_set_by_with_content)
152
131
  raise DuplicateContentError.new(self.class.name)
153
132
  end
154
133
 
134
+ @__vc_content_evaluated = false
155
135
  @__vc_render_in_block = block
156
136
 
157
137
  before_render
@@ -166,10 +146,15 @@ module ViewComponent
166
146
  value = if output_preamble.blank? && output_postamble.blank?
167
147
  rendered_template
168
148
  else
169
- safe_output_preamble + rendered_template + safe_output_postamble
149
+ __vc_safe_output_preamble + rendered_template + __vc_safe_output_postamble
170
150
  end
171
151
  end
172
152
 
153
+ if ActionView::Base.annotate_rendered_view_with_filenames && current_template.inline_call? && request&.format == :html
154
+ identifier = defined?(Rails.root) ? self.class.identifier.sub("#{Rails.root}/", "") : self.class.identifier
155
+ value = "<!-- BEGIN #{identifier} -->".html_safe + value + "<!-- END #{identifier} -->".html_safe
156
+ end
157
+
173
158
  value
174
159
  else
175
160
  ""
@@ -205,12 +190,16 @@ module ViewComponent
205
190
  #
206
191
  # When rendering the parent inside an .erb template, use `#render_parent` instead.
207
192
  def render_parent_to_string
208
- target_render = self.class.instance_variable_get(:@__vc_ancestor_calls)[@__vc_parent_render_level]
209
- @__vc_parent_render_level += 1
193
+ @__vc_parent_render_level ||= 0 # ensure a good starting value
210
194
 
211
- target_render.bind_call(self, @__vc_requested_details)
212
- ensure
213
- @__vc_parent_render_level -= 1
195
+ begin
196
+ target_render = self.class.instance_variable_get(:@__vc_ancestor_calls)[@__vc_parent_render_level]
197
+ @__vc_parent_render_level += 1
198
+
199
+ target_render.bind_call(self, @__vc_requested_details)
200
+ ensure
201
+ @__vc_parent_render_level -= 1
202
+ end
214
203
  end
215
204
 
216
205
  # Optional content to be returned before the rendered template.
@@ -333,12 +322,12 @@ module ViewComponent
333
322
  # @return [String]
334
323
  def content
335
324
  @__vc_content_evaluated = true
336
- return @__vc_content if @__vc_content != :unset
325
+ return @__vc_content if defined?(@__vc_content)
337
326
 
338
327
  @__vc_content =
339
328
  if __vc_render_in_block_provided?
340
329
  view_context.capture(self, &@__vc_render_in_block)
341
- elsif __vc_content_set_by_with_content?
330
+ elsif __vc_content_set_by_with_content_defined?
342
331
  @__vc_content_set_by_with_content
343
332
  end
344
333
  end
@@ -347,7 +336,11 @@ module ViewComponent
347
336
  #
348
337
  # @return [Boolean]
349
338
  def content?
350
- __vc_render_in_block_provided? || __vc_content_set_by_with_content?
339
+ __vc_render_in_block_provided? || __vc_content_set_by_with_content_defined?
340
+ end
341
+
342
+ def format
343
+ self.class.__vc_response_format
351
344
  end
352
345
 
353
346
  private
@@ -355,18 +348,14 @@ module ViewComponent
355
348
  attr_reader :view_context
356
349
 
357
350
  def __vc_render_in_block_provided?
358
- @view_context && @__vc_render_in_block
359
- end
360
-
361
- def __vc_content_set_by_with_content?
362
- !@__vc_content_set_by_with_content.nil?
351
+ defined?(@view_context) && @view_context && @__vc_render_in_block
363
352
  end
364
353
 
365
- def content_evaluated?
366
- @__vc_content_evaluated
354
+ def __vc_content_set_by_with_content_defined?
355
+ defined?(@__vc_content_set_by_with_content)
367
356
  end
368
357
 
369
- def maybe_escape_html(text)
358
+ def __vc_maybe_escape_html(text)
370
359
  return text if @current_template && !@current_template.html?
371
360
  return text if text.blank?
372
361
 
@@ -378,38 +367,18 @@ module ViewComponent
378
367
  end
379
368
  end
380
369
 
381
- def safe_output_preamble
382
- maybe_escape_html(output_preamble) do
370
+ def __vc_safe_output_preamble
371
+ __vc_maybe_escape_html(output_preamble) do
383
372
  Kernel.warn("WARNING: The #{self.class} component was provided an HTML-unsafe preamble. The preamble will be automatically escaped, but you may want to investigate.")
384
373
  end
385
374
  end
386
375
 
387
- def safe_output_postamble
388
- maybe_escape_html(output_postamble) do
376
+ def __vc_safe_output_postamble
377
+ __vc_maybe_escape_html(output_postamble) do
389
378
  Kernel.warn("WARNING: The #{self.class} component was provided an HTML-unsafe postamble. The postamble will be automatically escaped, but you may want to investigate.")
390
379
  end
391
380
  end
392
381
 
393
- # Set the controller used for testing components:
394
- #
395
- # ```ruby
396
- # config.view_component.test_controller = "MyTestController"
397
- # ```
398
- #
399
- # Defaults to `nil`. If this is falsy, `"ApplicationController"` is used. Can also be
400
- # configured on a per-test basis using `with_controller_class`.
401
- #
402
-
403
- # Parent class for generated components
404
- #
405
- # ```ruby
406
- # config.view_component.component_parent_class = "MyBaseComponent"
407
- # ```
408
- #
409
- # Defaults to nil. If this is falsy, generators will use
410
- # "ApplicationComponent" if defined, "ViewComponent::Base" otherwise.
411
- #
412
-
413
382
  # Configuration for generators.
414
383
  #
415
384
  # All options under this namespace default to `false` unless otherwise
@@ -467,6 +436,18 @@ module ViewComponent
467
436
  # ```
468
437
  #
469
438
  # Defaults to `false`.
439
+ #
440
+ # #### ßparent_class
441
+ #
442
+ # Parent class for generated components
443
+ #
444
+ # ```ruby
445
+ # config.view_component.generate.parent_class = "MyBaseComponent"
446
+ # ```
447
+ #
448
+ # Defaults to nil. If this is falsy, generators will use
449
+ # "ApplicationComponent" if defined, "ViewComponent::Base" otherwise.
450
+ #
470
451
 
471
452
  class << self
472
453
  # The file path of the component Ruby file.
@@ -535,6 +516,11 @@ module ViewComponent
535
516
  Collection.new(self, collection, spacer_component, **args)
536
517
  end
537
518
 
519
+ # @private
520
+ def __vc_compile(raise_errors: false, force: false)
521
+ __vc_compiler.compile(raise_errors: raise_errors, force: force)
522
+ end
523
+
538
524
  # @private
539
525
  def inherited(child)
540
526
  # Compile so child will inherit compiled `call_*` template methods that
@@ -557,12 +543,6 @@ module ViewComponent
557
543
  RUBY
558
544
  end
559
545
 
560
- # If Rails application is loaded, add application url_helpers to the component context
561
- # we need to check this to use this gem as a dependency
562
- if defined?(Rails) && Rails.application && !(child < Rails.application.routes.url_helpers)
563
- child.include Rails.application.routes.url_helpers
564
- end
565
-
566
546
  # Derive the source location of the component Ruby file from the call stack.
567
547
  # We need to ignore `inherited` frames here as they indicate that `inherited`
568
548
  # has been re-defined by the consuming application, likely in ApplicationComponent.
@@ -572,10 +552,10 @@ module ViewComponent
572
552
  child.virtual_path = child.name&.underscore
573
553
 
574
554
  # Set collection parameter to the extended component
575
- child.with_collection_parameter provided_collection_parameter
555
+ child.with_collection_parameter(__vc_provided_collection_parameter)
576
556
 
577
557
  if instance_methods(false).include?(:render_template_for)
578
- vc_ancestor_calls = (!@__vc_ancestor_calls.nil?) ? @__vc_ancestor_calls.dup : []
558
+ vc_ancestor_calls = defined?(@__vc_ancestor_calls) ? @__vc_ancestor_calls.dup : []
579
559
 
580
560
  vc_ancestor_calls.unshift(instance_method(:render_template_for))
581
561
  child.instance_variable_set(:@__vc_ancestor_calls, vc_ancestor_calls)
@@ -594,11 +574,6 @@ module ViewComponent
594
574
  __vc_compile unless __vc_compiled?
595
575
  end
596
576
 
597
- # @private
598
- def __vc_compile(raise_errors: false, force: false)
599
- __vc_compiler.compile(raise_errors: raise_errors, force: force)
600
- end
601
-
602
577
  # @private
603
578
  def __vc_compiler
604
579
  @__vc_compiler ||= Compiler.new(self)
@@ -612,8 +587,8 @@ module ViewComponent
612
587
  #
613
588
  # @param parameter [Symbol] The parameter name used when rendering elements of a collection.
614
589
  def with_collection_parameter(parameter)
615
- @provided_collection_parameter = parameter
616
- @initialize_parameters = nil
590
+ @__vc_provided_collection_parameter = parameter
591
+ @__vc_initialize_parameters = nil
617
592
  end
618
593
 
619
594
  # Strips trailing whitespace from templates before compiling them.
@@ -624,38 +599,16 @@ module ViewComponent
624
599
  # end
625
600
  # ```
626
601
  #
627
- # @deprecated Use the new component-local configuration option instead.
628
- #
629
- # ```ruby
630
- # class MyComponent < ViewComponent::Base
631
- # configure_view_component do |config|
632
- # config.strip_trailing_whitespace = true
633
- # end
634
- # end
635
- # ```
636
- #
637
602
  # @param value [Boolean] Whether to strip newlines.
638
603
  def strip_trailing_whitespace(value = true)
639
- ViewComponent::Deprecation.deprecation_warning(
640
- "strip_trailing_whitespace",
641
- <<~DOC
642
- Use the new component-local configuration option instead:
643
-
644
- class #{self.class.name} < ViewComponent::Base
645
- configure_view_component do |config|
646
- config.strip_trailing_whitespace = #{value}
647
- end
648
- end
649
- DOC
650
- )
651
- view_component_config.strip_trailing_whitespace = value
604
+ self.__vc_strip_trailing_whitespace = value
652
605
  end
653
606
 
654
607
  # Whether trailing whitespace will be stripped before compilation.
655
608
  #
656
609
  # @return [Boolean]
657
610
  def strip_trailing_whitespace?
658
- view_component_config.strip_trailing_whitespace
611
+ __vc_strip_trailing_whitespace
659
612
  end
660
613
 
661
614
  # Ensure the component initializer accepts the
@@ -665,17 +618,10 @@ module ViewComponent
665
618
  # rendering is optional.
666
619
  # @private
667
620
  def __vc_validate_collection_parameter!(validate_default: false)
668
- parameter = validate_default ? __vc_collection_parameter : provided_collection_parameter
621
+ parameter = validate_default ? __vc_collection_parameter : __vc_provided_collection_parameter
669
622
 
670
623
  return unless parameter
671
- return if initialize_parameter_names.include?(parameter) || splatted_keyword_argument_present?
672
-
673
- # If Ruby can't parse the component class, then the initialize
674
- # parameters will be empty and ViewComponent will not be able to render
675
- # the component.
676
- if initialize_parameters.empty?
677
- raise EmptyOrInvalidInitializerError.new(name, parameter)
678
- end
624
+ return if __vc_initialize_parameter_names.include?(parameter) || __vc_splatted_keyword_argument_present?
679
625
 
680
626
  raise MissingCollectionArgumentError.new(name, parameter)
681
627
  end
@@ -685,55 +631,57 @@ module ViewComponent
685
631
  # methods.
686
632
  # @private
687
633
  def __vc_validate_initialization_parameters!
688
- return unless initialize_parameter_names.include?(:content)
634
+ return unless __vc_initialize_parameter_names.include?(:content)
689
635
 
690
636
  raise ReservedParameterError.new(name, :content)
691
637
  end
692
638
 
693
639
  # @private
694
640
  def __vc_collection_parameter
695
- provided_collection_parameter || name && name.demodulize.underscore.chomp("_component").to_sym
641
+ @__vc_provided_collection_parameter ||= name && name.demodulize.underscore.chomp("_component").to_sym
696
642
  end
697
643
 
698
644
  # @private
699
645
  def __vc_collection_counter_parameter
700
- :"#{__vc_collection_parameter}_counter"
646
+ @__vc_collection_counter_parameter ||= :"#{__vc_collection_parameter}_counter"
701
647
  end
702
648
 
703
649
  # @private
704
650
  def __vc_counter_argument_present?
705
- initialize_parameter_names.include?(__vc_collection_counter_parameter)
651
+ __vc_initialize_parameter_names.include?(__vc_collection_counter_parameter)
706
652
  end
707
653
 
708
654
  # @private
709
655
  def __vc_collection_iteration_parameter
710
- :"#{__vc_collection_parameter}_iteration"
656
+ @__vc_collection_iteration_parameter ||= :"#{__vc_collection_parameter}_iteration"
711
657
  end
712
658
 
713
659
  # @private
714
660
  def __vc_iteration_argument_present?
715
- initialize_parameter_names.include?(__vc_collection_iteration_parameter)
661
+ __vc_initialize_parameter_names.include?(__vc_collection_iteration_parameter)
716
662
  end
717
663
 
718
664
  private
719
665
 
720
- def splatted_keyword_argument_present?
721
- initialize_parameters.flatten.include?(:keyrest) &&
722
- !initialize_parameters.include?([:keyrest, :**]) # Un-named splatted keyword args don't count!
666
+ def __vc_splatted_keyword_argument_present?
667
+ __vc_initialize_parameters.flatten.include?(:keyrest)
723
668
  end
724
669
 
725
- def initialize_parameter_names
726
- return attribute_names.map(&:to_sym) if respond_to?(:attribute_names)
727
-
728
- initialize_parameters.map(&:last)
670
+ def __vc_initialize_parameter_names
671
+ @__vc_initialize_parameter_names ||=
672
+ if respond_to?(:attribute_names)
673
+ attribute_names.map(&:to_sym)
674
+ else
675
+ __vc_initialize_parameters.map(&:last)
676
+ end
729
677
  end
730
678
 
731
- def initialize_parameters
732
- @initialize_parameters ||= instance_method(:initialize).parameters
679
+ def __vc_initialize_parameters
680
+ @__vc_initialize_parameters ||= instance_method(:initialize).parameters
733
681
  end
734
682
 
735
- def provided_collection_parameter
736
- @provided_collection_parameter ||= nil
683
+ def __vc_provided_collection_parameter
684
+ @__vc_provided_collection_parameter ||= nil
737
685
  end
738
686
  end
739
687
 
@@ -48,7 +48,16 @@ module ViewComponent
48
48
 
49
49
  define_render_template_for
50
50
 
51
- @component.register_default_slots
51
+ # Set the format if the component only responds to a single format.
52
+ # Unfortunately we cannot determine which format a multi-format
53
+ # component will respond to until render time, so those components
54
+ # will not set the response format.
55
+ #
56
+ # TODO: Investigate upstream changes necessary to support multi-format renderables
57
+ unique_formats = templates.map(&:format).uniq
58
+ @component.__vc_response_format = unique_formats.last if unique_formats.one?
59
+
60
+ @component.__vc_register_default_slots
52
61
  @component.__vc_build_i18n_backend
53
62
 
54
63
  CompileCache.register(@component)
@@ -108,10 +117,8 @@ module ViewComponent
108
117
 
109
118
  errors << "Couldn't find a template file or inline render method for #{@component}." if @templates.empty?
110
119
 
111
- # We currently allow components to have both an inline call method and a template for a variant, with the
112
- # inline call method overriding the template. We should aim to change this in v4 to instead
113
- # raise an error.
114
- @templates.reject(&:inline_call?)
120
+ @templates
121
+ .reject { |template| template.inline_call? && !template.defined_on_self? }
115
122
  .map { |template| [template.variant, template.format] }
116
123
  .tally
117
124
  .select { |_, count| count > 1 }
@@ -170,10 +177,10 @@ module ViewComponent
170
177
 
171
178
  def gather_templates
172
179
  @templates ||=
173
- if @component.inline_template.present?
180
+ if @component.__vc_inline_template.present?
174
181
  [Template::Inline.new(
175
182
  component: @component,
176
- inline_template: @component.inline_template
183
+ inline_template: @component.__vc_inline_template
177
184
  )]
178
185
  else
179
186
  path_parser = ActionView::Resolver::PathParser.new