view_component 3.22.0 → 4.0.0.alpha1

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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/view_component/preview_actions.rb +2 -9
  3. data/app/controllers/view_components_system_test_controller.rb +17 -20
  4. data/app/views/test_mailer/test_asset_email.html.erb +1 -0
  5. data/app/views/view_components/preview.html.erb +1 -9
  6. data/docs/CHANGELOG.md +127 -1
  7. data/lib/view_component/base.rb +41 -61
  8. data/lib/view_component/collection.rb +11 -17
  9. data/lib/view_component/compiler.rb +50 -74
  10. data/lib/view_component/config.rb +0 -21
  11. data/lib/view_component/deprecation.rb +1 -1
  12. data/lib/view_component/engine.rb +3 -82
  13. data/lib/view_component/errors.rb +16 -22
  14. data/lib/view_component/inline_template.rb +2 -3
  15. data/lib/view_component/instrumentation.rb +4 -10
  16. data/lib/view_component/preview.rb +3 -10
  17. data/lib/view_component/request_details.rb +30 -0
  18. data/lib/view_component/slot.rb +2 -5
  19. data/lib/view_component/slotable.rb +31 -39
  20. data/lib/view_component/system_test_helpers.rb +1 -2
  21. data/lib/view_component/template.rb +106 -83
  22. data/lib/view_component/test_helpers.rb +22 -42
  23. data/lib/view_component/translatable.rb +24 -23
  24. data/lib/view_component/use_helpers.rb +3 -4
  25. data/lib/view_component/version.rb +3 -3
  26. data/lib/view_component.rb +0 -1
  27. metadata +73 -220
  28. data/app/assets/vendor/prism.css +0 -4
  29. data/app/assets/vendor/prism.min.js +0 -12
  30. data/app/helpers/preview_helper.rb +0 -85
  31. data/app/views/view_components/_preview_source.html.erb +0 -17
  32. data/lib/rails/generators/abstract_generator.rb +0 -56
  33. data/lib/rails/generators/component/USAGE +0 -13
  34. data/lib/rails/generators/component/component_generator.rb +0 -67
  35. data/lib/rails/generators/component/templates/component.rb.tt +0 -16
  36. data/lib/rails/generators/erb/component_generator.rb +0 -33
  37. data/lib/rails/generators/erb/templates/component.html.erb.tt +0 -1
  38. data/lib/rails/generators/haml/component_generator.rb +0 -22
  39. data/lib/rails/generators/haml/templates/component.html.haml.tt +0 -1
  40. data/lib/rails/generators/locale/component_generator.rb +0 -46
  41. data/lib/rails/generators/preview/component_generator.rb +0 -39
  42. data/lib/rails/generators/preview/templates/component_preview.rb.tt +0 -9
  43. data/lib/rails/generators/rspec/component_generator.rb +0 -31
  44. data/lib/rails/generators/rspec/templates/component_spec.rb.tt +0 -15
  45. data/lib/rails/generators/slim/component_generator.rb +0 -22
  46. data/lib/rails/generators/slim/templates/component.html.slim.tt +0 -1
  47. data/lib/rails/generators/stimulus/component_generator.rb +0 -44
  48. data/lib/rails/generators/stimulus/templates/component_controller.js.tt +0 -7
  49. data/lib/rails/generators/stimulus/templates/component_controller.ts.tt +0 -9
  50. data/lib/rails/generators/tailwindcss/component_generator.rb +0 -11
  51. data/lib/rails/generators/tailwindcss/templates/component.html.erb.tt +0 -1
  52. data/lib/rails/generators/test_unit/component_generator.rb +0 -20
  53. data/lib/rails/generators/test_unit/templates/component_test.rb.tt +0 -12
  54. data/lib/view_component/component_error.rb +0 -6
  55. data/lib/view_component/docs_builder_component.html.erb +0 -22
  56. data/lib/view_component/docs_builder_component.rb +0 -96
  57. data/lib/view_component/rails/tasks/view_component.rake +0 -20
  58. data/lib/view_component/render_component_helper.rb +0 -10
  59. data/lib/view_component/render_component_to_string_helper.rb +0 -9
  60. data/lib/view_component/render_monkey_patch.rb +0 -13
  61. data/lib/view_component/render_to_string_monkey_patch.rb +0 -13
  62. data/lib/view_component/rendering_component_helper.rb +0 -9
  63. data/lib/view_component/rendering_monkey_patch.rb +0 -13
  64. data/lib/yard/mattr_accessor_handler.rb +0 -19
