view_component 4.20.0 → 4.21.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.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/docs/CHANGELOG.md +94 -0
  3. data/lib/generators/view_component/abstract_generator.rb +24 -4
  4. data/lib/generators/view_component/component/component_generator.rb +8 -6
  5. data/lib/generators/view_component/component/templates/component.rb.tt +1 -1
  6. data/lib/generators/view_component/erb/erb_generator.rb +2 -2
  7. data/lib/generators/view_component/haml/haml_generator.rb +1 -1
  8. data/lib/generators/view_component/locale/locale_generator.rb +2 -6
  9. data/lib/generators/view_component/preview/preview_generator.rb +9 -6
  10. data/lib/generators/view_component/preview/templates/component_preview.rb.tt +2 -2
  11. data/lib/generators/view_component/rspec/rspec_generator.rb +1 -1
  12. data/lib/generators/view_component/rspec/templates/component_spec.rb.tt +1 -1
  13. data/lib/generators/view_component/slim/slim_generator.rb +1 -1
  14. data/lib/generators/view_component/stimulus/stimulus_generator.rb +3 -7
  15. data/lib/generators/view_component/test_unit/templates/component_test.rb.tt +2 -2
  16. data/lib/generators/view_component/test_unit/test_unit_generator.rb +8 -7
  17. data/lib/view_component/base.rb +106 -98
  18. data/lib/view_component/cache_digest/dependency_tracking.rb +6 -6
  19. data/lib/view_component/cache_digest/digestor_lookup.rb +3 -1
  20. data/lib/view_component/cache_digest/resolver.rb +59 -38
  21. data/lib/view_component/cache_digest.rb +161 -53
  22. data/lib/view_component/cacheable.rb +16 -18
  23. data/lib/view_component/collection.rb +4 -9
  24. data/lib/view_component/compile_cache.rb +44 -5
  25. data/lib/view_component/compiler.rb +50 -2
  26. data/lib/view_component/config.rb +114 -8
  27. data/lib/view_component/engine.rb +1 -2
  28. data/lib/view_component/errors.rb +16 -0
  29. data/lib/view_component/slot.rb +12 -0
  30. data/lib/view_component/slotable.rb +28 -4
  31. data/lib/view_component/template.rb +16 -3
  32. data/lib/view_component/test_helpers.rb +38 -16
  33. data/lib/view_component/translatable.rb +34 -0
  34. data/lib/view_component/version.rb +1 -1
  35. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 04ae333990353bff3c4671c78db517fbe53ddcb15d9bfeb4448bc2d52c710f5b
4
- data.tar.gz: ef958403f0326485a5f2035a9d744c878197c041247df3f76e68cf2712b9a652
3
+ metadata.gz: 829de474217e06e2148d92de68ef3d93dd8a31e314b40073fc292440508731b8
4
+ data.tar.gz: 1dfb4a41b58c47814b115b72e6cfc736e3239886376632e7177cadfa024c8582
5
5
  SHA512:
6
- metadata.gz: 68e6ae32b57a9c1323ed64eb82c866e9595f2aeca16009b532f4922e85abe72536b06dc61d75328a202faef438f57e4d9deebef8ace06c96a3c7d609ad946a38
7
- data.tar.gz: 52ef898174f4270dbc4bdcc91c0d841d35fe12c04c8b46e0639b0b1cb1a43b2789d24174cf70f2bc4ee3718eae536448b24833247cdd9a0f891c33678186060f
6
+ metadata.gz: 644364f5c6e4375b130074078de4e692dfdc4fca34ceca1bfedd307a135cfe9bd8ed81b111f08e1222af4d334afcb81fee48ee50a9b92bc21df591f783230314
7
+ data.tar.gz: d7902711e70321bc27f0b4b9bbf5dbd7f75864444dcaacf4bdfc762c765c87f688463b9f65edcf01656aea723fc82baf421edcf6f35989ce60c9875ebb91c699
data/docs/CHANGELOG.md CHANGED
@@ -10,6 +10,100 @@ nav_order: 6
10
10
 
11
11
  ## main
