view_component 4.0.0 → 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/view_components_system_test_controller.rb +12 -1
- data/docs/CHANGELOG.md +214 -114
- data/lib/generators/view_component/component/component_generator.rb +1 -1
- data/lib/view_component/base.rb +117 -25
- data/lib/view_component/collection.rb +24 -12
- data/lib/view_component/compiler.rb +12 -10
- data/lib/view_component/config.rb +5 -1
- data/lib/view_component/configurable.rb +18 -2
- data/lib/view_component/engine.rb +12 -2
- data/lib/view_component/inline_template.rb +1 -0
- data/lib/view_component/instrumentation.rb +19 -6
- data/lib/view_component/preview.rb +3 -1
- data/lib/view_component/slot.rb +3 -4
- data/lib/view_component/slotable.rb +12 -3
- data/lib/view_component/system_test_helpers.rb +1 -0
- data/lib/view_component/template.rb +55 -2
- data/lib/view_component/test_helpers.rb +37 -13
- data/lib/view_component/version.rb +1 -1
- data/lib/view_component.rb +2 -1
- metadata +15 -7
|
@@ -21,11 +21,39 @@ module ViewComponent
|
|
|
21
21
|
|
|
22
22
|
class File < Template
|
|
23
23
|
def initialize(component:, details:, path:)
|
|
24
|
+
# If the template file has no format (e.g. .erb instead of .html.erb),
|
|
25
|
+
# assume the default format (html).
|
|
26
|
+
if details.format.nil?
|
|
27
|
+
Kernel.warn("WARNING: Template format for #{path} is missing, defaulting to :html.")
|
|
28
|
+
details = ActionView::TemplateDetails.new(details.locale, details.handler, DEFAULT_FORMAT, details.variant)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
@strip_annotation_line = false
|
|
32
|
+
|
|
33
|
+
# Rails 8.1 added a newline to compiled ERB output (rails/rails#53731).
|
|
34
|
+
# Use -1 to compensate for correct line numbers in stack traces.
|
|
35
|
+
# However, negative line numbers cause segfaults when Ruby's coverage
|
|
36
|
+
# is enabled (bugs.ruby-lang.org/issues/19363). In that case, strip the
|
|
37
|
+
# annotation line from compiled source instead.
|
|
38
|
+
lineno =
|
|
39
|
+
if Rails::VERSION::MAJOR >= 8 && Rails::VERSION::MINOR > 0 && details.handler == :erb
|
|
40
|
+
if coverage_running?
|
|
41
|
+
# Can't use negative lineno with coverage (causes segfault on Linux).
|
|
42
|
+
# Strip annotation line if enabled to preserve correct line numbers.
|
|
43
|
+
@strip_annotation_line = ActionView::Base.annotate_rendered_view_with_filenames
|
|
44
|
+
0
|
|
45
|
+
else
|
|
46
|
+
-1
|
|
47
|
+
end
|
|
48
|
+
else
|
|
49
|
+
0
|
|
50
|
+
end
|
|
51
|
+
|
|
24
52
|
super(
|
|
25
53
|
component: component,
|
|
26
54
|
details: details,
|
|
27
55
|
path: path,
|
|
28
|
-
lineno:
|
|
56
|
+
lineno: lineno
|
|
29
57
|
)
|
|
30
58
|
end
|
|
31
59
|
|
|
@@ -37,6 +65,16 @@ module ViewComponent
|
|
|
37
65
|
def source
|
|
38
66
|
::File.read(@path)
|
|
39
67
|
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def compiled_source
|
|
72
|
+
result = super
|
|
73
|
+
# Strip the annotation line to maintain correct line numbers when coverage
|
|
74
|
+
# is running (avoids segfault from negative lineno)
|
|
75
|
+
result = result.partition(";").last if @strip_annotation_line
|
|
76
|
+
result
|
|
77
|
+
end
|
|
40
78
|
end
|
|
41
79
|
|
|
42
80
|
class Inline < Template
|
|
@@ -45,11 +83,22 @@ module ViewComponent
|
|
|
45
83
|
def initialize(component:, inline_template:)
|
|
46
84
|
details = ActionView::TemplateDetails.new(nil, inline_template.language.to_sym, nil, nil)
|
|
47
85
|
|
|
86
|
+
# Rails 8.1 added a newline to compiled ERB output (rails/rails#53731).
|
|
87
|
+
# Subtract 1 to compensate for correct line numbers in stack traces.
|
|
88
|
+
# Inline templates start at line 2+ (defined inside a class), so this
|
|
89
|
+
# won't result in negative line numbers that cause segfaults with coverage.
|
|
90
|
+
lineno =
|
|
91
|
+
if Rails::VERSION::MAJOR >= 8 && Rails::VERSION::MINOR > 0 && details.handler == :erb
|
|
92
|
+
inline_template.lineno - 1
|
|
93
|
+
else
|
|
94
|
+
inline_template.lineno
|
|
95
|
+
end
|
|
96
|
+
|
|
48
97
|
super(
|
|
49
98
|
component: component,
|
|
50
99
|
details: details,
|
|
51
100
|
path: inline_template.path,
|
|
52
|
-
lineno:
|
|
101
|
+
lineno: lineno,
|
|
53
102
|
)
|
|
54
103
|
|
|
55
104
|
@source = inline_template.source.dup
|
|
@@ -108,6 +157,10 @@ module ViewComponent
|
|
|
108
157
|
@component.define_method(safe_method_name, @component.instance_method(@call_method_name))
|
|
109
158
|
end
|
|
110
159
|
|
|
160
|
+
def coverage_running?
|
|
161
|
+
defined?(Coverage) && Coverage.running?
|
|
162
|
+
end
|
|
163
|
+
|
|
111
164
|
def safe_method_name_call
|
|
112
165
|
m = safe_method_name
|
|
113
166
|
proc { send(m) }
|
|
@@ -35,12 +35,23 @@ module ViewComponent
|
|
|
35
35
|
# ```
|
|
36
36
|
#
|
|
37
37
|
# @param component [ViewComponent::Base, ViewComponent::Collection] The instance of the component to be rendered.
|
|
38
|
+
#
|
|
38
39
|
# @return [Nokogiri::HTML5]
|
|
39
40
|
def render_inline(component, **args, &block)
|
|
40
41
|
@page = nil
|
|
41
|
-
@rendered_content =
|
|
42
|
+
@rendered_content = vc_test_view_context.render(component, args, &block)
|
|
42
43
|
|
|
43
|
-
Nokogiri::HTML5.fragment(@rendered_content)
|
|
44
|
+
fragment = Nokogiri::HTML5.fragment(@rendered_content, context: "template")
|
|
45
|
+
@vc_test_view_context = nil
|
|
46
|
+
fragment
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Returns the view context used to render components in tests. Note that the view context
|
|
50
|
+
# is reset after each call to `render_inline`.
|
|
51
|
+
#
|
|
52
|
+
# @return [ActionView::Base]
|
|
53
|
+
def vc_test_view_context
|
|
54
|
+
@vc_test_view_context ||= vc_test_controller.view_context
|
|
44
55
|
end
|
|
45
56
|
|
|
46
57
|
# `JSON.parse`-d component output.
|
|
@@ -61,17 +72,18 @@ module ViewComponent
|
|
|
61
72
|
# assert_text("Hello, World!")
|
|
62
73
|
# ```
|
|
63
74
|
#
|
|
64
|
-
# Note: `#rendered_preview` expects a preview to be defined with the same class
|
|
65
|
-
# name as the calling test, but with `Test` replaced with `Preview`:
|
|
66
|
-
#
|
|
67
|
-
# MyComponentTest -> MyComponentPreview etc.
|
|
68
|
-
#
|
|
69
|
-
# In RSpec, `Preview` is appended to `described_class`.
|
|
70
|
-
#
|
|
71
75
|
# @param name [String] The name of the preview to be rendered.
|
|
72
76
|
# @param from [ViewComponent::Preview] The class of the preview to be rendered.
|
|
73
77
|
# @param params [Hash] Parameters to be passed to the preview.
|
|
78
|
+
#
|
|
74
79
|
# @return [Nokogiri::HTML5]
|
|
80
|
+
#
|
|
81
|
+
# @note `#rendered_preview` expects a preview to be defined with the same class
|
|
82
|
+
# name as the calling test, but with `Test` replaced with `Preview`:
|
|
83
|
+
#
|
|
84
|
+
# MyComponentTest -> MyComponentPreview etc.
|
|
85
|
+
#
|
|
86
|
+
# In RSpec, `Preview` is appended to `described_class`.
|
|
75
87
|
def render_preview(name, from: __vc_test_helpers_preview_class, params: {})
|
|
76
88
|
previews_controller = __vc_test_helpers_build_controller(Rails.application.config.view_component.previews.controller.constantize)
|
|
77
89
|
|
|
@@ -103,7 +115,7 @@ module ViewComponent
|
|
|
103
115
|
# ```
|
|
104
116
|
def render_in_view_context(...)
|
|
105
117
|
@page = nil
|
|
106
|
-
@rendered_content =
|
|
118
|
+
@rendered_content = vc_test_view_context.instance_exec(...)
|
|
107
119
|
Nokogiri::HTML5.fragment(@rendered_content)
|
|
108
120
|
end
|
|
109
121
|
|
|
@@ -115,7 +127,7 @@ module ViewComponent
|
|
|
115
127
|
# end
|
|
116
128
|
# ```
|
|
117
129
|
#
|
|
118
|
-
# @param variants [Symbol
|
|
130
|
+
# @param variants [Array<Symbol>] The variants to be set for the provided block.
|
|
119
131
|
def with_variant(*variants)
|
|
120
132
|
old_variants = vc_test_controller.view_context.lookup_context.variants
|
|
121
133
|
|
|
@@ -152,7 +164,7 @@ module ViewComponent
|
|
|
152
164
|
# end
|
|
153
165
|
# ```
|
|
154
166
|
#
|
|
155
|
-
# @param formats [Symbol
|
|
167
|
+
# @param formats [Array<Symbol>] The format(s) to be set for the provided block.
|
|
156
168
|
def with_format(*formats)
|
|
157
169
|
old_formats = vc_test_controller.view_context.lookup_context.formats
|
|
158
170
|
|
|
@@ -186,10 +198,19 @@ module ViewComponent
|
|
|
186
198
|
# end
|
|
187
199
|
# ```
|
|
188
200
|
#
|
|
201
|
+
# To specify a protocol, pass the protocol param:
|
|
202
|
+
#
|
|
203
|
+
# ```ruby
|
|
204
|
+
# with_request_url("/users/42", protocol: :https) do
|
|
205
|
+
# render_inline(MyComponent.new)
|
|
206
|
+
# end
|
|
207
|
+
# ```
|
|
208
|
+
#
|
|
189
209
|
# @param full_path [String] The path to set for the current request.
|
|
190
210
|
# @param host [String] The host to set for the current request.
|
|
191
211
|
# @param method [String] The request method to set for the current request.
|
|
192
|
-
|
|
212
|
+
# @param protocol [Symbol] The protocol to set for the current request (e.g., `:http` or `:https`).
|
|
213
|
+
def with_request_url(full_path, host: nil, method: nil, protocol: nil)
|
|
193
214
|
old_request_host = vc_test_request.host
|
|
194
215
|
old_request_method = vc_test_request.request_method
|
|
195
216
|
old_request_path_info = vc_test_request.path_info
|
|
@@ -197,6 +218,7 @@ module ViewComponent
|
|
|
197
218
|
old_request_query_parameters = vc_test_request.query_parameters
|
|
198
219
|
old_request_query_string = vc_test_request.query_string
|
|
199
220
|
old_request_format = vc_test_request.format.symbol
|
|
221
|
+
old_request_scheme = vc_test_request.scheme
|
|
200
222
|
old_controller = defined?(@vc_test_controller) && @vc_test_controller
|
|
201
223
|
|
|
202
224
|
path, query = full_path.split("?", 2)
|
|
@@ -204,6 +226,7 @@ module ViewComponent
|
|
|
204
226
|
vc_test_request.instance_variable_set(:@original_fullpath, full_path)
|
|
205
227
|
vc_test_request.host = host if host
|
|
206
228
|
vc_test_request.request_method = method if method
|
|
229
|
+
vc_test_request.set_header(Rack::RACK_URL_SCHEME, protocol.to_s) if protocol
|
|
207
230
|
vc_test_request.path_info = path
|
|
208
231
|
vc_test_request.path_parameters = Rails.application.routes.recognize_path_with_request(vc_test_request, path, {})
|
|
209
232
|
vc_test_request.set_header("action_dispatch.request.query_parameters",
|
|
@@ -213,6 +236,7 @@ module ViewComponent
|
|
|
213
236
|
ensure
|
|
214
237
|
vc_test_request.host = old_request_host
|
|
215
238
|
vc_test_request.request_method = old_request_method
|
|
239
|
+
vc_test_request.set_header(Rack::RACK_URL_SCHEME, old_request_scheme)
|
|
216
240
|
vc_test_request.path_info = old_request_path_info
|
|
217
241
|
vc_test_request.path_parameters = old_request_path_parameters
|
|
218
242
|
vc_test_request.set_header("action_dispatch.request.query_parameters", old_request_query_parameters)
|
data/lib/view_component.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "action_view"
|
|
4
4
|
require "active_support/dependencies/autoload"
|
|
5
|
+
require "view_component/version"
|
|
5
6
|
|
|
6
7
|
module ViewComponent
|
|
7
8
|
extend ActiveSupport::Autoload
|
|
@@ -16,7 +17,7 @@ module ViewComponent
|
|
|
16
17
|
autoload :Preview
|
|
17
18
|
autoload :Translatable
|
|
18
19
|
|
|
19
|
-
if Rails.env.test?
|
|
20
|
+
if defined?(Rails.env) && Rails.env.test?
|
|
20
21
|
autoload :TestHelpers
|
|
21
22
|
autoload :SystemSpecHelpers
|
|
22
23
|
autoload :SystemTestHelpers
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: view_component
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.
|
|
4
|
+
version: 4.12.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ViewComponent Team
|
|
@@ -16,9 +16,6 @@ dependencies:
|
|
|
16
16
|
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
18
|
version: 7.1.0
|
|
19
|
-
- - "<"
|
|
20
|
-
- !ruby/object:Gem::Version
|
|
21
|
-
version: '8.1'
|
|
22
19
|
type: :runtime
|
|
23
20
|
prerelease: false
|
|
24
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -26,9 +23,20 @@ dependencies:
|
|
|
26
23
|
- - ">="
|
|
27
24
|
- !ruby/object:Gem::Version
|
|
28
25
|
version: 7.1.0
|
|
29
|
-
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: actionview
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
30
31
|
- !ruby/object:Gem::Version
|
|
31
|
-
version:
|
|
32
|
+
version: 7.1.0
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: 7.1.0
|
|
32
40
|
- !ruby/object:Gem::Dependency
|
|
33
41
|
name: concurrent-ruby
|
|
34
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -127,7 +135,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
127
135
|
- !ruby/object:Gem::Version
|
|
128
136
|
version: '0'
|
|
129
137
|
requirements: []
|
|
130
|
-
rubygems_version:
|
|
138
|
+
rubygems_version: 4.0.10
|
|
131
139
|
specification_version: 4
|
|
132
140
|
summary: A framework for building reusable, testable & encapsulated view components
|
|
133
141
|
in Ruby on Rails.
|