@@ -8,7 +8,7 @@ module ViewComponent
8
8
  # * true (a blocking mode which ensures thread safety when redefining the `call` method for components,
9
9
  # default in Rails development and test mode)
10
10
  # * false(a non-blocking mode, default in Rails production mode)
11
- class_attribute :development_mode, default: false
11
+ class_attribute :__vc_development_mode, default: false
12
12
 
13
13
  def initialize(component)
14
14
  @component = component
@@ -30,8 +30,8 @@ module ViewComponent
30
30
 
31
31
  gather_templates
32
32
 
33
- if self.class.development_mode && @templates.any?(&:requires_compiled_superclass?)
34
- @component.superclass.compile(raise_errors: raise_errors)
33
+ if self.class.__vc_development_mode && @templates.any?(&:requires_compiled_superclass?)
34
+ @component.superclass.__vc_compile(raise_errors: raise_errors)
35
35
  end
36
36
 
37
37
  if template_errors.present?
@@ -42,19 +42,36 @@ module ViewComponent
42
42
  end
43
43
 
44
44
  if raise_errors
45
- @component.validate_initialization_parameters!
46
- @component.validate_collection_parameter!
45
+ @component.__vc_validate_initialization_parameters!
46
+ @component.__vc_validate_collection_parameter!
47
47
  end
48
48
 
49
49
  define_render_template_for
50
50
 
51
51
  @component.register_default_slots
52
- @component.build_i18n_backend
52
+ @component.__vc_build_i18n_backend
53
53
 
54
54
  CompileCache.register(@component)
55
55
  end
56
56
  end
57
57
 
58
+ # @return all matching compiled templates, in priority order based on the requested details from LookupContext
59
+ #
60
+ # @param [ActionView::TemplateDetails::Requested] requested_details i.e. locales, formats, variants
61
+ def find_templates_for(requested_details)
62
+ filtered_templates = @templates.select do |template|
63
+ template.details.matches?(requested_details)
64
+ end
65
+
66
+ if filtered_templates.count > 1
67
+ filtered_templates.sort_by! do |template|
68
+ template.details.sort_key_for(requested_details)
69
+ end
70
+ end
71
+
72
+ filtered_templates
73
+ end
74
+
58
75
  private
59
76
 
60
77
  attr_reader :templates
@@ -64,40 +81,25 @@ module ViewComponent
64
81
  template.compile_to_component
65
82
  end
66
83
 
67
- method_body =
68
- if @templates.one?
69
- @templates.first.safe_method_name_call
70
- elsif (template = @templates.find(&:inline?))
71
- template.safe_method_name_call
72
- else
73
- branches = []
74
-
75
- @templates.each do |template|
76
- conditional =
77
- if template.inline_call?
78
- "variant&.to_sym == #{template.variant.inspect}"
79
- else
80
- [
81
- template.default_format? ? "(format == #{ViewComponent::Base::VC_INTERNAL_DEFAULT_FORMAT.inspect} || format.nil?)" : "format == #{template.format.inspect}",
82
- template.variant.nil? ? "variant.nil?" : "variant&.to_sym == #{template.variant.inspect}"
83
- ].join(" && ")
84
- end
85
-
86
- branches << [conditional, template.safe_method_name_call]
87
- end
84
+ @component.silence_redefinition_of_method(:render_template_for)
88
85
 
