view_component 2.43.1 → 2.47.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: 68e0fc06b5f3e59c4baf241bef8827bb6d917823ee7d79052ff7e2e9406363da
4
- data.tar.gz: a21f0dabe952531d817fdd6b3859089e4a21bd3f7474bdaee6c6b5953a653593
3
+ metadata.gz: 2c94e6fdeccccb7180494d100eef8c78a8dc8e3e9bc30e5647017b440788c6eb
4
+ data.tar.gz: c3836df9c15e8039ba673895ad5a9544aebe55325f9fbd418b96f4bb4238b8d6
5
5
  SHA512:
6
- metadata.gz: 7c8068857218b70cba3d99fe04df088ab2bb0da3f03f9277352b7a96dcebaff10fcf0d1b3657004ca5d6552c2033173a599fd64ce81b1e5635db224d48617d51
7
- data.tar.gz: e7808192370b1c9f3eb4af84def46ebbfc4828b23f4c75be2f4753b18463f733beff77fff65ba3ccaa690efb6365fa5dcab574b962f905deb1cfbd4475dc3f20
6
+ metadata.gz: 644f964985c8418371fdcac45e1e7b1771d89ee65ed8a692ec51c50cbed8a578937ba1bb7b9bf1d3b3e3729bd418df5fb2e9d1aee3bbb8215bfbed4504457074
7
+ data.tar.gz: c4df9e14be4dc20e529beb794c96baf424087f7e36423b301402325e1b1ce0ae8ab575fe1b69334b3e27b5fa9e2fee227e9bd20e0e99fe5f384f2f535b43f80c
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
- <img src="/docs/logo/viewcomponent-color-logo.svg" alt="ViewComponent logo" width="500">
1
+ ![ViewComponent logo](/docs/logo/readme-light.svg#gh-light-mode-only)
2
+ ![ViewComponent logo](/docs/logo/readme-dark.svg#gh-dark-mode-only)
2
3
 
3
4
  A framework for building reusable, testable & encapsulated view components in Ruby on Rails.
4
5
 
@@ -29,12 +29,11 @@ class ViewComponentsController < Rails::ApplicationController # :nodoc:
29
29
  @example_name = File.basename(params[:path])
30
30
  @render_args = @preview.render_args(@example_name, params: params.permit!)
31
31
  layout = determine_layout(@render_args[:layout], prepend_views: false)[:layout]
32
- template = @render_args[:template]
33
32
  locals = @render_args[:locals]
34
33
  opts = {}
35
34
  opts[:layout] = layout if layout.present? || layout == false
36
35
  opts[:locals] = locals if locals.present?
37
- render template, opts # rubocop:disable GitHub/RailsControllerRenderLiteral
36
+ render "view_components/preview", opts # rubocop:disable GitHub/RailsControllerRenderLiteral
38
37
  end
39
38
  end
40
39
 
@@ -4,16 +4,64 @@ module PreviewHelper
4
4
  AVAILABLE_PRISM_LANGUAGES = ["ruby", "erb", "haml"]
5
5
  FALLBACK_LANGUAGE = "ruby"
6
6
 
7
- def prism_language_name(template:)
7
+ def preview_source
8
+ return if @render_args.nil?
9
+
10
+ render "preview_source" # rubocop:disable GitHub/RailsViewRenderPathsExist
11
+ end
12
+
13
+ def find_template_data(lookup_context:, template_identifier:)
14
+ template = lookup_context.find_template(template_identifier)
15
+
16
+ if Rails.version.to_f >= 6.1 || template.source.present?
17
+ return {
18
+ source: template.source,
19
+ prism_language_name: prism_language_name_by_template(template: template)
20
+ }
21
+ else
22
+ # Fetch template source via finding it through preview paths
23
+ # to accomodate source view when exclusively using templates
24
+ # for previews for Rails < 6.1.
25
+ all_template_paths = ViewComponent::Base.preview_paths.map do |preview_path|
26
+ Dir.glob("#{preview_path}/**/*")
27
+ end.flatten
28
+
29
+ # Search for templates the contain `html`.
30
+ matching_templates = all_template_paths.find_all do |template|
31
+ template =~ /#{template_identifier}*.(html)/
32
+ end
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
38
+
39
+ template_file_path = matching_templates.first
40
+ template_source = File.read(template_file_path)
41
+ prism_language_name = prism_language_name_by_template_path(template_file_path: template_file_path)
42
+
43
+ return {
44
+ source: template_source,
45
+ prism_language_name: prism_language_name
46
+ }
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def prism_language_name_by_template(template:)
8
53
  language = template.identifier.split(".").last
54
+
9
55
  return FALLBACK_LANGUAGE unless AVAILABLE_PRISM_LANGUAGES.include? language
10
56
 
11
57
  language
12
58
  end
13
59
 
14
- def preview_source
15
- return if @render_args.nil?
60
+ def prism_language_name_by_template_path(template_file_path:)
61
+ language = template_file_path.gsub(".html", "").split(".").last
16
62
 
17
- render "preview_source" # rubocop:disable GitHub/RailsViewRenderPathsExist
63
+ return FALLBACK_LANGUAGE unless AVAILABLE_PRISM_LANGUAGES.include? language
64
+
65
+ language
18
66
  end
19
67
  end
@@ -7,9 +7,9 @@
7
7
  <%= h @preview.preview_source(@example_name) %>
8
8
  </code>
9
9
  <% else %>
10
- <% template = @view_renderer.lookup_context.find_template(@render_args[:template]) %>
11
- <code class="language-<%= prism_language_name(template: template) %>">
12
- <%= h template.source %>
10
+ <% template_data = find_template_data(lookup_context: @view_renderer.lookup_context, template_identifier: @render_args[:template]) %>
11
+ <code class="language-<%= template_data[:prism_language_name] %>">
12
+ <%= h template_data[:source] %>
13
13
  </code>
14
14
  <% end %>
15
15
  </pre>
@@ -1,7 +1,11 @@
1
- <% if ViewComponent::Base.render_monkey_patch_enabled || Rails.version.to_f >= 6.1 %>
2
- <%= render(@render_args[:component], @render_args[:args], &@render_args[:block]) %>
1
+ <% if @render_args[:component] %>
2
+ <% if ViewComponent::Base.render_monkey_patch_enabled || Rails.version.to_f >= 6.1 %>
3
+ <%= render(@render_args[:component], @render_args[:args], &@render_args[:block]) %>
4
+ <% else %>
5
+ <%= render_component(@render_args[:component], &@render_args[:block]) %>
6
+ <% end %>
3
7
  <% else %>
4
- <%= render_component(@render_args[:component], &@render_args[:block]) %>
8
+ <%= render template: @render_args[:template], locals: @render_args[:locals] || {} %>
5
9
  <% end %>
6
10
 
7
11
  <% if ViewComponent::Base.show_previews_source %>
data/docs/CHANGELOG.md CHANGED
@@ -7,6 +7,191 @@ title: Changelog
7
7
 
8
8
  ## main
9
9
 
10
+ ## 2.47.0
11
+
12
+ * Display preview source on previews that exclusively use templates.
13
+
14
+ *Edwin Mak*
15
+
16
+ * Add a test to ensure trailing newlines are stripped when rendering with `#render_in`.
17
+
18
+ *Simon Fish*
19
+
20
+ * Add WEBrick as a depenency to the docs application.
21
+
22
+ *Connor McQuillan*
23
+
24
+ * Update Ruby version in `.tool-versions`.
25
+
26
+ *Connor McQuillan*
27
+
28
+ * Add a test to ensure blocks can be passed into lambda slots without the need for any other arguments.
29
+
30
+ *Simon Fish*
31
+
32
+ * Add linters for file consistency.
33
+
34
+ *Simon Fish*
35
+
36
+ * Add @boardfish to docs/index.md and sort contributors.
37
+
38
+ *Simon Fish*
39
+
40
+ * Set up Codespaces for bug replication.
41
+
42
+ *Simon Fish*
43
+
44
+ * Add instructions for replicating bugs and failures.
45
+
46
+ *Simon Fish*
47
+
48
+ * Make @boardfish a committer.
49
+
50
+ *Joel Hawksley*
51
+
52
+ * Validate collection parameter with Active Model attribute names.
53
+
54
+ *Simon Fish*
55
+
56
+ * Fix `helpers` not working with component slots when rendered more than 2 component levels deep.
57
+
58
+ *Blake Williams*
59
+
60
+ * Update ruby to the latest versions
61
+
62
+ *Pedro Paiva*
63
+
64
+ * Fix `vale` linter config options.
65
+
66
+ *Hans Lemuet*
67
+
68
+ * Improve Contributing docs to include how to run tests for a specific version on Rails.
69
+
70
+ *Hans Lemuet*
71
+
72
+ * Add failing test for default form builder and documentation around known issue.
73
+
74
+ *Simon Fish*
75
+
76
+ * Add `--locale` flag to the component generator. Generates as many locale files as defined in `I18n.available_locales`, alongside the component.
77
+ * Add config option `config.view_component.generate_locale` to enable project-wide locale generation.
78
+ * Add config option `config.view_component.generate_distinct_locale_files` to enable project-wide per-locale translations file generation.
79
+
80
+ *Bob Maerten*
81
+
82
+ ## 2.46.0
83
+
84
+ * Add thread safety to the compiler.
85
+
86
+ *Horia Radu*
87
+
88
+ * Add theme-specific logo images to readme.
89
+
90
+ *Dylan Smith*
91
+
92
+ * Add Orbit to users list.
93
+
94
+ *Nicolas Goutay*
95
+
96
+ * Increase clarity around purpose and use of slots.
97
+
98
+ *Simon Fish*
99
+
100
+ * Deprecate loading `view_component/engine` directly.
101
+
102
+ **Upgrade notice**: You should update your `Gemfile` like this:
103
+
104
+ ```diff
105
+ - gem "view_component", require: "view_component/engine"`
106
+ + gem "view_component"
107
+ ```
108
+
109
+ *Yoshiyuki Hirano*
110
+
111
+ ## 2.45.0
112
+
113
+ * Remove internal APIs from API documentation, fix link to license.
114
+
115
+ *Joel Hawksley*
116
+
117
+ * Add @yhirano55 to triage team.
118
+
119
+ *Joel Hawksley*
120
+
121
+ * Correct a typo in the sample slots code.
122
+
123
+ *Simon Fish*
124
+
125
+ * Add note about `allowed_queries`.
126
+
127
+ *Joel Hawksley*
128
+
129
+ * Add `vale` content linter.
130
+
131
+ *Joel Hawksley*
132
+
133
+ * Remove `require "rails/generators/test_case"` in generator tests.
134
+
135
+ *Yoshiyuki Hirano*
136
+
137
+ * Suppress zeitwerk warning about circular require.
138
+
139
+ *Yoshiyuki Hirano*
140
+
141
+ * Move `test_unit_generator_test.rb` from `test/view_component/` to `test/generators/`.
142
+
143
+ *Yoshiyuki Hirano*
144
+
145
+ * Unify test code of `TestUnitGeneratorTest` with the other generators tests.
146
+
147
+ *Yoshiyuki Hirano*
148
+
149
+ ## 2.44.0
150
+
151
+ * Rename internal accessor to use private naming.
152
+
153
+ *Joel Hawksley*, *Blake Williams*, *Cameron Dutro*
154
+
155
+ * Add Github repo link to docs website header.
156
+
157
+ *Hans Lemuet*
158
+
159
+ * Change logo in README for dark theme readability.
160
+
161
+ *Dylan Smith*
162
+
163
+ * Add Litmus to users list.
164
+
165
+ *Dylan Smith*
166
+
167
+ * Add @dylanatsmith as codeowner of the ViewComponent logo and member of committers team.
168
+
169
+ *Joel Hawksley*
170
+
171
+ * Autoload `CompileCache`, which is optionally called in `engine.rb`.
172
+
173
+ *Gregory Igelmund*
174
+
175
+ * Move frequently asked questions to other pages, add History page.
176
+
177
+ *Joel Hawksley*
178
+
179
+ * Fix typo.
180
+
181
+ *James Hart*
182
+
183
+ * Add `require "method_source"` if it options.show_previews_source is enabled.
184
+
185
+ *Yoshiyuki Hirano*
186
+
187
+ * Move show_previews_source definition to Previewable.
188
+
189
+ *Yoshiyuki Hirano*
190
+
191
+ * Clear cache in MethodSource to apply the change odf preview code without app server restart.
192
+
193
+ *Yoshiyuki Hirano*
194
+
10
195
  ## 2.43.1
11
196
 
12
197
  * Remove unnecessary call to `ruby2_keywords` for polymorphic slot getters.
@@ -96,7 +281,7 @@ title: Changelog
96
281
 
97
282
  *Matthew Rider*
98
283
 
99
- * Fix bug where `with_collection_parameter` did not inherit from parent component.
284
+ * Fix bug where `with_collection_parameter` didn't inherit from parent component.
100
285
 
101
286
  *Will Drexler*, *Christian Campoli*
102
287
 
@@ -300,7 +485,7 @@ title: Changelog
300
485
 
301
486
  *Hans Lemuet*
302
487
 
303
- * Fix bug where ViewComponents did not work in ActionMailers.
488
+ * Fix bug where ViewComponents didn't work in ActionMailers.
304
489
 
305
490
  *dark-panda*
306
491
 
@@ -348,7 +533,7 @@ title: Changelog
348
533
 
349
534
  ## 2.31.0
350
535
 
351
- _Note: This release includes an underlying change to Slots that may affect incorrect usage of the API, where Slots were set on a line prefixed by `<%=`. The result of setting a Slot should not be returned. (`<%`)_
536
+ _Note: This release includes an underlying change to Slots that may affect incorrect usage of the API, where Slots were set on a line prefixed by `<%=`. The result of setting a Slot shouldn't be returned. (`<%`)_
352
537
 
353
538
  * Add `#with_content` to allow setting content without a block.
354
539
 
@@ -359,7 +544,7 @@ _Note: This release includes an underlying change to Slots that may affect incor
359
544
  *Mario Schüttel*
360
545
 
361
546
  * Improve feature parity with Rails translations
362
- * Don't create a translation backend if the component has no translation file
547
+ * Don't create a translation back end if the component has no translation file
363
548
  * Mark translation keys ending with `html` as HTML-safe
364
549
  * Always convert keys to String
365
550
  * Support multiple keys
@@ -401,7 +586,7 @@ _Note: This release includes an underlying change to Slots that may affect incor
401
586
 
402
587
  *Alex Robbin, Blake Williams*
403
588
 
404
- * Experimental: call `._sidecar_files` to fetch the sidecar files for a given list of extensions, e.g. passing `["yml", "yaml"]`.
589
+ * Experimental: call `._sidecar_files` to fetch the sidecar files for a given list of extensions, for example passing `["yml", "yaml"]`.
405
590
 
406
591
  *Elia Schito*
407
592
 
@@ -441,11 +626,11 @@ _Note: This release includes an underlying change to Slots that may affect incor
441
626
 
442
627
  ## 2.26.0
443
628
 
444
- * Lazily evaluate component `content` in `render?`, preventing the `content` block from being evaluated when `render?` returns false.
629
+ * Delay evaluating component `content` in `render?`, preventing the `content` block from being evaluated when `render?` returns false.
445
630
 
446
631
  *Blake Williams*
447
632
 
448
- * Do not generate template when using `--inline` flag.
633
+ * Don't generate template when using `--inline` flag.
449
634
 
450
635
  *Hans Lemuet*
451
636
 
@@ -506,7 +691,7 @@ _Note: This release includes an underlying change to Slots that may affect incor
506
691
  * `with_slot collection: true` becomes `renders_many`.
507
692
  * Slot definitions now accept either a component class, component class name, or a lambda instead of a `class_name:` keyword argument.
508
693
  * Slots now support positional arguments.
509
- * Slots no longer use the `content` attribute to render content, instead relying on `to_s`. e.g. `<%= my_slot %>`.
694
+ * Slots no longer use the `content` attribute to render content, instead relying on `to_s`. for example `<%= my_slot %>`.
510
695
  * Slot values are no longer set via the `slot` method, and instead use the name of the slot.
511
696
 
512
697
  *Blake Williams*
@@ -569,7 +754,7 @@ _Note: This release includes an underlying change to Slots that may affect incor
569
754
 
570
755
  ## 2.18.2
571
756
 
572
- * Raise an error if controller or view context is accessed during initialize as they are only available in render.
757
+ * Raise an error if controller or view context is accessed during initialize as they're only available in render.
573
758
 
574
759
  *Julian Nadeau*
575
760
 
@@ -585,7 +770,7 @@ _Note: This release includes an underlying change to Slots that may affect incor
585
770
 
586
771
  ## 2.18.0
587
772
 
588
- * Fix auto-loading of previews (changes no longer require a server restart)
773
+ * Fix auto loading of previews (changes no longer require a server restart)
589
774
 
590
775
  *Matt Brictson*
591
776
 
@@ -678,7 +863,7 @@ _Note: This release includes an underlying change to Slots that may affect incor
678
863
 
679
864
  ## 2.10.0
680
865
 
681
- * Raise an `ArgumentError` with a helpful message when Ruby cannot parse a component class.
866
+ * Raise an `ArgumentError` with a helpful message when Ruby can't parse a component class.
682
867
 
683
868
  *Max Beizer*
684
869
 
@@ -770,7 +955,7 @@ _Note: This release includes an underlying change to Slots that may affect incor
770
955
 
771
956
  ## v2.2.1
772
957
 
773
- * Fix bug where template could not be found if `inherited` was redefined.
958
+ * Fix bug where template couldn't be found if `inherited` was redefined.
774
959
 
775
960
  *Joel Hawksley*
776
961
 
@@ -786,7 +971,7 @@ _Note: This release includes an underlying change to Slots that may affect incor
786
971
 
787
972
  ## v2.1.0
788
973
 
789
- * Support rendering collections (e.g., `render(MyComponent.with_collection(@items))`).
974
+ * Support rendering collections (for example, `render(MyComponent.with_collection(@items))`).
790
975
 
791
976
  *Tim Clem*
792
977
 
@@ -808,7 +993,7 @@ _Note: This release includes an underlying change to Slots that may affect incor
808
993
 
809
994
  *Andrew Mason*
810
995
 
811
- * ViewComponent generators do not not prompt for content requirement.
996
+ * ViewComponent generators don't not prompt for content requirement.
812
997
 
813
998
  *Joel Hawksley*
814
999
 
@@ -930,7 +1115,7 @@ _Note: This release includes an underlying change to Slots that may affect incor
930
1115
 
931
1116
  *Jon Palmer*
932
1117
 
933
- * Add `#render?` hook to easily allow components to be no-ops.
1118
+ * Add `#render?` hook to allow components to be no-ops.
934
1119
 
935
1120
  *Kyle Fox*
936
1121
 
@@ -998,7 +1183,9 @@ _Note: This release includes an underlying change to Slots that may affect incor
998
1183
 
999
1184
  * Fix edge case issue with extracting variants from less conventional source_locations.
1000
1185
 
1186
+ <!-- vale proselint.GenderBias = NO -->
1001
1187
  *Ryan Workman*
1188
+ <!-- vale proselint.GenderBias = YES -->
1002
1189
 
1003
1190
  ## v1.6.0
1004
1191
 
@@ -1034,7 +1221,9 @@ _Note: This release includes an underlying change to Slots that may affect incor
1034
1221
 
1035
1222
  * Add support for RSpec to generators.
1036
1223
 
1224
+ <!-- vale proselint.GenderBias = NO -->
1037
1225
  *Dylan Clark, Ryan Workman*
1226
+ <!-- vale proselint.GenderBias = YES -->
1038
1227
 
1039
1228
  * Require controllers as part of setting autoload paths.
1040
1229
 
@@ -1058,7 +1247,9 @@ Note: `actionview-component` is now loaded by requiring `actionview/component`,
1058
1247
 
1059
1248
  * Fix issue with generating component method signatures.
1060
1249
 
1250
+ <!-- vale proselint.GenderBias = NO -->
1061
1251
  *Ryan Workman, Dylan Clark*
1252
+ <!-- vale proselint.GenderBias = YES -->
1062
1253
 
1063
1254
  * Create component generator.
1064
1255
 
@@ -1130,7 +1321,7 @@ Note: `actionview-component` is now loaded by requiring `actionview/component`,
1130
1321
 
1131
1322
  ## v1.3.3
1132
1323
 
1133
- * Do not raise error when sidecar files that are not templates exist.
1324
+ * Don't raise error when sidecar files that aren't templates exist.
1134
1325
 
1135
1326
  *Joel Hawksley*
1136
1327
 
@@ -15,6 +15,7 @@ module Rails
15
15
  class_option :parent, type: :string, desc: "The parent class for the generated component"
16
16
  class_option :stimulus, type: :boolean, default: ViewComponent::Base.generate_stimulus_controller
17
17
  class_option :sidecar, type: :boolean, default: false
18
+ class_option :locale, type: :boolean, default: ViewComponent::Base.generate_locale
18
19
 
19
20
  def create_component_file
20
21
  template "component.rb", File.join(component_path, class_path, "#{file_name}_component.rb")
@@ -26,6 +27,8 @@ module Rails
26
27
 
27
28
  hook_for :stimulus, type: :boolean
28
29
 
30
+ hook_for :locale, type: :boolean
31
+
29
32
  hook_for :template_engine do |instance, template_engine|
30
33
  instance.invoke template_engine, [instance.name]
31
34
  end
@@ -6,7 +6,7 @@ class <%= class_name %>Component < <%= parent_class %>
6
6
  <%= initialize_body %>
7
7
  end
8
8
  <%- end -%>
9
- <%- if initialize_call_method_for_inline? -%>
9
+ <%- if initialize_call_method_for_inline? -%>
10
10
  def call
11
11
  content_tag :h1, "Hello world!"<%= ", data: { controller: \"#{stimulus_controller}\" }" if options["stimulus"] %>
12
12
  end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/abstract_generator"
4
+
5
+ module Locale
6
+ module Generators
7
+ class ComponentGenerator < ::Rails::Generators::NamedBase
8
+ include ViewComponent::AbstractGenerator
9
+
10
+ source_root File.expand_path("templates", __dir__)
11
+ argument :attributes, type: :array, default: [], banner: "attribute"
12
+ class_option :sidecar, type: :boolean, default: false
13
+
14
+ def create_locale_file
15
+ if ViewComponent::Base.generate_distinct_locale_files
16
+ I18n.available_locales.each do |locale|
17
+ create_file destination(locale), translations_hash([locale]).to_yaml
18
+ end
19
+ else
20
+ create_file destination, translations_hash(I18n.available_locales).to_yaml
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def translations_hash(locales = [:en])
27
+ locales.map { |locale| [locale.to_s, translation_keys] }.to_h
28
+ end
29
+
30
+ def translation_keys
31
+ keys = attributes.map(&:name)
32
+ keys = %w[hello] if keys.empty?
33
+ keys.map { |name| [name, name.capitalize] }.to_h
34
+ end
35
+
36
+ def destination(locale = nil)
37
+ extention = ".#{locale}" if locale
38
+ if options["sidecar"]
39
+ File.join(component_path, class_path, "#{file_name}_component", "#{file_name}_component#{extention}.yml")
40
+ else
41
+ File.join(component_path, class_path, "#{file_name}_component#{extention}.yml")
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -29,7 +29,7 @@ module ViewComponent
29
29
  class_attribute :content_areas
30
30
  self.content_areas = [] # class_attribute:default doesn't work until Rails 5.2
31
31
 
32
- attr_accessor :original_view_context
32
+ attr_accessor :__vc_original_view_context
33
33
 
34
34
  # EXPERIMENTAL: This API is experimental and may be removed at any time.
35
35
  # Hook for allowing components to do work as part of the compilation process.
@@ -52,7 +52,7 @@ module ViewComponent
52
52
  self.class.compile(raise_errors: true)
53
53
 
54
54
  @view_context = view_context
55
- self.original_view_context ||= view_context
55
+ self.__vc_original_view_context ||= view_context
56
56
 
57
57
  @lookup_context ||= view_context.lookup_context
58
58
 
@@ -137,10 +137,10 @@ module ViewComponent
137
137
  # @private
138
138
  def render(options = {}, args = {}, &block)
139
139
  if options.is_a? ViewComponent::Base
140
- options.original_view_context = original_view_context
140
+ options.__vc_original_view_context = __vc_original_view_context
141
141
  super
142
142
  else
143
- original_view_context.render(options, args, &block)
143
+ __vc_original_view_context.render(options, args, &block)
144
144
  end
145
145
  end
146
146
 
@@ -152,7 +152,7 @@ module ViewComponent
152
152
  if view_context.nil?
153
153
  raise(
154
154
  ViewContextCalledBeforeRenderError,
155
- "`#controller` cannot be used during initialization, as it depends " \
155
+ "`#controller` can't be used during initialization, as it depends " \
156
156
  "on the view context that only exists once a ViewComponent is passed to " \
157
157
  "the Rails render pipeline.\n\n" \
158
158
  "It's sometimes possible to fix this issue by moving code dependent on " \
@@ -171,7 +171,7 @@ module ViewComponent
171
171
  if view_context.nil?
172
172
  raise(
173
173
  ViewContextCalledBeforeRenderError,
174
- "`#helpers` cannot be used during initialization, as it depends " \
174
+ "`#helpers` can't be used during initialization, as it depends " \
175
175
  "on the view context that only exists once a ViewComponent is passed to " \
176
176
  "the Rails render pipeline.\n\n" \
177
177
  "It's sometimes possible to fix this issue by moving code dependent on " \
@@ -186,7 +186,7 @@ module ViewComponent
186
186
  #
187
187
  # This allows ivars to remain persisted when using the same helper via
188
188
  # `helpers` across multiple components and partials.
189
- @__vc_helpers ||= original_view_context || controller.view_context
189
+ @__vc_helpers ||= __vc_original_view_context || controller.view_context
190
190
  end
191
191
 
192
192
  # Exposes .virtual_path as an instance method
@@ -206,7 +206,7 @@ module ViewComponent
206
206
  #
207
207
  # @private
208
208
  def format
209
- # Ruby 2.6 throws a warning without checking `defined?`, 2.7 does not
209
+ # Ruby 2.6 throws a warning without checking `defined?`, 2.7 doesn't
210
210
  if defined?(@__vc_variant)
211
211
  @__vc_variant
212
212
  end
@@ -271,27 +271,38 @@ module ViewComponent
271
271
  #
272
272
  mattr_accessor :render_monkey_patch_enabled, instance_writer: false, default: true
273
273
 
274
- # Enable or disable source code previews in component previews:
274
+ # Always generate a Stimulus controller alongside the component:
275
275
  #
276
- # config.view_component.show_previews_source = true
276
+ # config.view_component.generate_stimulus_controller = true
277
277
  #
278
278
  # Defaults to `false`.
279
279
  #
280
- mattr_accessor :show_previews_source, instance_writer: false, default: false
280
+ mattr_accessor :generate_stimulus_controller, instance_writer: false, default: false
281
281
 
282
- # Always generate a Stimulus controller alongside the component:
282
+ # Always generate translations file alongside the component:
283
283
  #
284
- # config.view_component.generate_stimulus_controller = true
284
+ # config.view_component.generate_locale = true
285
285
  #
286
286
  # Defaults to `false`.
287
287
  #
288
- mattr_accessor :generate_stimulus_controller, instance_writer: false, default: false
288
+ mattr_accessor :generate_locale, instance_writer: false, default: false
289
+
290
+ # Always generate as many translations files as available locales:
291
+ #
292
+ # config.view_component.generate_distinct_locale_files = true
293
+ #
294
+ # Defaults to `false`.
295
+ #
296
+ # One file will be generated for each configured `I18n.available_locales`.
297
+ # Fallback on `[:en]` when no available_locales is defined.
298
+ #
299
+ mattr_accessor :generate_distinct_locale_files, instance_writer: false, default: false
289
300
 
290
301
  # Path for component files
291
302
  #
292
303
  # config.view_component.view_component_path = "app/my_components"
293
304
  #
294
- # Defaults to "app/components".
305
+ # Defaults to `app/components`.
295
306
  mattr_accessor :view_component_path, instance_writer: false, default: "app/components"
296
307
 
297
308
  # Parent class for generated components
@@ -326,7 +337,7 @@ module ViewComponent
326
337
 
327
338
  # Add support for nested components defined in the same file.
328
339
  #
329
- # e.g.
340
+ # for example
330
341
  #
331
342
  # class MyComponent < ViewComponent::Base
332
343
  # class MyOtherComponent < ViewComponent::Base
@@ -439,7 +450,7 @@ module ViewComponent
439
450
  end
440
451
 
441
452
  # Ensure the component initializer accepts the
442
- # collection parameter. By default, we do not
453
+ # collection parameter. By default, we don't
443
454
  # validate that the default parameter name
444
455
  # is accepted, as support for collection
445
456
  # rendering is optional.
@@ -450,7 +461,7 @@ module ViewComponent
450
461
  return unless parameter
451
462
  return if initialize_parameter_names.include?(parameter)
452
463
 
453
- # If Ruby cannot parse the component class, then the initalize
464
+ # If Ruby can't parse the component class, then the initalize
454
465
  # parameters will be empty and ViewComponent will not be able to render
455
466
  # the component.
456
467
  if initialize_parameters.empty?
@@ -463,14 +474,14 @@ module ViewComponent
463
474
  end
464
475
 
465
476
  raise ArgumentError.new(
466
- "The initializer for #{self} does not accept the parameter `#{parameter}`, " \
477
+ "The initializer for #{self} doesn't accept the parameter `#{parameter}`, " \
467
478
  "which is required in order to render it as a collection.\n\n" \
468
479
  "To fix this issue, update the initializer to accept `#{parameter}`.\n\n" \
469
480
  "See https://viewcomponent.org/guide/collections.html for more information on rendering collections."
470
481
  )
471
482
  end
472
483
 
473
- # Ensure the component initializer does not define
484
+ # Ensure the component initializer doesn't define
474
485
  # invalid parameters that could override the framework's
475
486
  # methods.
476
487
  # @private TODO: add documentation
@@ -478,7 +489,7 @@ module ViewComponent
478
489
  return unless initialize_parameter_names.include?(RESERVED_PARAMETER)
479
490
 
480
491
  raise ViewComponent::ComponentError.new(
481
- "#{self} initializer cannot accept the parameter `#{RESERVED_PARAMETER}`, as it will override a " \
492
+ "#{self} initializer can't accept the parameter `#{RESERVED_PARAMETER}`, as it will override a " \
482
493
  "public ViewComponent method. To fix this issue, rename the parameter."
483
494
  )
484
495
  end
@@ -515,6 +526,10 @@ module ViewComponent
515
526
  private
516
527
 
517
528
  def initialize_parameter_names
529
+ return attribute_names.map(&:to_sym) if respond_to?(:attribute_names)
530
+
531
+ return attribute_types.keys.map(&:to_sym) if Rails::VERSION::MAJOR <= 5 && respond_to?(:attribute_types)
532
+
518
533
  initialize_parameters.map(&:last)
519
534
  end
520
535
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ViewComponent
4
4
  # Keeps track of which templates have already been compiled
5
- # This is not part of the public API
5
+ # This isn't part of the public API
6
6
  module CompileCache
7
7
  mattr_accessor :cache, instance_reader: false, instance_accessor: false do
8
8
  Set.new
@@ -18,6 +18,10 @@ module ViewComponent
18
18
  cache.include? klass
19
19
  end
20
20
 
21
+ def invalidate_class!(klass)
22
+ cache.delete(klass)
23
+ end
24
+
21
25
  def invalidate!
22
26
  cache.clear
23
27
  end
@@ -2,8 +2,12 @@
2
2
 
3
3
  module ViewComponent
4
4
  class Compiler
5
+ # Lock required to be obtained before compiling the component
6
+ attr_reader :__vc_compiler_lock
7
+
5
8
  def initialize(component_class)
6
9
  @component_class = component_class
10
+ @__vc_compiler_lock = Monitor.new
7
11
  end
8
12
 
9
13
  def compiled?
@@ -13,55 +17,59 @@ module ViewComponent
13
17
  def compile(raise_errors: false)
14
18
  return if compiled?
15
19
 
16
- subclass_instance_methods = component_class.instance_methods(false)
20
+ __vc_compiler_lock.synchronize do
21
+ CompileCache.invalidate_class!(component_class)
17
22
 
18
- if subclass_instance_methods.include?(:with_content) && raise_errors
19
- raise ViewComponent::ComponentError.new(
20
- "#{component_class} implements a reserved method, `#with_content`.\n\n" \
21
- "To fix this issue, change the name of the method."
22
- )
23
- end
23
+ subclass_instance_methods = component_class.instance_methods(false)
24
24
 
25
- if template_errors.present?
26
- raise ViewComponent::TemplateError.new(template_errors) if raise_errors
27
-
28
- return false
29
- end
25
+ if subclass_instance_methods.include?(:with_content) && raise_errors
26
+ raise ViewComponent::ComponentError.new(
27
+ "#{component_class} implements a reserved method, `#with_content`.\n\n" \
28
+ "To fix this issue, change the name of the method."
29
+ )
30
+ end
30
31
 
31
- if subclass_instance_methods.include?(:before_render_check)
32
- ActiveSupport::Deprecation.warn(
33
- "`#before_render_check` will be removed in v3.0.0.\n\n" \
34
- "To fix this issue, use `#before_render` instead."
35
- )
36
- end
32
+ if template_errors.present?
33
+ raise ViewComponent::TemplateError.new(template_errors) if raise_errors
37
34
 
38
- if raise_errors
39
- component_class.validate_initialization_parameters!
40
- component_class.validate_collection_parameter!
41
- end
35
+ return false
36
+ end
42
37
 
43
- templates.each do |template|
44
- # Remove existing compiled template methods,
45
- # as Ruby warns when redefining a method.
46
- method_name = call_method_name(template[:variant])
38
+ if subclass_instance_methods.include?(:before_render_check)
39
+ ActiveSupport::Deprecation.warn(
40
+ "`#before_render_check` will be removed in v3.0.0.\n\n" \
41
+ "To fix this issue, use `#before_render` instead."
42
+ )
43
+ end
47
44
 
48
- if component_class.instance_methods.include?(method_name.to_sym)
49
- component_class.send(:undef_method, method_name.to_sym)
45
+ if raise_errors
46
+ component_class.validate_initialization_parameters!
47
+ component_class.validate_collection_parameter!
50
48
  end
51
49
 
52
- component_class.class_eval <<-RUBY, template[:path], -1
53
- def #{method_name}
54
- @output_buffer = ActionView::OutputBuffer.new
55
- #{compiled_template(template[:path])}
50
+ templates.each do |template|
51
+ # Remove existing compiled template methods,
52
+ # as Ruby warns when redefining a method.
53
+ method_name = call_method_name(template[:variant])
54
+
55
+ if component_class.instance_methods.include?(method_name.to_sym)
56
+ component_class.send(:undef_method, method_name.to_sym)
56
57
  end
57
- RUBY
58
- end
59
58
 
60
- define_render_template_for
59
+ component_class.class_eval <<-RUBY, template[:path], -1
60
+ def #{method_name}
61
+ @output_buffer = ActionView::OutputBuffer.new
62
+ #{compiled_template(template[:path])}
63
+ end
64
+ RUBY
65
+ end
66
+
67
+ define_render_template_for
61
68
 
62
- component_class._after_compile
69
+ component_class._after_compile
63
70
 
64
- CompileCache.register(component_class)
71
+ CompileCache.register(component_class)
72
+ end
65
73
  end
66
74
 
67
75
  private
@@ -95,7 +103,7 @@ module ViewComponent
95
103
  errors = []
96
104
 
97
105
  if (templates + inline_calls).empty?
98
- errors << "Could not find a template file or inline render method for #{component_class}."
106
+ errors << "Couldn't find a template file or inline render method for #{component_class}."
99
107
  end
100
108
 
101
109
  if templates.count { |template| template[:variant].nil? } > 1
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rails"
4
- require "view_component"
5
4
 
6
5
  module ViewComponent
7
6
  class Engine < Rails::Engine # :nodoc:
@@ -17,6 +16,7 @@ module ViewComponent
17
16
 
18
17
  options.render_monkey_patch_enabled = true if options.render_monkey_patch_enabled.nil?
19
18
  options.show_previews = Rails.env.development? || Rails.env.test? if options.show_previews.nil?
19
+ options.show_previews_source ||= ViewComponent::Base.show_previews_source
20
20
  options.instrumentation_enabled = false if options.instrumentation_enabled.nil?
21
21
  options.preview_route ||= ViewComponent::Base.preview_route
22
22
  options.preview_controller ||= ViewComponent::Base.preview_controller
@@ -32,6 +32,14 @@ module ViewComponent
32
32
  )
33
33
  options.preview_paths << options.preview_path
34
34
  end
35
+
36
+ if options.show_previews_source
37
+ require "method_source"
38
+
39
+ app.config.to_prepare do
40
+ MethodSource.instance_variable_set(:@lines_for_file, {})
41
+ end
42
+ end
35
43
  end
36
44
 
37
45
  ActiveSupport.on_load(:view_component) do
@@ -136,3 +144,14 @@ module ViewComponent
136
144
  end
137
145
  end
138
146
  end
147
+
148
+ # :nocov:
149
+ unless defined?(ViewComponent::Base)
150
+ ActiveSupport::Deprecation.warn(
151
+ "This manually engine loading is deprecated and will be removed in v3.0.0. " \
152
+ "Remove `require \"view_component/engine\"`."
153
+ )
154
+
155
+ require "view_component"
156
+ end
157
+ # :nocov:
@@ -80,7 +80,7 @@ module ViewComponent # :nodoc:
80
80
  if preview_path.nil?
81
81
  raise(
82
82
  PreviewTemplateError,
83
- "A preview template for example #{example} does not exist.\n\n" \
83
+ "A preview template for example #{example} doesn't exist.\n\n" \
84
84
  "To fix this issue, create a template for the example."
85
85
  )
86
86
  end
@@ -15,6 +15,14 @@ module ViewComponent
15
15
  #
16
16
  mattr_accessor :show_previews, instance_writer: false
17
17
 
18
+ # Enable or disable source code previews in component previews:
19
+ #
20
+ # config.view_component.show_previews_source = true
21
+ #
22
+ # Defaults to `false`.
23
+ #
24
+ mattr_accessor :show_previews_source, instance_writer: false, default: false
25
+
18
26
  # Set a custom default layout used for preview index and individual previews:
19
27
  #
20
28
  # config.view_component.default_preview_layout = "component_preview"
@@ -20,7 +20,7 @@ module ViewComponent
20
20
  # component, or a function that returns a component, we render that
21
21
  # component instance, returning the string.
22
22
  #
23
- # If the slot renderable is a function and returns a string, it is
23
+ # If the slot renderable is a function and returns a string, it's
24
24
  # set as `@__vc_content` and is returned directly.
25
25
  #
26
26
  # If there is no slot renderable, we evaluate the block passed to
@@ -40,6 +40,8 @@ module ViewComponent
40
40
 
41
41
  @content =
42
42
  if defined?(@__vc_component_instance)
43
+ @__vc_component_instance.__vc_original_view_context = @parent.__vc_original_view_context
44
+
43
45
  if defined?(@__vc_content_set_by_with_content)
44
46
  @__vc_component_instance.with_content(@__vc_content_set_by_with_content)
45
47
 
@@ -69,7 +71,7 @@ module ViewComponent
69
71
 
70
72
  # Allow access to public component methods via the wrapper
71
73
  #
72
- # e.g.
74
+ # for example
73
75
  #
74
76
  # calling `header.name` (where `header` is a slot) will call `name`
75
77
  # on the `HeaderComponent` instance.
@@ -29,12 +29,12 @@ module ViewComponent
29
29
  )
30
30
 
31
31
  slot_names.each do |slot_name|
32
- # Ensure slot_name is not already declared
32
+ # Ensure slot_name isn't already declared
33
33
  if self.slots.key?(slot_name)
34
34
  raise ArgumentError.new("#{slot_name} slot declared multiple times")
35
35
  end
36
36
 
37
- # Ensure slot name is not :content
37
+ # Ensure slot name isn't :content
38
38
  if slot_name == :content
39
39
  raise ArgumentError.new ":content is a reserved slot name. Please use another name, such as ':body'"
40
40
  end
@@ -105,7 +105,7 @@ module ViewComponent
105
105
  # <% end %>
106
106
  #
107
107
  def slot(slot_name, **args, &block)
108
- # Raise ArgumentError if `slot` does not exist
108
+ # Raise ArgumentError if `slot` doesn't exist
109
109
  unless slots.keys.include?(slot_name)
110
110
  raise ArgumentError.new "Unknown slot '#{slot_name}' - expected one of '#{slots.keys}'"
111
111
  end
@@ -140,7 +140,7 @@ module ViewComponent
140
140
  instance_variable_set(slot[:instance_variable_name], slot_instance)
141
141
  end
142
142
 
143
- # Return nil, as this method should not output anything to the view itself.
143
+ # Return nil, as this method shouldn't output anything to the view itself.
144
144
  nil
145
145
  end
146
146
  end
@@ -121,7 +121,7 @@ module ViewComponent
121
121
  singular_name = ActiveSupport::Inflector.singularize(slot_name)
122
122
 
123
123
  # Define setter for singular names
124
- # e.g. `renders_many :items` allows fetching all tabs with
124
+ # for example `renders_many :items` allows fetching all tabs with
125
125
  # `component.tabs` and setting a tab with `component.tab`
126
126
  define_method singular_name do |*args, &block|
127
127
  set_slot(slot_name, nil, *args, &block)
@@ -181,7 +181,7 @@ module ViewComponent
181
181
  # If callable is a string, we assume it's referencing an internal class
182
182
  slot[:renderable_class_name] = callable
183
183
  elsif callable.respond_to?(:call)
184
- # If slot does not respond to `render_in`, we assume it's a proc,
184
+ # If slot doesn't respond to `render_in`, we assume it's a proc,
185
185
  # define a method, and save a reference to it to call when setting
186
186
  method_name = :"_call_#{slot_name}"
187
187
  define_method method_name, &callable
@@ -256,7 +256,7 @@ module ViewComponent
256
256
  # 1. If this is a `content_area` style sub-component, we will render the
257
257
  # block via the `slot`
258
258
  #
259
- # 2. Since we have to pass block content to components when calling
259
+ # 2. Since we've to pass block content to components when calling
260
260
  # `render`, evaluating the block here would require us to call
261
261
  # `view_context.capture` twice, which is slower
262
262
  slot.__vc_content_block = block if block_given?
@@ -103,7 +103,7 @@ module ViewComponent
103
103
  @controller = old_controller
104
104
  end
105
105
 
106
- # Set the URL for the current request (such as when using request-dependent path helpers):
106
+ # Set the URL of the current request (such as when using request-dependent path helpers):
107
107
  #
108
108
  # ```ruby
109
109
  # with_request_url("/users/42") do
@@ -3,8 +3,8 @@
3
3
  module ViewComponent
4
4
  module VERSION
5
5
  MAJOR = 2
6
- MINOR = 43
7
- PATCH = 1
6
+ MINOR = 47
7
+ PATCH = 0
8
8
 
9
9
  STRING = [MAJOR, MINOR, PATCH].join(".")
10
10
  end
@@ -8,6 +8,7 @@ module ViewComponent
8
8
 
9
9
  autoload :Base
10
10
  autoload :Compiler
11
+ autoload :CompileCache
11
12
  autoload :ComponentError
12
13
  autoload :Instrumentation
13
14
  autoload :Preview
@@ -18,4 +19,13 @@ module ViewComponent
18
19
  autoload :Translatable
19
20
  end
20
21
 
21
- require "view_component/engine" if defined?(Rails::Engine)
22
+ # :nocov:
23
+ if defined?(ViewComponent::Engine)
24
+ ActiveSupport::Deprecation.warn(
25
+ "This manually engine loading is deprecated and will be removed in v3.0.0. " \
26
+ "Remove `require \"view_component/engine\"`."
27
+ )
28
+ elsif defined?(Rails::Engine)
29
+ require "view_component/engine"
30
+ end
31
+ # :nocov:
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: view_component
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.43.1
4
+ version: 2.47.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub Open Source
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-11-09 00:00:00.000000000 Z
11
+ date: 2021-12-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -76,16 +76,16 @@ dependencies:
76
76
  name: bundler
77
77
  requirement: !ruby/object:Gem::Requirement
78
78
  requirements:
79
- - - "~>"
79
+ - - ">="
80
80
  - !ruby/object:Gem::Version
81
- version: '2.2'
81
+ version: 1.15.0
82
82
  type: :development
83
83
  prerelease: false
84
84
  version_requirements: !ruby/object:Gem::Requirement
85
85
  requirements:
86
- - - "~>"
86
+ - - ">="
87
87
  - !ruby/object:Gem::Version
88
- version: '2.2'
88
+ version: 1.15.0
89
89
  - !ruby/object:Gem::Dependency
90
90
  name: erb_lint
91
91
  requirement: !ruby/object:Gem::Requirement
@@ -268,7 +268,7 @@ dependencies:
268
268
  - - ">="
269
269
  - !ruby/object:Gem::Version
270
270
  version: '0'
271
- description:
271
+ description:
272
272
  email:
273
273
  - opensource+view_component@github.com
274
274
  executables: []
@@ -295,6 +295,7 @@ files:
295
295
  - lib/rails/generators/erb/templates/component.html.erb.tt
296
296
  - lib/rails/generators/haml/component_generator.rb
297
297
  - lib/rails/generators/haml/templates/component.html.haml.tt
298
+ - lib/rails/generators/locale/component_generator.rb
298
299
  - lib/rails/generators/preview/component_generator.rb
299
300
  - lib/rails/generators/preview/templates/component_preview.rb.tt
300
301
  - lib/rails/generators/rspec/component_generator.rb
@@ -341,7 +342,7 @@ licenses:
341
342
  - MIT
342
343
  metadata:
343
344
  allowed_push_host: https://rubygems.org
344
- post_install_message:
345
+ post_install_message:
345
346
  rdoc_options: []
346
347
  require_paths:
347
348
  - lib
@@ -356,8 +357,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
356
357
  - !ruby/object:Gem::Version
357
358
  version: '0'
358
359
  requirements: []
359
- rubygems_version: 3.2.22
360
- signing_key:
360
+ rubygems_version: 3.1.2
361
+ signing_key:
361
362
  specification_version: 4
362
363
  summary: View components for Rails
363
364
  test_files: []