view_component 2.43.1 → 2.47.0

Sign up to get free protection for your applications and to get access to all the features.

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: []