89
- out = branches.each_with_object(+"") do |(conditional, branch_body), memo|
90
- memo << "#{(!memo.present?) ? "if" : "elsif"} #{conditional}\n #{branch_body}\n"
86
+ if @templates.one?
87
+ template = @templates.first
88
+ safe_call = template.safe_method_name_call
89
+ @component.define_method(:render_template_for) do |_|
90
+ @current_template = template
91
+ instance_exec(&safe_call)
92
+ end
93
+ else
94
+ compiler = self
95
+ @component.define_method(:render_template_for) do |details|
96
+ if (@current_template = compiler.find_templates_for(details).first)
97
+ instance_exec(&@current_template.safe_method_name_call)
98
+ else
99
+ raise MissingTemplateError.new(self.class.name, details)
91
100
  end
92
- out << "else\n #{templates.find { _1.variant.nil? && _1.default_format? }.safe_method_name_call}\nend"
93
101
  end
94
-
95
- @component.silence_redefinition_of_method(:render_template_for)
96
- @component.class_eval <<-RUBY, __FILE__, __LINE__ + 1
97
- def render_template_for(variant = nil, format = nil)
98
- #{method_body}
99
102
  end
100
- RUBY
101
103
  end
102
104
 
103
105
  def template_errors
@@ -168,30 +170,18 @@ module ViewComponent
168
170
 
169
171
  def gather_templates
170
172
  @templates ||=
171
- begin
173
+ if @component.inline_template.present?
174
+ [Template::Inline.new(
175
+ component: @component,
176
+ inline_template: @component.inline_template
177
+ )]
178
+ else
179
+ path_parser = ActionView::Resolver::PathParser.new
172
180
  templates = @component.sidecar_files(
173
181
  ActionView::Template.template_handler_extensions
174
182
  ).map do |path|
175
- # Extract format and variant from template filename
176
- this_format, variant =
177
- File
178
- .basename(path) # "variants_component.html+mini.watch.erb"
179
- .split(".")[1..-2] # ["html+mini", "watch"]
180
- .join(".") # "html+mini.watch"
181
- .split("+") # ["html", "mini.watch"]
182
- .map(&:to_sym) # [:html, :"mini.watch"]
183
-
184
- out = Template.new(
185
- component: @component,
186
- type: :file,
187
- path: path,
188
- lineno: 0,
189
- extension: path.split(".").last,
190
- this_format: this_format.to_s.split(".").last&.to_sym, # strip locale from this_format, see #2113
191
- variant: variant
192
- )
193
-
194
- out
183
+ details = path_parser.parse(path).details
184
+ Template::File.new(component: @component, path: path, details: details)
195
185
  end
196
186
 
197
187
  component_instance_methods_on_self = @component.instance_methods(false)
@@ -201,24 +191,10 @@ module ViewComponent
201
191
  ).flat_map { |ancestor| ancestor.instance_methods(false).grep(/^call(_|$)/) }
202
192
  .uniq
203
193
  .each do |method_name|
