view_component 3.20.0 → 3.22.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 84bc019145d592e5a7f4dbb439cc960497ac347e12c2a6f73d7544a777365522
4
- data.tar.gz: 2832c147430779d124335b8e82c915115b5b6840ab02365df551f5313621e85c
3
+ metadata.gz: b6afc3fe994c0954654d58d797e36d274013494e6caec61ef1b5535a4a5a4267
4
+ data.tar.gz: ae921110a568f5e212b0c2d60cf31bf4ac7628b961a65cde871577f13fddee60
5
5
  SHA512:
6
- metadata.gz: ccbe1677fec16d8de063aafe220ada3ee0647d9963d91f269ef3081b9b2edffc4bf2f535c2097a730d72e80521a9367e97353927997f353f3ec59923e2611deb
7
- data.tar.gz: d52f357464701228bc686f4ea367974a287ce4fcd895845814f9a175635750664b705411c096f3f01c0bad0914a1db95573f50d12ff1b0e7d9029a55adf492b8
6
+ metadata.gz: 240679c690a13c69669173be5c076271e1a2f0f02dfabfc2bdbfa49c0b03cbd7456e88204c84be3e7b85c703c606270b3c794f47f845d3062c6a9f620fda9da2
7
+ data.tar.gz: da6b4298a67311608e1641c5ace24e321e6270fb67921f2bb6f70cce9dedb7f53b2ea8006acb193775ce4d4f6a963338f310d754bc95aac6c793a9b52abddf62
@@ -14,7 +14,14 @@ module ViewComponent
14
14
 
15
15
  # Including helpers here ensures that we're loading the
16
16
  # latest version of helpers if code-reloading is enabled
17
- helper :all if include_all_helpers
17
+ if include_all_helpers
18
+ helper :all
19
+ else
20
+ # :nocov:
21
+ # Always provide the #view_source helper
22
+ helper PreviewHelper
23
+ # :nocov:
24
+ end
18
25
  end
19
26
 
20
27
  def index
@@ -22,7 +22,7 @@ module PreviewHelper
22
22
  serve_static_preview_assets? ? asset_path("prism.min.js", skip_pipeline: true) : "https://cdn.jsdelivr.net/npm/prismjs@1.28.0/prism.min.js"
23
23
  end
24
24
 
25
- def find_template_data(lookup_context:, template_identifier:)
25
+ def find_template_data_for_preview_source(lookup_context:, template_identifier:)
26
26
  template = lookup_context.find_template(template_identifier)
27
27
 
28
28
  if Rails.version.to_f >= 6.1 || template.source.present?
@@ -7,7 +7,7 @@
7
7
  <%= h @preview.preview_source(@example_name) %>
8
8
  </code>
9
9
  <% else %>
10
- <% template_data = find_template_data(lookup_context: @view_renderer.lookup_context, template_identifier: @render_args[:template]) %>
10
+ <% template_data = find_template_data_for_preview_source(lookup_context: @view_renderer.lookup_context, template_identifier: @render_args[:template]) %>
11
11
  <code class="language-<%= template_data[:prism_language_name] %>">
12
12
  <%= h template_data[:source] %>
13
13
  </code>
data/docs/CHANGELOG.md CHANGED
@@ -10,6 +10,76 @@ nav_order: 5
10
10
 
11
11
  ## main
12
12
 
13
+ ## 3.22.0
14
+
15
+ * Rewrite `ViewComponents at GitHub` documentation as more general `Best practices`.
16
+
17
+ *Phil Schalm*, *Joel Hawksley*
18
+
19
+ * Add unused mechanism for inheriting config from parent modules to enable future engine-local configuration.
20
+
21
+ *Simon Fish*
22
+
23
+ * Improve handling of malformed component edge case when mocking components in tests.
24
+
25
+ *Martin Meyerhoff*, *Joel Hawksley*
26
+
27
+ * Add Content Harmony & Learn To Be to list of companies using ViewComponent.
28
+
29
+ *Kane Jamison*
30
+
31
+ * Clarify error message about render-dependent logic.
32
+
33
+ Error messages about render-dependent logic were sometimes inaccurate, saying `during initialization` despite also being raised after a component had been initialized but before it was rendered.
34
+
35
+ *Joel Hawksley*
36
+
37
+ * Remove JS and CSS docs as they proved difficult to maintain and lacked consensus.
38
+
39
+ *Joel Hawksley*
40
+
41
+ * Do not prefix release tags with `v`, per recommendation from @bkuhlmann.
42
+
43
+ *Joel Hawksley*
44
+
45
+ * Add ruby 3.4 support to CI.
46
+
47
+ *Reegan Viljoen*
48
+
49
+ * Add HomeStyler AI to list of companies using ViewComponent.
50
+
51
+ *JP Balarini*
52
+
53
+ ## 3.21.0
54
+
55
+ * Updates testing docs to include an example of how to use with RSpec.
56
+
57
+ *Rylan Bowers*
58
+
59
+ * Add `--skip-suffix` option to component generator.
60
+
61
+ *KAWAKAMI Moeki*
62
+
63
+ * Add FreeATS to list of companies using ViewComponent.
64
+
65
+ *Ilia Liamshin*
66
+
67
+ * Ensure HTML output safety wrapper is used for all inline templates.
68
+
69
+ *Joel Hawksley*
70
+
71
+ * Expose `.identifier` method as part of public API.
72
+
73
+ *Joel Hawksley*
74
+
75
+ * Add rails 8 support to CI.
76
+
77
+ *Reegan Viljoen*
78
+
79
+ * Updates ActionText compatibility documentation to reference `rich_textarea_tag` for Rails 8.0 support.
80
+
81
+ *Alvin Crespo*
82
+
13
83
  ## 3.20.0
