view_component 3.23.2 → 4.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/controllers/concerns/view_component/preview_actions.rb +11 -14
- data/app/controllers/view_components_system_test_controller.rb +25 -19
- data/app/views/test_mailer/test_asset_email.html.erb +1 -0
- data/app/views/test_mailer/test_url_email.html.erb +1 -0
- data/app/views/view_components/preview.html.erb +1 -9
- data/docs/CHANGELOG.md +612 -108
- data/lib/{rails/generators → generators/view_component}/abstract_generator.rb +2 -2
- data/lib/{rails/generators → generators/view_component}/component/component_generator.rb +17 -4
- data/lib/{rails/generators → generators/view_component}/component/templates/component.rb.tt +6 -1
- data/lib/{rails/generators/erb/component_generator.rb → generators/view_component/erb/erb_generator.rb} +4 -3
- data/lib/{rails/generators/haml/component_generator.rb → generators/view_component/haml/haml_generator.rb} +3 -3
- data/lib/{rails/generators/locale/component_generator.rb → generators/view_component/locale/locale_generator.rb} +3 -3
- data/lib/{rails/generators/preview/component_generator.rb → generators/view_component/preview/preview_generator.rb} +3 -3
- data/lib/{rails/generators/rspec/component_generator.rb → generators/view_component/rspec/rspec_generator.rb} +3 -3
- data/lib/{rails/generators/slim/component_generator.rb → generators/view_component/slim/slim_generator.rb} +3 -3
- data/lib/{rails/generators/stimulus/component_generator.rb → generators/view_component/stimulus/stimulus_generator.rb} +3 -3
- data/lib/generators/view_component/tailwindcss/tailwindcss_generator.rb +11 -0
- data/lib/{rails/generators/test_unit/component_generator.rb → generators/view_component/test_unit/test_unit_generator.rb} +2 -2
- data/lib/view_component/base.rb +252 -163
- data/lib/view_component/collection.rb +33 -35
- data/lib/view_component/compiler.rb +55 -80
- data/lib/view_component/config.rb +55 -85
- data/lib/view_component/configurable.rb +19 -3
- data/lib/view_component/deprecation.rb +1 -1
- data/lib/view_component/engine.rb +46 -106
- data/lib/view_component/errors.rb +16 -34
- data/lib/view_component/inline_template.rb +4 -4
- data/lib/view_component/instrumentation.rb +21 -14
- data/lib/view_component/preview.rb +7 -12
- data/lib/view_component/request_details.rb +30 -0
- data/lib/view_component/slot.rb +7 -15
- data/lib/view_component/slotable.rb +94 -80
- data/lib/view_component/system_spec_helpers.rb +11 -0
- data/lib/view_component/system_test_helpers.rb +2 -2
- data/lib/view_component/template.rb +159 -83
- data/lib/view_component/test_helpers.rb +67 -50
- data/lib/view_component/translatable.rb +33 -32
- data/lib/view_component/version.rb +3 -3
- data/lib/view_component.rb +9 -6
- metadata +36 -556
- data/app/assets/vendor/prism.css +0 -4
- data/app/assets/vendor/prism.min.js +0 -12
- data/app/helpers/preview_helper.rb +0 -85
- data/app/views/view_components/_preview_source.html.erb +0 -17
- data/lib/rails/generators/tailwindcss/component_generator.rb +0 -11
- data/lib/view_component/capture_compatibility.rb +0 -44
- data/lib/view_component/component_error.rb +0 -6
- data/lib/view_component/rails/tasks/view_component.rake +0 -20
- data/lib/view_component/render_component_helper.rb +0 -10
- data/lib/view_component/render_component_to_string_helper.rb +0 -9
- data/lib/view_component/render_monkey_patch.rb +0 -13
- data/lib/view_component/render_to_string_monkey_patch.rb +0 -13
- data/lib/view_component/rendering_component_helper.rb +0 -9
- data/lib/view_component/rendering_monkey_patch.rb +0 -13
- data/lib/view_component/slotable_default.rb +0 -20
- data/lib/view_component/use_helpers.rb +0 -42
- /data/lib/{rails/generators → generators/view_component}/erb/templates/component.html.erb.tt +0 -0
- /data/lib/{rails/generators → generators/view_component}/haml/templates/component.html.haml.tt +0 -0
- /data/lib/{rails/generators → generators/view_component}/preview/templates/component_preview.rb.tt +0 -0
- /data/lib/{rails/generators → generators/view_component}/rspec/templates/component_spec.rb.tt +0 -0
- /data/lib/{rails/generators → generators/view_component}/slim/templates/component.html.slim.tt +0 -0
- /data/lib/{rails/generators → generators/view_component}/stimulus/templates/component_controller.js.tt +0 -0
- /data/lib/{rails/generators → generators/view_component}/stimulus/templates/component_controller.ts.tt +0 -0
- /data/lib/{rails/generators → generators/view_component}/tailwindcss/templates/component.html.erb.tt +0 -0
- /data/lib/{rails/generators → generators/view_component}/test_unit/templates/component_test.rb.tt +0 -0
|
@@ -8,159 +8,109 @@ module ViewComponent
|
|
|
8
8
|
class Engine < Rails::Engine # :nodoc:
|
|
9
9
|
config.view_component = ViewComponent::Config.current
|
|
10
10
|
|
|
11
|
-
if Rails.version.to_f < 8.0
|
|
12
|
-
rake_tasks do
|
|
13
|
-
load "view_component/rails/tasks/view_component.rake"
|
|
14
|
-
end
|
|
15
|
-
else
|
|
16
|
-
initializer "view_component.stats_directories" do |app|
|
|
17
|
-
require "rails/code_statistics"
|
|
18
|
-
|
|
19
|
-
if Rails.root.join(ViewComponent::Base.view_component_path).directory?
|
|
20
|
-
Rails::CodeStatistics.register_directory("ViewComponents", ViewComponent::Base.view_component_path)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
if Rails.root.join("test/components").directory?
|
|
24
|
-
Rails::CodeStatistics.register_directory("ViewComponent tests", "test/components", test_directory: true)
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
11
|
initializer "view_component.set_configs" do |app|
|
|
30
12
|
options = app.config.view_component
|
|
31
13
|
|
|
32
|
-
%i[generate
|
|
14
|
+
%i[generate previews].each do |config_option|
|
|
33
15
|
options[config_option] ||= ViewComponent::Base.public_send(config_option)
|
|
34
16
|
end
|
|
35
17
|
options.instrumentation_enabled = false if options.instrumentation_enabled.nil?
|
|
36
|
-
options.
|
|
37
|
-
options.show_previews = (Rails.env.development? || Rails.env.test?) if options.show_previews.nil?
|
|
18
|
+
options.previews.enabled = (Rails.env.development? || Rails.env.test?) if options.previews.enabled.nil?
|
|
38
19
|
|
|
39
|
-
if options.
|
|
20
|
+
if options.previews.enabled
|
|
40
21
|
# This is still necessary because when `config.view_component` is declared, `Rails.root` is unspecified.
|
|
41
|
-
options.
|
|
22
|
+
options.previews.paths << "#{Rails.root}/test/components/previews" if defined?(Rails.root) && Dir.exist?(
|
|
42
23
|
"#{Rails.root}/test/components/previews"
|
|
43
24
|
)
|
|
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
25
|
end
|
|
53
26
|
end
|
|
54
27
|
|
|
55
28
|
initializer "view_component.enable_instrumentation" do |app|
|
|
56
29
|
ActiveSupport.on_load(:view_component) do
|
|
57
30
|
if app.config.view_component.instrumentation_enabled.present?
|
|
58
|
-
# :nocov: Re-executing the below in tests duplicates initializers and causes order-dependent failures.
|
|
59
31
|
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
32
|
end
|
|
68
33
|
end
|
|
69
34
|
end
|
|
70
35
|
|
|
71
|
-
# :nocov:
|
|
72
|
-
initializer "view_component.enable_capture_patch" do |app|
|
|
73
|
-
ActiveSupport.on_load(:view_component) do
|
|
74
|
-
ActionView::Base.include(ViewComponent::CaptureCompatibility) if app.config.view_component.capture_compatibility_patch_enabled
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
# :nocov:
|
|
78
|
-
|
|
79
36
|
initializer "view_component.set_autoload_paths" do |app|
|
|
80
37
|
options = app.config.view_component
|
|
81
38
|
|
|
82
|
-
if options.
|
|
83
|
-
paths_to_add = options.
|
|
39
|
+
if options.previews.enabled && !options.previews.paths.empty?
|
|
40
|
+
paths_to_add = options.previews.paths - ActiveSupport::Dependencies.autoload_paths
|
|
84
41
|
ActiveSupport::Dependencies.autoload_paths.concat(paths_to_add) if paths_to_add.any?
|
|
85
42
|
end
|
|
86
43
|
end
|
|
87
44
|
|
|
88
|
-
initializer "view_component.
|
|
89
|
-
ActiveSupport.on_load(:
|
|
90
|
-
|
|
45
|
+
initializer "view_component.propshaft_support" do |_app|
|
|
46
|
+
ActiveSupport.on_load(:view_component) do
|
|
47
|
+
if defined?(Propshaft)
|
|
48
|
+
include Propshaft::Helper
|
|
49
|
+
end
|
|
91
50
|
end
|
|
92
51
|
end
|
|
93
52
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
|
53
|
+
config.after_routes_loaded do
|
|
54
|
+
ActiveSupport.on_load(:view_component) do
|
|
55
|
+
if defined?(Sprockets::Rails)
|
|
56
|
+
include Sprockets::Rails::Helper
|
|
104
57
|
|
|
105
|
-
|
|
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
|
|
58
|
+
app = Rails.application
|
|
113
59
|
|
|
114
|
-
|
|
115
|
-
|
|
60
|
+
# Copy relevant config to VC context
|
|
61
|
+
# See: https://github.com/rails/sprockets-rails/blob/266ec49f3c7c96018dd75f9dc4f9b62fe3f7eecf/lib/sprockets/railtie.rb#L245
|
|
62
|
+
self.debug_assets = app.config.assets.debug
|
|
63
|
+
self.digest_assets = app.config.assets.digest
|
|
64
|
+
self.assets_prefix = app.config.assets.prefix
|
|
65
|
+
self.assets_precompile = app.config.assets.precompile
|
|
116
66
|
|
|
117
|
-
|
|
118
|
-
|
|
67
|
+
self.assets_environment = app.assets
|
|
68
|
+
self.assets_manifest = app.assets_manifest
|
|
119
69
|
|
|
120
|
-
|
|
121
|
-
require "view_component/render_component_helper"
|
|
122
|
-
ActionView::Base.include ViewComponent::RenderComponentHelper
|
|
123
|
-
end
|
|
70
|
+
self.resolve_assets_with = app.config.assets.resolve_with
|
|
124
71
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
72
|
+
self.check_precompiled_asset = app.config.assets.check_precompiled_asset
|
|
73
|
+
self.unknown_asset_fallback = app.config.assets.unknown_asset_fallback
|
|
74
|
+
# Expose the app precompiled asset check to the view
|
|
75
|
+
self.precompiled_asset_checker = ->(logical_path) { app.asset_precompiled? logical_path }
|
|
76
|
+
end
|
|
130
77
|
end
|
|
131
|
-
# :nocov:
|
|
132
78
|
end
|
|
133
79
|
|
|
134
|
-
initializer "
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
80
|
+
initializer "view_component.eager_load_actions" do
|
|
81
|
+
ActiveSupport.on_load(:after_initialize) do
|
|
82
|
+
if Rails.application.config.eager_load
|
|
83
|
+
ActiveSupport::Notifications.instrument("compile.view_component") do
|
|
84
|
+
ViewComponent::Base.descendants.each do |component|
|
|
85
|
+
next if component.anonymous?
|
|
139
86
|
|
|
140
|
-
|
|
141
|
-
|
|
87
|
+
component.__vc_compile
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
142
92
|
end
|
|
143
93
|
|
|
144
94
|
initializer "compiler mode" do |_app|
|
|
145
|
-
ViewComponent::Compiler.
|
|
95
|
+
ViewComponent::Compiler.__vc_development_mode = (Rails.env.development? || Rails.env.test?)
|
|
146
96
|
end
|
|
147
97
|
|
|
148
98
|
config.after_initialize do |app|
|
|
149
99
|
options = app.config.view_component
|
|
150
100
|
|
|
151
|
-
if options.
|
|
101
|
+
if options.previews.enabled
|
|
152
102
|
app.routes.prepend do
|
|
153
|
-
preview_controller = options.
|
|
103
|
+
preview_controller = options.previews.controller.sub(/Controller$/, "").underscore
|
|
154
104
|
|
|
155
105
|
get(
|
|
156
|
-
options.
|
|
106
|
+
options.previews.route,
|
|
157
107
|
to: "#{preview_controller}#index",
|
|
158
108
|
as: :preview_view_components,
|
|
159
109
|
internal: true
|
|
160
110
|
)
|
|
161
111
|
|
|
162
112
|
get(
|
|
163
|
-
"#{options.
|
|
113
|
+
"#{options.previews.route}/*path",
|
|
164
114
|
to: "#{preview_controller}#previews",
|
|
165
115
|
as: :preview_view_component,
|
|
166
116
|
internal: true
|
|
@@ -174,16 +124,6 @@ module ViewComponent
|
|
|
174
124
|
end
|
|
175
125
|
end
|
|
176
126
|
|
|
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
127
|
app.executor.to_run :before do
|
|
188
128
|
CompileCache.invalidate! unless ActionView::Base.cache_template_loading
|
|
189
129
|
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, " \
|
|
@@ -49,18 +65,6 @@ module ViewComponent
|
|
|
49
65
|
end
|
|
50
66
|
end
|
|
51
67
|
|
|
52
|
-
class EmptyOrInvalidInitializerError < StandardError
|
|
53
|
-
MESSAGE =
|
|
54
|
-
"The COMPONENT initializer is empty or invalid. " \
|
|
55
|
-
"It must accept the parameter `PARAMETER` to render it as a collection.\n\n" \
|
|
56
|
-
"To fix this issue, update the initializer to accept `PARAMETER`.\n\n" \
|
|
57
|
-
"See [the collections docs](https://viewcomponent.org/guide/collections.html) for more information on rendering collections."
|
|
58
|
-
|
|
59
|
-
def initialize(klass_name, parameter)
|
|
60
|
-
super(MESSAGE.gsub("COMPONENT", klass_name.to_s).gsub("PARAMETER", parameter.to_s))
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
68
|
class MissingCollectionArgumentError < StandardError
|
|
65
69
|
MESSAGE =
|
|
66
70
|
"The initializer for COMPONENT doesn't accept the parameter `PARAMETER`, " \
|
|
@@ -202,28 +206,6 @@ module ViewComponent
|
|
|
202
206
|
"`#controller` to a [`#before_render` method](https://viewcomponent.org/api.html#before_render--void)."
|
|
203
207
|
end
|
|
204
208
|
|
|
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
209
|
class SystemTestControllerNefariousPathError < BaseError
|
|
228
210
|
MESSAGE = "ViewComponent SystemTest controller attempted to load a file outside of the expected directory."
|
|
229
211
|
end
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
module ViewComponent # :nodoc:
|
|
4
4
|
module InlineTemplate
|
|
5
5
|
extend ActiveSupport::Concern
|
|
6
|
+
|
|
6
7
|
Template = Struct.new(:source, :language, :path, :lineno)
|
|
7
8
|
|
|
8
9
|
class_methods do
|
|
@@ -32,23 +33,22 @@ module ViewComponent # :nodoc:
|
|
|
32
33
|
|
|
33
34
|
@__vc_inline_template_defined = true
|
|
34
35
|
end
|
|
35
|
-
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
|
|
36
36
|
|
|
37
37
|
def respond_to_missing?(method, include_all = false)
|
|
38
38
|
method.end_with?("_template") || super
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
def
|
|
41
|
+
def __vc_inline_template
|
|
42
42
|
@__vc_inline_template if defined?(@__vc_inline_template)
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
def
|
|
45
|
+
def __vc_inline_template_language
|
|
46
46
|
@__vc_inline_template_language if defined?(@__vc_inline_template_language)
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def inherited(subclass)
|
|
50
50
|
super
|
|
51
|
-
subclass.instance_variable_set(:@__vc_inline_template_language,
|
|
51
|
+
subclass.instance_variable_set(:@__vc_inline_template_language, __vc_inline_template_language)
|
|
52
52
|
end
|
|
53
53
|
end
|
|
54
54
|
end
|
|
@@ -5,27 +5,34 @@ require "active_support/notifications"
|
|
|
5
5
|
module ViewComponent # :nodoc:
|
|
6
6
|
module Instrumentation
|
|
7
7
|
def self.included(mod)
|
|
8
|
-
mod.prepend(self) unless
|
|
8
|
+
mod.prepend(self) unless self <= ViewComponent::Instrumentation
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
def render_in(view_context, &block)
|
|
11
|
+
def render_in(view_context, **_, &block)
|
|
12
|
+
return super if !Rails.application.config.view_component.instrumentation_enabled.present?
|
|
13
|
+
|
|
14
|
+
payload = {
|
|
15
|
+
name: self.class.name,
|
|
16
|
+
identifier: self.class.identifier,
|
|
17
|
+
view_identifier: nil
|
|
18
|
+
}
|
|
19
|
+
|
|
12
20
|
ActiveSupport::Notifications.instrument(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
name: self.class.name,
|
|
16
|
-
identifier: self.class.identifier
|
|
17
|
-
}
|
|
21
|
+
"render.view_component",
|
|
22
|
+
payload
|
|
18
23
|
) do
|
|
19
|
-
super
|
|
24
|
+
result = super
|
|
25
|
+
payload[:view_identifier] = @__vc_instrumentation_view_identifier
|
|
26
|
+
result
|
|
20
27
|
end
|
|
28
|
+
ensure
|
|
29
|
+
@__vc_instrumentation_view_identifier = nil
|
|
21
30
|
end
|
|
22
31
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"render.view_component"
|
|
32
|
+
def around_render
|
|
33
|
+
result = super
|
|
34
|
+
@__vc_instrumentation_view_identifier = current_template&.path
|
|
35
|
+
result
|
|
29
36
|
end
|
|
30
37
|
end
|
|
31
38
|
end
|
|
@@ -30,18 +30,18 @@ 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
|
-
|
|
36
|
+
__vc_load_previews
|
|
39
37
|
|
|
40
38
|
descendants
|
|
41
39
|
end
|
|
42
40
|
|
|
43
41
|
# Returns the arguments for rendering of the component in its layout
|
|
44
42
|
def render_args(example, params: {})
|
|
43
|
+
raise AbstractController::ActionNotFound, "#{example} is not a valid preview example" unless examples.include?(example.to_s)
|
|
44
|
+
|
|
45
45
|
example_params_names = instance_method(example).parameters.map(&:last)
|
|
46
46
|
provided_params = params.slice(*example_params_names).to_h.symbolize_keys
|
|
47
47
|
result = provided_params.empty? ? new.public_send(example) : new.public_send(example, **provided_params)
|
|
@@ -94,22 +94,17 @@ module ViewComponent # :nodoc:
|
|
|
94
94
|
.sub(/\..*$/, "")
|
|
95
95
|
end
|
|
96
96
|
|
|
97
|
-
#
|
|
98
|
-
def
|
|
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
|
|
97
|
+
# @private
|
|
98
|
+
def __vc_load_previews
|
|
104
99
|
Array(preview_paths).each do |preview_path|
|
|
105
|
-
Dir["#{preview_path}/**/*preview.rb"].sort.each { |file|
|
|
100
|
+
Dir["#{preview_path}/**/*preview.rb"].sort.each { |file| require file }
|
|
106
101
|
end
|
|
107
102
|
end
|
|
108
103
|
|
|
109
104
|
private
|
|
110
105
|
|
|
111
106
|
def preview_paths
|
|
112
|
-
Base.
|
|
107
|
+
Base.previews.paths
|
|
113
108
|
end
|
|
114
109
|
end
|
|
115
110
|
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
|
data/lib/view_component/slot.rb
CHANGED
|
@@ -6,7 +6,7 @@ module ViewComponent
|
|
|
6
6
|
class Slot
|
|
7
7
|
include ViewComponent::WithContentHelper
|
|
8
8
|
|
|
9
|
-
attr_writer :__vc_component_instance, :__vc_content_block, :__vc_content
|
|
9
|
+
attr_writer :__vc_component_instance, :__vc_content_block, :__vc_content, :__vc_content_block_virtual_path
|
|
10
10
|
|
|
11
11
|
def initialize(parent)
|
|
12
12
|
@parent = parent
|
|
@@ -58,13 +58,7 @@ module ViewComponent
|
|
|
58
58
|
if defined?(@__vc_content_block)
|
|
59
59
|
# render_in is faster than `parent.render`
|
|
60
60
|
@__vc_component_instance.render_in(view_context) do |*args|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
block_context = @__vc_content_block.binding.receiver
|
|
64
|
-
|
|
65
|
-
if block_context.class < ActionView::Base
|
|
66
|
-
block_context.capture(*args, &@__vc_content_block)
|
|
67
|
-
else
|
|
61
|
+
@parent.with_captured_virtual_path(@__vc_content_block_virtual_path) do
|
|
68
62
|
@__vc_content_block.call(*args)
|
|
69
63
|
end
|
|
70
64
|
end
|
|
@@ -74,7 +68,9 @@ module ViewComponent
|
|
|
74
68
|
elsif defined?(@__vc_content)
|
|
75
69
|
@__vc_content
|
|
76
70
|
elsif defined?(@__vc_content_block)
|
|
77
|
-
|
|
71
|
+
@parent.with_captured_virtual_path(@__vc_content_block_virtual_path) do
|
|
72
|
+
view_context.capture(&@__vc_content_block)
|
|
73
|
+
end
|
|
78
74
|
elsif defined?(@__vc_content_set_by_with_content)
|
|
79
75
|
@__vc_content_set_by_with_content
|
|
80
76
|
end
|
|
@@ -100,16 +96,12 @@ module ViewComponent
|
|
|
100
96
|
# end
|
|
101
97
|
# end
|
|
102
98
|
# end
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
@__vc_component_instance.public_send(symbol, *args, &block)
|
|
99
|
+
def method_missing(symbol, *args, **kwargs, &block)
|
|
100
|
+
@__vc_component_instance.public_send(symbol, *args, **kwargs, &block)
|
|
106
101
|
end
|
|
107
|
-
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
|
|
108
102
|
|
|
109
103
|
def html_safe?
|
|
110
|
-
# :nocov:
|
|
111
104
|
to_s.html_safe?
|
|
112
|
-
# :nocov:
|
|
113
105
|
end
|
|
114
106
|
|
|
115
107
|
def respond_to_missing?(symbol, include_all = false)
|