12
12
 
13
+ ## 4.21.0
14
+
15
+ * Avoid `NameError` during eager loading when components include modules with temporary names.
16
+
17
+ *Joel Hawksley*
18
+
19
+ * Avoid repeated scanning of unterminated ERB tags during cache dependency discovery.
20
+
21
+ *Joel Hawksley*
22
+
23
+ * Skip dependency compilation for static ERB markup and literal templates while preserving helper-rendered component tracking.
24
+
25
+ *Joel Hawksley*
26
+
27
+ * Track helper-rendered component cache dependencies, enforce single-use instances on cache hits, and emit one render notification per self-cached render.
28
+
29
+ *Joel Hawksley*
30
+
31
+ * Resolve symbolic translation defaults in sidecars, restore request URL state in tests, and refresh inherited templates whenever template reloading is enabled.
32
+
33
+ *Joel Hawksley*
34
+
35
+ * Reuse existing GitHub releases when retrying a failed gem publish.
36
+
37
+ *Joel Hawksley*
38
+
39
+ * Prevent self-cache key collisions and track component references in views and format-specific partial dependencies.
40
+
41
+ *Joel Hawksley*
42
+
43
+ * Isolate preview and request parameters, honor preview locale callbacks, restore controller view contexts, and accept nil translation keys.
44
+
45
+ *Joel Hawksley*
46
+
47
+ * Honor negative generator flags and preserve pre-release versions throughout release automation. Report gem publishing failures.
48
+
49
+ *Joel Hawksley*
50
+
51
+ * Fix fragment cache collisions after `render_parent`, missing relative-partial and inherited-template dependencies, format fallback cache collisions, and stale cached output after development template edits.
52
+
53
+ *Joel Hawksley*
54
+
55
+ * Fix template method collisions between formats and variants, preserve default slot state, and copy collection spacer slots independently.
56
+
57
+ *Joel Hawksley*
58
+
59
+ * Apply `--skip-suffix` to all generated component files and restore full request URLs after `with_request_url` blocks.
60
+
61
+ *Joel Hawksley*
62
+
63
+ * Reset the assertion page when rendering a preview so assertions inspect the latest output.
64
+
65
+ *Joel Hawksley*
66
+
67
+ * Rediscover templates after development changes, including added or removed variants and parent templates.
68
+
69
+ *Joel Hawksley*
70
+
71
+ * Include all requested variants in output cache keys, preserving fallback order.
72
+
73
+ *Joel Hawksley*
74
+
75
+ * Resolve cache dependencies in their Ruby lexical namespaces and component template scopes.
76
+
77
+ *Joel Hawksley*
78
+
79
+ * Keep default slot configuration isolated between parent components and subclasses.
80
+
81
+ *Joel Hawksley*
82
+
83
+ * Preserve the calling view's translation scope when a component returns cached output or rejects content.
84
+
85
+ *Joel Hawksley*
86
+
87
+ * Reduce rendering and collection overhead, and speed up eager loading by sharing template-path parsing and avoiding redundant template and ancestor checks.
88
+
89
+ *Joel Hawksley*
90
+
91
+ * Speed up eager loading by sharing source-file reads between template compilation and cache digest precomputation, and reduce nested-render and default-slot lookup overhead.
92
+
93
+ *Joel Hawksley*
94
+
95
+ * Speed up eager loading by sharing sidecar directory listings during compilation, and reduce per-render state reset overhead.
96
+
97
+ *Joel Hawksley*
98
+
99
+ * Ignore unresolved partial dependencies inferred from dynamic renders, such as `render UI.button`, without requiring configuration for component facades. Real partials and explicit dependencies are preserved.
100
+
101
+ *Joel Hawksley*
102
+
103
+ * Raise `ViewComponent::UnknownConfigOptionError` when setting a `config.view_component` option that doesn't exist, rather than ignoring it. This includes options renamed or removed in 4.0, such as `preview_controller` (now `previews.controller`), and the error names the replacement. Replacing `previews` or `generate` with a hash preserves this validation, including through Hash mutation methods such as `merge!`, `update`, `store`, and `replace`.
104
+
105
+ *Fiona Burcombe*
106
+
13
107
  ## 4.20.0