14
84
 
15
85
  * Allow rendering `with_collection` to accept an optional `spacer_component` to be rendered between each item.
@@ -188,6 +258,10 @@ nav_order: 5
188
258
 
189
259
  *Reegan Viljoen*
190
260
 
261
+ * Fix a bug where component previews would crash with "undefined local variable or method `preview_source`."
262
+
263
+ *Henning Koch*
264
+
191
265
  ## 3.12.1
192
266
 
193
267
  * Ensure content is rendered correctly for forwarded slots.
@@ -19,9 +19,10 @@ module Rails
19
19
  class_option :sidecar, type: :boolean, default: false
20
20
  class_option :stimulus, type: :boolean,
21
21
  default: ViewComponent::Base.config.generate.stimulus_controller
22
+ class_option :skip_suffix, type: :boolean, default: false
22
23
 
23
24
  def create_component_file
24
- template "component.rb", File.join(component_path, class_path, "#{file_name}_component.rb")
25
+ template "component.rb", File.join(component_path, class_path, "#{file_name}#{options[:skip_suffix] ? "" : "_component"}.rb")
25
26
  end
26
27
 
27
28
  hook_for :test_framework
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  <% module_namespacing do -%>
4
- class <%= class_name %>Component < <%= parent_class %>
4
+ class <%= class_name %><%= options[:skip_suffix] ? "" : "Component" %> < <%= parent_class %>
5
5
  <%- if initialize_signature -%>
6
6
  def initialize(<%= initialize_signature %>)
7
7
  <%= initialize_body %>
@@ -25,6 +25,10 @@ module ViewComponent
25
25
  #
26
26
  # @return [ActiveSupport::OrderedOptions]
27
27
  def config
28
+ module_parents.each do |m|
29
+ config = m.try(:config).try(:view_component)
30
+ return config if config
31
+ end
28
32
  ViewComponent::Config.current
29
33
  end
30
34
  end
@@ -108,14 +112,7 @@ module ViewComponent
108
112
  before_render
109
113
 
110
114
  if render?
111
- rendered_template =
112
- if compiler.renders_template_for?(@__vc_variant, __vc_request&.format&.to_sym)
113
- render_template_for(@__vc_variant, __vc_request&.format&.to_sym)
114
- else
115
- maybe_escape_html(render_template_for(@__vc_variant, __vc_request&.format&.to_sym)) do
116
- Kernel.warn("WARNING: The #{self.class} component rendered HTML-unsafe output. The output will be automatically escaped, but you may want to investigate.")
117
- end
118
- end.to_s
115
+ rendered_template = render_template_for(@__vc_variant, __vc_request&.format&.to_sym).to_s
119
116
 
120
117
  # Avoid allocating new string when output_preamble and output_postamble are blank
121
118
  if output_preamble.blank? && output_postamble.blank?
@@ -358,10 +355,6 @@ module ViewComponent
358
355
  end
359
356
  end
360
357
 
361
- def compiler
362
- @compiler ||= self.class.compiler
363
- end
364
-
365
358
  # Set the controller used for testing components:
366
359
  #
367
360
  # ```ruby
@@ -457,8 +450,16 @@ module ViewComponent
457
450
  # Defaults to `false`.
458
451
 
459
452
  class << self
453
+ # The file path of the component Ruby file.
454
+ #
455
+ # @return [String]
456
+ attr_reader :identifier
457
+
460
458
  # @private