204
- templates << Template.new(
205
- component: @component,
206
- type: :inline_call,
207
- this_format: ViewComponent::Base::VC_INTERNAL_DEFAULT_FORMAT,
208
- variant: method_name.to_s.include?("call_") ? method_name.to_s.sub("call_", "").to_sym : nil,
209
- method_name: method_name,
210
- defined_on_self: component_instance_methods_on_self.include?(method_name)
211
- )
212
- end
213
-
214
- if @component.inline_template.present?
215
- templates << Template.new(
194
+ templates << Template::InlineCall.new(
216
195
  component: @component,
217
- type: :inline,
218
- path: @component.inline_template.path,
219
- lineno: @component.inline_template.lineno,
220
- source: @component.inline_template.source.dup,
221
- extension: @component.inline_template.language
196
+ method_name: method_name,
197
+ defined_on_self: component_instance_methods_on_self.include?(method_name)
222
198
  )
223
199
  end
224
200
 
@@ -15,10 +15,7 @@ module ViewComponent
15
15
  generate: default_generate_options,
16
16
  preview_controller: "ViewComponentsController",
17
17
  preview_route: "/rails/view_components",
18
- show_previews_source: false,
19
18
  instrumentation_enabled: false,
20
- use_deprecated_instrumentation_name: true,
21
- render_monkey_patch_enabled: true,
22
19
  view_component_path: "app/components",
23
20
  component_parent_class: nil,
24
21
  show_previews: Rails.env.development? || Rails.env.test?,
@@ -109,29 +106,11 @@ module ViewComponent
109
106
  # The entry route for component previews.
110
107
  # Defaults to `"/rails/view_components"`.
111
108
 
112
- # @!attribute show_previews_source
113
- # @return [Boolean]
114
- # Whether to display source code previews in component previews.
115
- # Defaults to `false`.
116
-
117
109
  # @!attribute instrumentation_enabled
118
110
  # @return [Boolean]
119
111
  # Whether ActiveSupport notifications are enabled.
120
112
  # Defaults to `false`.
121
113
 
122
- # @!attribute use_deprecated_instrumentation_name
123
- # @return [Boolean]
124
- # Whether ActiveSupport Notifications use the private name `"!render.view_component"`
125
- # or are made more publicly available via `"render.view_component"`.
126
- # Will default to `false` in next major version.
127
- # Defaults to `true`.
128
-
129
- # @!attribute render_monkey_patch_enabled
130
- # @return [Boolean] Whether the #render method should be monkey patched.
131
- # If this is disabled, use `#render_component` or
132
- # `#render_component_to_string` instead.
133
- # Defaults to `true`.
134
-
135
114
  # @!attribute view_component_path
136
115
  # @return [String]
137
116
  # The path in which components, their templates, and their sidecars should
@@ -3,6 +3,6 @@
3
3
  require "active_support/deprecation"
4
4
 
5
5
  module ViewComponent
6
- DEPRECATION_HORIZON = "4.0.0"
6
+ DEPRECATION_HORIZON = "5.0.0"
7
7
  Deprecation = ActiveSupport::Deprecation.new(DEPRECATION_HORIZON, "ViewComponent")
8
8
  end
@@ -29,11 +29,10 @@ module ViewComponent
29
29
  initializer "view_component.set_configs" do |app|
30
30
  options = app.config.view_component
31
31
 
32
- %i[generate preview_controller preview_route show_previews_source].each do |config_option|
32
+ %i[generate preview_controller preview_route].each do |config_option|
33
33
  options[config_option] ||= ViewComponent::Base.public_send(config_option)
34
34
  end
35
35
  options.instrumentation_enabled = false if options.instrumentation_enabled.nil?
36
- options.render_monkey_patch_enabled = true if options.render_monkey_patch_enabled.nil?
37
36
  options.show_previews = (Rails.env.development? || Rails.env.test?) if options.show_previews.nil?
38
37
 
39
38
  if options.show_previews
@@ -41,40 +40,22 @@ module ViewComponent
41
40
  options.preview_paths << "#{Rails.root}/test/components/previews" if defined?(Rails.root) && Dir.exist?(
42
41
  "#{Rails.root}/test/components/previews"
43
42
  )
44
-
45
- if options.show_previews_source
46
- require "method_source"
47
-
48
- app.config.to_prepare do
49
- MethodSource.instance_variable_set(:@lines_for_file, {})
50
- end
51
- end
52
43
  end
53
44
  end
54
45
 
55
46
  initializer "view_component.enable_instrumentation" do |app|
56
47
  ActiveSupport.on_load(:view_component) do
57
48
  if app.config.view_component.instrumentation_enabled.present?
58
- # :nocov: Re-executing the below in tests duplicates initializers and causes order-dependent failures.
59
49
  ViewComponent::Base.prepend(ViewComponent::Instrumentation)
60
- if app.config.view_component.use_deprecated_instrumentation_name
61
- ViewComponent::Deprecation.deprecation_warning(
62
- "!render.view_component",
63
- "Use the new instrumentation key `render.view_component` instead. See https://viewcomponent.org/guide/instrumentation.html"
64
- )
65
- end
66
- # :nocov:
67
50
  end
68
51
  end
69
52
  end
70
53
 
71
- # :nocov:
72
54
  initializer "view_component.enable_capture_patch" do |app|
73
55
  ActiveSupport.on_load(:view_component) do
74
56
  ActionView::Base.include(ViewComponent::CaptureCompatibility) if app.config.view_component.capture_compatibility_patch_enabled
75
57
  end
76
58
  end
77
- # :nocov:
78
59
 
79
60
  initializer "view_component.set_autoload_paths" do |app|
80
61
  options = app.config.view_component
@@ -87,62 +68,12 @@ module ViewComponent
87
68
 
88
69
  initializer "view_component.eager_load_actions" do
89
70
  ActiveSupport.on_load(:after_initialize) do
90
- ViewComponent::Base.descendants.each(&:compile) if Rails.application.config.eager_load
91
- end
92
- end
93
-
94
- initializer "view_component.monkey_patch_render" do |app|
95
- next if Rails.version.to_f >= 6.1 || !app.config.view_component.render_monkey_patch_enabled
96
-
97
- # :nocov:
98
- ViewComponent::Deprecation.deprecation_warning("Monkey patching `render`", "ViewComponent 4.0 will remove the `render` monkey patch")
99
-
100
- ActiveSupport.on_load(:action_view) do
101
- require "view_component/render_monkey_patch"
102
- ActionView::Base.prepend ViewComponent::RenderMonkeyPatch
103
- end
104
-
105
- ActiveSupport.on_load(:action_controller) do
106
- require "view_component/rendering_monkey_patch"
107
- require "view_component/render_to_string_monkey_patch"
108
- ActionController::Base.prepend ViewComponent::RenderingMonkeyPatch
109
- ActionController::Base.prepend ViewComponent::RenderToStringMonkeyPatch
110
- end
111
- # :nocov:
112
- end
113
-
114
- initializer "view_component.include_render_component" do |_app|
115
- next if Rails.version.to_f >= 6.1
116
-
117
- # :nocov:
118
- ViewComponent::Deprecation.deprecation_warning("using `render_component`", "ViewComponent 4.0 will remove `render_component`")
119
-
120
- ActiveSupport.on_load(:action_view) do
121
- require "view_component/render_component_helper"
122
- ActionView::Base.include ViewComponent::RenderComponentHelper
123
- end
124
-
125
- ActiveSupport.on_load(:action_controller) do
126
- require "view_component/rendering_component_helper"
127
- require "view_component/render_component_to_string_helper"
128
- ActionController::Base.include ViewComponent::RenderingComponentHelper
129
- ActionController::Base.include ViewComponent::RenderComponentToStringHelper
130
- end
131
- # :nocov:
132
- end
133
-
134
- initializer "static assets" do |app|
135
- if serve_static_preview_assets?(app.config)
136
- app.middleware.use(::ActionDispatch::Static, "#{root}/app/assets/vendor")
71
+ ViewComponent::Base.descendants.each(&:__vc_compile) if Rails.application.config.eager_load
137
72
  end
138
73
  end
139
74
 
140
- def serve_static_preview_assets?(app_config)
141
- app_config.view_component.show_previews && app_config.public_file_server.enabled
142
- end
143
-
144
75
  initializer "compiler mode" do |_app|
145
- ViewComponent::Compiler.development_mode = (Rails.env.development? || Rails.env.test?)
76
+ ViewComponent::Compiler.__vc_development_mode = (Rails.env.development? || Rails.env.test?)
146
77
  end
147
78
 
148
79
  config.after_initialize do |app|
@@ -174,16 +105,6 @@ module ViewComponent
174
105
  end
175
106
  end
176
107
 
177
- # :nocov:
178
- if RUBY_VERSION < "3.2.0"
179
- ViewComponent::Deprecation.deprecation_warning("Support for Ruby versions < 3.2.0", "ViewComponent v4 will remove support for Ruby versions < 3.2.0 no earlier than April 1, 2025")
180
- end
181
-
182
- if Rails.version.to_f < 7.1
183
- ViewComponent::Deprecation.deprecation_warning("Support for Rails versions < 7.1", "ViewComponent v4 will remove support for Rails versions < 7.1 no earlier than April 1, 2025")
184
- end
185
- # :nocov:
186
-
187
108
  app.executor.to_run :before do
188
109
  CompileCache.invalidate! unless ActionView::Base.cache_template_loading
189
110
  end
@@ -38,6 +38,22 @@ module ViewComponent
38
38
  end
39
39
  end
40
40
 
41
+ class MissingTemplateError < StandardError
42
+ MESSAGE =
43
+ "No templates for COMPONENT match the request DETAIL.\n\n" \
44
+ "To fix this issue, provide a suitable template."
45
+
46
+ def initialize(component, request_detail)
47
+ detail = {
48
+ locale: request_detail.locale,
49
+ formats: request_detail.formats,
50
+ variants: request_detail.variants,
51
+ handlers: request_detail.handlers
52
+ }
53
+ super(MESSAGE.gsub("COMPONENT", component).gsub("DETAIL", detail.inspect))
54
+ end
55
+ end
56
+
41
57
  class DuplicateContentError < StandardError
42
58
  MESSAGE =
43
59
  "It looks like a block was provided after calling `with_content` on COMPONENT, " \
@@ -202,28 +218,6 @@ module ViewComponent
202
218
  "`#controller` to a [`#before_render` method](https://viewcomponent.org/api.html#before_render--void)."
203
219
  end
204
220
 
205
- # :nocov:
206
- class NoMatchingTemplatesForPreviewError < StandardError
207
- MESSAGE = "Found 0 matches for templates for TEMPLATE_IDENTIFIER."
208
-
209
- def initialize(template_identifier)
210
- super(MESSAGE.gsub("TEMPLATE_IDENTIFIER", template_identifier))
211
- end
212
- end
213
-
214
- class MultipleMatchingTemplatesForPreviewError < StandardError
215
- MESSAGE = "Found multiple templates for TEMPLATE_IDENTIFIER."
216
-
217
- def initialize(template_identifier)
218
- super(MESSAGE.gsub("TEMPLATE_IDENTIFIER", template_identifier))
219
- end
220
- end
221
- # :nocov:
222
-
223
- class SystemTestControllerOnlyAllowedInTestError < BaseError
224
- MESSAGE = "ViewComponent SystemTest controller must only be called in a test environment for security reasons."
225
- end
226
-
227
221
  class SystemTestControllerNefariousPathError < BaseError
228
222
  MESSAGE = "ViewComponent SystemTest controller attempted to load a file outside of the expected directory."
229
223
  end
@@ -32,7 +32,6 @@ module ViewComponent # :nodoc:
32
32
 
33
33
  @__vc_inline_template_defined = true
34
34
  end
35
- ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
36
35
 
37
36
  def respond_to_missing?(method, include_all = false)
38
37
  method.end_with?("_template") || super
@@ -42,13 +41,13 @@ module ViewComponent # :nodoc:
42
41
  @__vc_inline_template if defined?(@__vc_inline_template)
43
42
  end
44
43
 
45
- def inline_template_language
44
+ def __vc_inline_template_language
46
45
  @__vc_inline_template_language if defined?(@__vc_inline_template_language)
47
46
  end
48
47
 
49
48
  def inherited(subclass)
50
49
  super
51
- subclass.instance_variable_set(:@__vc_inline_template_language, inline_template_language)
50
+ subclass.instance_variable_set(:@__vc_inline_template_language, __vc_inline_template_language)
52
51
  end
53
52
  end
54
53
  end
@@ -5,12 +5,14 @@ require "active_support/notifications"
5
5
  module ViewComponent # :nodoc:
6
6
  module Instrumentation
7
7
  def self.included(mod)
8
- mod.prepend(self) unless ancestors.include?(ViewComponent::Instrumentation)
8
+ mod.prepend(self) unless self <= ViewComponent::Instrumentation
9
9
  end
10
10
 
11
11
  def render_in(view_context, &block)
12
+ return super if !Rails.application.config.view_component.instrumentation_enabled.present?
13
+
12
14
  ActiveSupport::Notifications.instrument(
13
- notification_name,
15
+ "render.view_component",
14
16
  {
15
17
  name: self.class.name,
16
18
  identifier: self.class.identifier
@@ -19,13 +21,5 @@ module ViewComponent # :nodoc:
19
21
  super
20
22
  end
21
23
  end
22
-
23
- private
24
-
25
- def notification_name
26
- return "!render.view_component" if ViewComponent::Base.config.use_deprecated_instrumentation_name
27
-
28
- "render.view_component"
29
- end
30
24
  end
31
25
  end
@@ -30,12 +30,10 @@ module ViewComponent # :nodoc:
30
30
  }
31
31
  end
32
32
 
33
- alias_method :render_component, :render
34
-
35
33
  class << self
36
34
  # Returns all component preview classes.
37
35
  def all
38
- load_previews
36
+ __vc_load_previews
39
37
 
40
38
  descendants
41
39
  end
@@ -94,13 +92,8 @@ module ViewComponent # :nodoc:
94
92
  .sub(/\..*$/, "")
95
93
  end
96
94
 
97
- # Returns the method body for the example from the preview file.
98
- def preview_source(example)
99
- source = instance_method(example.to_sym).source.split("\n")
100
- source[1...(source.size - 1)].join("\n")
101
- end
102
-
103
- def load_previews
95
+ # @private
96
+ def __vc_load_previews
104
97
  Array(preview_paths).each do |preview_path|
105
98
  Dir["#{preview_path}/**/*preview.rb"].sort.each { |file| require_dependency file }
106
99
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ # LookupContext computes and encapsulates @details for each request
5
+ # so that it doesn't need to be recomputed on each partial render.
6
+ # This data is wrapped in ActionView::TemplateDetails::Requested and
7
+ # used by instances of ActionView::Resolver to choose which template
8
+ # best matches the request.
9
+ #
10
+ # ActionView considers this logic internal to template/partial resolution.
11
+ # We're exposing it to the compiler via `refine` so that ViewComponent
12
+ # can match Rails' template picking logic.
13
+ module RequestDetails
14
+ refine ActionView::LookupContext do
15
+ # Return an abstraction for matching and sorting available templates
16
+ # based on the current lookup context details.
17
+ #
18
+ # @return ActionView::TemplateDetails::Requested
19
+ # @see ActionView::LookupContext#detail_args_for
20
+ # @see ActionView::FileSystemResolver#_find_all
21
+ def vc_requested_details(user_details = {})
22
+ # The hash `user_details` would normally be the standard arguments that
23
+ # `render` accepts, but there's currently no mechanism for users to
24
+ # provide these when calling render on a ViewComponent.
25
+ details, cached = detail_args_for(user_details)
26
+ cached || ActionView::TemplateDetails::Requested.new(**details)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -101,15 +101,12 @@ module ViewComponent
101
101
  # end
102
102
  # end
103
103
  #
104
- def method_missing(symbol, *args, &block)
105
- @__vc_component_instance.public_send(symbol, *args, &block)
104
+ def method_missing(symbol, *args, **kwargs, &block)
105
+ @__vc_component_instance.public_send(symbol, *args, **kwargs, &block)
106
106
  end
107
- ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
108
107
 
109
108
  def html_safe?
110
- # :nocov:
111
109
  to_s.html_safe?
112
- # :nocov:
113
110
  end
114
111
 
115
112
  def respond_to_missing?(symbol, include_all = false)