14
108
 
15
109
  * Fix `invalid byte sequence in UTF-8` errors when precomputing cache digests for components loaded from encrypted or binary source files.
@@ -2,6 +2,22 @@
2
2
 
3
3
  module ViewComponent
4
4
  module AbstractGenerator
5
+ def self.included(generator)
6
+ generator.class_option :skip_suffix, type: :boolean, default: false
7
+ end
8
+
9
+ def initialize(...)
10
+ super
11
+ generate = ViewComponent::Base.config.generate
12
+ defaults = {
13
+ "sidecar" => generate.sidecar,
14
+ "stimulus" => generate.stimulus_controller,
15
+ "typescript" => generate.typescript
16
+ }.slice(*self.class.class_options.keys.map(&:to_s))
17
+ # Resolve defaults once so hooks inherit explicit false values as well.
18
+ self.options = options.class.new(defaults.merge(options)).freeze
19
+ end
20
+
5
21
  def copy_view_file
6
22
  template("component.html.#{engine_name}", destination) unless options["inline"] || options["call"]
7
23
  end
@@ -21,7 +37,11 @@ module ViewComponent
21
37
  end
22
38
 
23
39
  def destination_file_name
24
- "#{file_name}_component"
40
+ "#{file_name}#{"_component" unless options[:skip_suffix]}"
41
+ end
42
+
43
+ def component_class_name
44
+ "#{class_name}#{"Component" unless options[:skip_suffix]}"
25
45
  end
26
46
 
27
47
  def file_name
@@ -42,15 +62,15 @@ module ViewComponent
42
62
  end
43
63
 
44
64
  def sidecar?
45
- options["sidecar"] || ViewComponent::Base.config.generate.sidecar
65
+ options["sidecar"]
46
66
  end
47
67
 
48
68
  def stimulus?
49
- options["stimulus"] || ViewComponent::Base.config.generate.stimulus_controller
69
+ options["stimulus"]
50
70
  end
51
71
 
52
72
  def typescript?
53
- options["typescript"] || ViewComponent::Base.config.generate.typescript
73
+ options["typescript"]
54
74
  end
55
75
  end
56
76
  end
@@ -10,20 +10,22 @@ module ViewComponent
10
10
  source_root File.expand_path("templates", __dir__)
11
11
 
12
12
  argument :attributes, type: :array, default: [], banner: "attribute"
13
- check_class_collision suffix: "Component"
14
13
 
15
14
  class_option :call, type: :boolean, default: false
16
15
  class_option :inline, type: :boolean, default: false
17
16
  class_option :locale, type: :boolean, default: ViewComponent::Base.config.generate.locale
18
17
  class_option :parent, type: :string, desc: "The parent class for the generated component"
19
18
  class_option :preview, type: :boolean, default: ViewComponent::Base.config.generate.preview
20
- class_option :sidecar, type: :boolean, default: false
21
- class_option :stimulus, type: :boolean,
22
- default: ViewComponent::Base.config.generate.stimulus_controller
23
- class_option :skip_suffix, type: :boolean, default: false
19
+ class_option :sidecar, type: :boolean
20
+ class_option :stimulus, type: :boolean
21
+ class_option :typescript, type: :boolean
22
+
23
+ def check_class_collision
24
+ class_collisions component_class_name
25
+ end
24
26
 
25
27
  def create_component_file
26
- template "component.rb", File.join(component_path, class_path, "#{file_name}#{"_component" unless options[:skip_suffix]}.rb")
28
+ template "component.rb", File.join(component_path, class_path, "#{destination_file_name}.rb")
27
29
  end
28
30
 
29
31
  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 %><%= options[:skip_suffix] ? "" : "Component" %> < <%= parent_class %>
4
+ class <%= component_class_name %> < <%= parent_class %>
5
5
  <%- if inline_template? -%>
6
6
  <%= template_engine %>_template <<~<%= template_engine.upcase %>