461
- attr_accessor :source_location, :virtual_path
459
+ attr_writer :identifier
460
+
461
+ # @private
462
+ attr_accessor :virtual_path
462
463
 
463
464
  # Find sidecar files for the given extensions.
464
465
  #
@@ -468,13 +469,13 @@ module ViewComponent
468
469
  # For example, one might collect sidecar CSS files that need to be compiled.
469
470
  # @param extensions [Array<String>] Extensions of which to return matching sidecar files.
470
471
  def sidecar_files(extensions)
471
- return [] unless source_location
472
+ return [] unless identifier
472
473
 
473
474
  extensions = extensions.join(",")
474
475
 
475
476
  # view files in a directory named like the component
476
- directory = File.dirname(source_location)
477
- filename = File.basename(source_location, ".rb")
477
+ directory = File.dirname(identifier)
478
+ filename = File.basename(identifier, ".rb")
478
479
  component_name = name.demodulize.underscore
479
480
 
480
481
  # Add support for nested components defined in the same file.
@@ -499,7 +500,7 @@ module ViewComponent
499
500
 
500
501
  sidecar_directory_files = Dir["#{directory}/#{component_name}/#{filename}.*{#{extensions}}"]
501
502
 
502
- (sidecar_files - [source_location] + sidecar_directory_files + nested_component_files).uniq
503
+ (sidecar_files - [identifier] + sidecar_directory_files + nested_component_files).uniq
503
504
  end
504
505
 
505
506
  # Render a component for each element in a collection ([documentation](/guide/collections)):
@@ -548,11 +549,11 @@ module ViewComponent
548
549
  # has been re-defined by the consuming application, likely in ApplicationComponent.
549
550
  # We use `base_label` method here instead of `label` to avoid cases where the method
550
551
  # owner is included in a prefix like `ApplicationComponent.inherited`.
551
- child.source_location = caller_locations(1, 10).reject { |l| l.base_label == "inherited" }[0].path
552
+ child.identifier = caller_locations(1, 10).reject { |l| l.base_label == "inherited" }[0].path
552
553
 
553
554
  # If Rails application is loaded, removes the first part of the path and the extension.
554
555
  if defined?(Rails) && Rails.application
555
- child.virtual_path = child.source_location.gsub(
556
+ child.virtual_path = child.identifier.gsub(
556
557
  /(.*#{Regexp.quote(ViewComponent::Base.config.view_component_path)})|(\.rb)/, ""
557
558
  )
558
559
  end
@@ -590,15 +591,6 @@ module ViewComponent
590
591
  @__vc_compiler ||= Compiler.new(self)
591
592
  end
592
593
 
593
- # @private
594
- def identifier
595
- # :nocov:
596
- Kernel.warn("WARNING: The #{self.class}.identifier is undocumented and was meant for internal framework usage only. As it is no longer used by the framework it will be removed in a coming non-breaking ViewComponent release.")
597
-
598
- source_location
599
- # :nocov:
600
- end
601
-
602
594
  # Set the parameter name used when rendering elements of a collection ([documentation](/guide/collections)):
603
595
  #
604
596
  # ```ruby
@@ -636,7 +628,7 @@ module ViewComponent
636
628
  # validate that the default parameter name
637
629
  # is accepted, as support for collection
638
630
  # rendering is optional.
639
- # @private TODO: add documentation
631
+ # @private
640
632
  def validate_collection_parameter!(validate_default: false)
641
633
  parameter = validate_default ? collection_parameter : provided_collection_parameter
642
634
 
@@ -656,7 +648,7 @@ module ViewComponent
656
648
  # Ensure the component initializer doesn't define
657
649
  # invalid parameters that could override the framework's
658
650
  # methods.
659
- # @private TODO: add documentation
651
+ # @private
660
652
  def validate_initialization_parameters!
661
653
  return unless initialize_parameter_names.include?(RESERVED_PARAMETER)
662
654
 
@@ -13,7 +13,6 @@ module ViewComponent
13
13
  def initialize(component)
14
14
  @component = component
15
15
  @lock = Mutex.new
16
- @rendered_templates = Set.new
17
16
  end
18
17
 
19
18
  def compiled?
@@ -56,10 +55,6 @@ module ViewComponent
56
55
  end
57
56
  end
58
57
 
59
- def renders_template_for?(variant, format)
60
- @rendered_templates.include?([variant, format])
61
- end
62
-
63
58
  private
64
59
 
65
60
  attr_reader :templates
@@ -71,9 +66,9 @@ module ViewComponent
71
66
 
72
67
  method_body =
73
68
  if @templates.one?
74
- @templates.first.safe_method_name
69
+ @templates.first.safe_method_name_call
75
70
  elsif (template = @templates.find(&:inline?))
76
- template.safe_method_name
71
+ template.safe_method_name_call
77
72
  else
78
73
  branches = []
79
74
 
@@ -88,13 +83,13 @@ module ViewComponent
88
83
  ].join(" && ")
89
84
  end
90
85
 
91
- branches << [conditional, template.safe_method_name]
86
+ branches << [conditional, template.safe_method_name_call]
92
87
  end
93
88
 
94
89
  out = branches.each_with_object(+"") do |(conditional, branch_body), memo|
95
90
  memo << "#{(!memo.present?) ? "if" : "elsif"} #{conditional}\n #{branch_body}\n"
96
91
  end
97
- out << "else\n #{templates.find { _1.variant.nil? && _1.default_format? }.safe_method_name}\nend"
92
+ out << "else\n #{templates.find { _1.variant.nil? && _1.default_format? }.safe_method_name_call}\nend"
98
93
  end
99
94
 
100
95
  @component.silence_redefinition_of_method(:render_template_for)
@@ -196,10 +191,6 @@ module ViewComponent
196
191
  variant: variant
197
192
  )