7
7
  <h1>Hello, World!</h1>
@@ -9,10 +9,10 @@ module ViewComponent
9
9
  include ViewComponent::AbstractGenerator
10
10
 
11
11
  source_root File.expand_path("templates", __dir__)
12
- class_option :sidecar, type: :boolean, default: false
12
+ class_option :sidecar, type: :boolean
13
13
  class_option :inline, type: :boolean, default: false
14
14
  class_option :call, type: :boolean, default: false
15
- class_option :stimulus, type: :boolean, default: false
15
+ class_option :stimulus, type: :boolean
16
16
 
17
17
  def engine_name
18
18
  "erb"
@@ -8,7 +8,7 @@ module ViewComponent
8
8
  include ViewComponent::AbstractGenerator
9
9
 
10
10
  source_root File.expand_path("templates", __dir__)
11
- class_option :sidecar, type: :boolean, default: false
11
+ class_option :sidecar, type: :boolean
12
12
 
13
13
  def engine_name
14
14
  "haml"
@@ -9,7 +9,7 @@ module ViewComponent
9
9
 
10
10
  source_root File.expand_path("templates", __dir__)
11
11
  argument :attributes, type: :array, default: [], banner: "attribute"
12
- class_option :sidecar, type: :boolean, default: false
12
+ class_option :sidecar, type: :boolean
13
13
 
14
14
  def create_locale_file
15
15
  if ViewComponent::Base.config.generate.distinct_locale_files
@@ -35,11 +35,7 @@ module ViewComponent
35
35
 
36
36
  def destination(locale = nil)
37
37
  extension = ".#{locale}" if locale
38
- if sidecar?
39
- File.join(component_path, class_path, "#{file_name}_component", "#{file_name}_component#{extension}.yml")
40
- else
41
- File.join(component_path, class_path, "#{file_name}_component#{extension}.yml")
42
- end
38
+ File.join(destination_directory, "#{destination_file_name}#{extension}.yml")
43
39
  end
44
40
  end
45
41
  end
@@ -1,13 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "generators/view_component/abstract_generator"
4
+
3
5
  module ViewComponent
4
6
  module Generators
5
7
  class PreviewGenerator < ::Rails::Generators::NamedBase
8
+ include ViewComponent::AbstractGenerator
9
+
6
10
  source_root File.expand_path("templates", __dir__)
7
11
  class_option :preview_path, type: :string, desc: "Path for previews, required when multiple preview paths are configured", default: ViewComponent::Base.config.generate.preview_path
8
12
 
9
13
  argument :attributes, type: :array, default: [], banner: "attribute"
10
- check_class_collision suffix: "ComponentPreview"
14
+
15
+ def check_class_collision
16
+ class_collisions "#{component_class_name}Preview"
17
+ end
11
18
 
12
19
  def create_preview_file
13
20
  preview_paths = ViewComponent::Base.config.previews.paths
@@ -20,15 +27,11 @@ module ViewComponent
20
27
  preview_paths.one? ? preview_paths.first : "test/components/previews"
21
28
  end
22
29
 
23
- template "component_preview.rb", File.join(path_prefix, class_path, "#{file_name}_component_preview.rb")
30
+ template "component_preview.rb", File.join(path_prefix, class_path, "#{destination_file_name}_preview.rb")
24
31
  end
25
32
 
26
33
  private
27
34
 
28
- def file_name
29
- @_file_name ||= super.sub(/_component\z/i, "")
30
- end
31
-
32
35
  def render_signature
33
36
  return if attributes.blank?
34
37
 
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  <% module_namespacing do -%>
4
- class <%= class_name %>ComponentPreview < ViewComponent::Preview
4
+ class <%= component_class_name %>Preview < ViewComponent::Preview
5
5
  def default
6
- render(<%= class_name %>Component.new<%= "(#{render_signature})" if render_signature %>)
6
+ render(<%= component_class_name %>.new<%= "(#{render_signature})" if render_signature %>)
7
7
  end
8
8
  end
9
9
  <% end -%>
@@ -10,7 +10,7 @@ module ViewComponent
10
10
  source_root File.expand_path("templates", __dir__)
11
11
 
12
12
  def create_test_file
13
- template "component_spec.rb", File.join(spec_component_path, class_path, "#{file_name}_component_spec.rb")
13
+ template "component_spec.rb", File.join(spec_component_path, class_path, "#{destination_file_name}_spec.rb")
14
14
  end
15
15
 
16
16
  private
@@ -2,7 +2,7 @@
2
2
 
3
3
  require "rails_helper"
4
4
 
5
- RSpec.describe <%= namespaced? ? "#{namespace.name}::" : '' %><%= class_name %>Component, type: :component do
5
+ RSpec.describe <%= namespaced? ? "#{namespace.name}::" : '' %><%= component_class_name %>, type: :component do
6
6
  pending "add some examples to (or delete) #{__FILE__}"
7
7
 
8
8
  # it "renders something useful" do
@@ -8,7 +8,7 @@ module ViewComponent
8
8
  include ViewComponent::AbstractGenerator
9
9
 
10
10
  source_root File.expand_path("templates", __dir__)
11
- class_option :sidecar, type: :boolean, default: false
11
+ class_option :sidecar, type: :boolean
12
12
 
13
13
  def engine_name
14
14
  "slim"
@@ -8,8 +8,8 @@ module ViewComponent
8
8
  include ViewComponent::AbstractGenerator
9
9
 
10
10
  source_root File.expand_path("templates", __dir__)
11
- class_option :sidecar, type: :boolean, default: false
12
- class_option :typescript, type: :boolean, default: false
11
+ class_option :sidecar, type: :boolean
12
+ class_option :typescript, type: :boolean
13
13
 
14
14
  def create_stimulus_controller
15
15
  template "component_controller.#{filetype}", destination
@@ -28,11 +28,7 @@ module ViewComponent
28
28
  end
29
29
 
30
30
  def destination
31
- if sidecar?
32
- File.join(component_path, class_path, "#{file_name}_component", "#{file_name}_component_controller.#{filetype}")
33
- else
34
- File.join(component_path, class_path, "#{file_name}_component_controller.#{filetype}")
35
- end
31
+ File.join(destination_directory, "#{destination_file_name}_controller.#{filetype}")
36
32
  end
37
33
 
38
34
  def legacy_stimulus?
@@ -2,11 +2,11 @@
2
2
 
3
3
  require "test_helper"
4
4
 
5
- class <%= namespaced? ? "#{namespace.name}::" : '' %><%= class_name %>ComponentTest < ViewComponent::TestCase
5
+ class <%= namespaced? ? "#{namespace.name}::" : '' %><%= component_class_name %>Test < ViewComponent::TestCase
6
6
  def test_component_renders_something_useful
7
7
  # assert_equal(
8
8
  # %(<span>Hello, components!</span>),
9
- # render_inline(<%= class_name %>Component.new(message: "Hello, components!")).css("span").to_html
9
+ # render_inline(<%= component_class_name %>.new(message: "Hello, components!")).css("span").to_html
10
10
  # )
11
11
  end
12
12
  end
@@ -1,19 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "generators/view_component/abstract_generator"
4
+
3
5
  module ViewComponent
4
6
  module Generators
5
7
  class TestUnitGenerator < ::Rails::Generators::NamedBase
8
+ include ViewComponent::AbstractGenerator
9
+
6
10
  source_root File.expand_path("templates", __dir__)
7
- check_class_collision suffix: "ComponentTest"
8
11
 
9
- def create_test_file
10
- template "component_test.rb", File.join("test/components", class_path, "#{file_name}_component_test.rb")
12
+ def check_class_collision
13
+ class_collisions "#{component_class_name}Test"
11
14
  end
12
15
 
13
- private
14
-
15
- def file_name
16
- @_file_name ||= super.sub(/_component\z/i, "")
16
+ def create_test_file
17
+ template "component_test.rb", File.join("test/components", class_path, "#{destination_file_name}_test.rb")
17
18
  end
18
19
  end
19
20
  end