198
193
 
199
- # TODO: We should consider inlining the HTML output safety logic into the compiled render_template_for
200
- # instead of this indirect approach
201
- @rendered_templates << [out.variant, out.this_format]
202
-
203
194
  out
204
195
  end
205
196
 
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module Configurable
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ next if respond_to?(:config) && config.respond_to?(:view_component) && config.respond_to_missing?(:test_controller)
9
+
10
+ include ActiveSupport::Configurable
11
+
12
+ configure do |config|
13
+ config.view_component ||= ActiveSupport::InheritableOptions.new
14
+ end
15
+ end
16
+ end
17
+ end
@@ -177,7 +177,7 @@ module ViewComponent
177
177
 
178
178
  class TranslateCalledBeforeRenderError < BaseError
179
179
  MESSAGE =
180
- "`#translate` can't be used during initialization as it depends " \
180
+ "`#translate` can't be used before rendering as it depends " \
181
181
  "on the view context that only exists once a ViewComponent is passed to " \
182
182
  "the Rails render pipeline.\n\n" \
183
183
  "It's sometimes possible to fix this issue by moving code dependent on " \
@@ -186,7 +186,7 @@ module ViewComponent
186
186
 
187
187
  class HelpersCalledBeforeRenderError < BaseError
188
188
  MESSAGE =
189
- "`#helpers` can't be used during initialization as it depends " \
189
+ "`#helpers` can't be used before rendering as it depends " \
190
190
  "on the view context that only exists once a ViewComponent is passed to " \
191
191
  "the Rails render pipeline.\n\n" \
192
192
  "It's sometimes possible to fix this issue by moving code dependent on " \
@@ -195,7 +195,7 @@ module ViewComponent
195
195
 
196
196
  class ControllerCalledBeforeRenderError < BaseError
197
197
  MESSAGE =
198
- "`#controller` can't be used during initialization, as it depends " \
198
+ "`#controller` can't be used before rendering, as it depends " \
199
199
  "on the view context that only exists once a ViewComponent is passed to " \
200
200
  "the Rails render pipeline.\n\n" \
201
201
  "It's sometimes possible to fix this issue by moving code dependent on " \
@@ -13,7 +13,7 @@ module ViewComponent # :nodoc:
13
13
  notification_name,
14
14
  {
15
15
  name: self.class.name,
16
- identifier: self.class.source_location
16
+ identifier: self.class.identifier
17
17
  }
18
18
  ) do
19
19
  super
@@ -59,6 +59,14 @@ module ViewComponent
59
59
  @component.define_method(safe_method_name, @component.instance_method(@call_method_name))
60
60
  end
61
61
 
62
+ def safe_method_name_call
63
+ return safe_method_name unless inline_call?
64
+
65
+ "maybe_escape_html(#{safe_method_name}) " \
66
+ "{ Kernel.warn(\"WARNING: The #{@component} component rendered HTML-unsafe output. " \
67
+ "The output will be automatically escaped, but you may want to investigate.\") } "
68
+ end
69
+
62
70
  def requires_compiled_superclass?
63
71
  inline_call? && !defined_on_self?
64
72
  end
@@ -3,7 +3,7 @@
3
3
  module ViewComponent
4
4
  module VERSION
5
5
  MAJOR = 3
6
- MINOR = 20
6
+ MINOR = 22
7
7
  PATCH = 0
8
8
  PRE = nil
9
9
 
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: view_component
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.20.0
4
+ version: 3.22.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ViewComponent Team
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-10-31 00:00:00.000000000 Z
10
+ date: 2025-03-25 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: activesupport
@@ -48,16 +47,16 @@ dependencies:
48
47
  name: concurrent-ruby
49
48
  requirement: !ruby/object:Gem::Requirement
50
49
  requirements:
51
- - - "~>"
50
+ - - '='
52
51
  - !ruby/object:Gem::Version
53
- version: '1.0'
52
+ version: 1.3.4
54
53
  type: :runtime
55
54
  prerelease: false
56
55
  version_requirements: !ruby/object:Gem::Requirement
57
56
  requirements:
58
- - - "~>"
57
+ - - '='
59
58
  - !ruby/object:Gem::Version
60
- version: '1.0'
59
+ version: 1.3.4
61
60
  - !ruby/object:Gem::Dependency
62
61
  name: allocation_stats
63
62
  requirement: !ruby/object:Gem::Requirement
@@ -324,6 +323,20 @@ dependencies:
324
323
  - - '='
325
324
  - !ruby/object:Gem::Version
326
325
  version: 4.9.0
326
+ - !ruby/object:Gem::Dependency
327
+ name: sprockets-rails
328
+ requirement: !ruby/object:Gem::Requirement
329
+ requirements:
330
+ - - "~>"
331
+ - !ruby/object:Gem::Version
332
+ version: 3.4.2
333
+ type: :development
334
+ prerelease: false
335
+ version_requirements: !ruby/object:Gem::Requirement
336
+ requirements:
337
+ - - "~>"
338
+ - !ruby/object:Gem::Version
339
+ version: 3.4.2
327
340
  - !ruby/object:Gem::Dependency
328
341
  name: standard
329
342
  requirement: !ruby/object:Gem::Requirement
@@ -380,20 +393,6 @@ dependencies:
380
393
  - - "~>"
381
394
  - !ruby/object:Gem::Version
382
395
  version: '5.1'
383
- - !ruby/object:Gem::Dependency
384
- name: sprockets-rails
385
- requirement: !ruby/object:Gem::Requirement
386
- requirements:
387
- - - "~>"
388
- - !ruby/object:Gem::Version
389
- version: 3.4.2
390
- type: :development
391
- prerelease: false
392
- version_requirements: !ruby/object:Gem::Requirement
393
- requirements:
394
- - - "~>"
395
- - !ruby/object:Gem::Version
396
- version: 3.4.2
397
396
  - !ruby/object:Gem::Dependency
398
397
  name: turbo-rails
399
398
  requirement: !ruby/object:Gem::Requirement
@@ -548,8 +547,20 @@ dependencies:
548
547
  - - ">="
549
548
  - !ruby/object:Gem::Version
550
549
  version: '0'
551
- description:
552
- email:
550
+ - !ruby/object:Gem::Dependency
551
+ name: propshaft
552
+ requirement: !ruby/object:Gem::Requirement
553
+ requirements:
554
+ - - "~>"
555
+ - !ruby/object:Gem::Version
556
+ version: 1.1.0
557
+ type: :development
558
+ prerelease: false
559
+ version_requirements: !ruby/object:Gem::Requirement
560
+ requirements:
561
+ - - "~>"
562
+ - !ruby/object:Gem::Version
563
+ version: 1.1.0
553
564
  executables: []
554
565
  extensions: []
555
566
  extra_rdoc_files: []
@@ -598,6 +609,7 @@ files:
598
609
  - lib/view_component/compiler.rb
599
610
  - lib/view_component/component_error.rb
600
611
  - lib/view_component/config.rb
612
+ - lib/view_component/configurable.rb
601
613
  - lib/view_component/deprecation.rb
602
614
  - lib/view_component/docs_builder_component.html.erb
603
615
  - lib/view_component/docs_builder_component.rb
@@ -633,7 +645,6 @@ metadata:
633
645
  allowed_push_host: https://rubygems.org
634
646
  source_code_uri: https://github.com/viewcomponent/view_component
635
647
  changelog_uri: https://github.com/ViewComponent/view_component/blob/main/docs/CHANGELOG.md
636
- post_install_message:
637
648
  rdoc_options: []
638
649
  require_paths:
639
650
  - lib
@@ -648,8 +659,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
648
659
  - !ruby/object:Gem::Version
649
660
  version: '0'
650
661
  requirements: []
651
- rubygems_version: 3.5.3
652
- signing_key:
662
+ rubygems_version: 3.6.2
653
663
  specification_version: 4
654
664
  summary: A framework for building reusable, testable & encapsulated view components
655
665
  in Ruby on Rails.