view_component 2.50.0 → 2.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of view_component might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/docs/CHANGELOG.md +66 -1
- data/lib/view_component/base.rb +35 -5
- data/lib/view_component/compile_cache.rb +2 -1
- data/lib/view_component/compiler.rb +13 -11
- data/lib/view_component/engine.rb +16 -0
- data/lib/view_component/global_output_buffer.rb +99 -0
- data/lib/view_component/output_buffer_stack.rb +67 -0
- data/lib/view_component/slot_v2.rb +4 -10
- data/lib/view_component/translatable.rb +3 -1
- data/lib/view_component/version.rb +1 -1
- data/lib/view_component.rb +2 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0d505afaa4cb16810628e814c69bb2d00bc76498f6c681173d7811f44cd87c8e
|
4
|
+
data.tar.gz: 2bc859d6034fed19e34739cf1844679bcf83332c7e227c487ddb7364e8166f4b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 75dcf204fd8e3dfad8a1da18de6d96d43fd779f1c9a73ca66b76f0db1ac7be04cf04447c9eaa901d32bc674d7eb7fa79fd5572bff08652a892a7e96f5186faaa
|
7
|
+
data.tar.gz: 7be68adaa9de981a4fb12451ebcc19875f5754fa4798eef60f98702b1c7396bb598a1f6ae77b768bbe55e5b4f77c33f6b9db7156fa18f04c0a63075c54fdd7cc
|
data/docs/CHANGELOG.md
CHANGED
@@ -3,10 +3,75 @@ layout: default
|
|
3
3
|
title: Changelog
|
4
4
|
---
|
5
5
|
|
6
|
+
<!-- Add unreleased changes under the "main" heading. -->
|
7
|
+
|
6
8
|
# Changelog
|
7
9
|
|
8
10
|
## main
|
9
11
|
|
12
|
+
## 2.53.0
|
13
|
+
|
14
|
+
* Add support for relative I18n scopes to translations.
|
15
|
+
|
16
|
+
*Elia Schito*
|
17
|
+
|
18
|
+
* Update CI configuration to use latest Rails 7.0.
|
19
|
+
|
20
|
+
*Hans Lemuet*
|
21
|
+
|
22
|
+
* Document how to use blocks with lambda slots.
|
23
|
+
|
24
|
+
*Sam Partington*
|
25
|
+
|
26
|
+
* Skip Rails 5.2 in local test environment if using incompatible Ruby version.
|
27
|
+
|
28
|
+
*Cameron Dutro*, *Blake Williams*, *Joel Hawksley*
|
29
|
+
|
30
|
+
* Improve landing page documentation.
|
31
|
+
|
32
|
+
*Jason Swett*
|
33
|
+
|
34
|
+
* Add Bearer to list of companies that heavily rely on ViewComponent.
|
35
|
+
|
36
|
+
*Yaroslav Shmarov*
|
37
|
+
|
38
|
+
* Add articles to resources page.
|
39
|
+
|
40
|
+
*Joel Hawksley*
|
41
|
+
|
42
|
+
## 2.52.0
|
43
|
+
|
44
|
+
* Add ADR for separate slot getter/setter API.
|
45
|
+
|
46
|
+
*Blake Williams*
|
47
|
+
|
48
|
+
* Add the option to use a "global" output buffer so `form_for` and friends can be used with view components.
|
49
|
+
|
50
|
+
*Cameron Dutro*, *Blake Williams*
|
51
|
+
|
52
|
+
* Fix fragment caching in partials when global output buffer is enabled.
|
53
|
+
* Fix template inheritance when eager loading is disabled.
|
54
|
+
|
55
|
+
*Cameron Dutro*
|
56
|
+
|
57
|
+
## 2.51.0
|
58
|
+
|
59
|
+
* Update the docs only when releasing a new version.
|
60
|
+
|
61
|
+
*Hans Lemuet*
|
62
|
+
|
63
|
+
* Alphabetize companies using ViewComponent and add Brightline to the list.
|
64
|
+
|
65
|
+
*Jack Schuss*
|
66
|
+
|
67
|
+
* Add CMYK value for ViewComponent Red color on logo page.
|
68
|
+
|
69
|
+
*Dylan Smith*
|
70
|
+
|
71
|
+
* Improve performance by moving template compilation from `#render_in` to `#render_template_for`.
|
72
|
+
|
73
|
+
*Cameron Dutro*
|
74
|
+
|
10
75
|
## 2.50.0
|
11
76
|
|
12
77
|
* Add tests for `layout` usage when rendering via controller.
|
@@ -91,7 +156,7 @@ title: Changelog
|
|
91
156
|
|
92
157
|
*Joel Hawksley*
|
93
158
|
|
94
|
-
* Add Ruby 3.1 and Rails 7.0 to CI
|
159
|
+
* Add Ruby 3.1 and Rails 7.0 to CI.
|
95
160
|
|
96
161
|
*Peter Goldstein*
|
97
162
|
|
data/lib/view_component/base.rb
CHANGED
@@ -66,11 +66,11 @@ module ViewComponent
|
|
66
66
|
#
|
67
67
|
# @return [String]
|
68
68
|
def render_in(view_context, &block)
|
69
|
-
self.class.compile(raise_errors: true)
|
70
|
-
|
71
69
|
@view_context = view_context
|
72
70
|
self.__vc_original_view_context ||= view_context
|
73
71
|
|
72
|
+
@output_buffer = ActionView::OutputBuffer.new unless @global_buffer_in_use
|
73
|
+
|
74
74
|
@lookup_context ||= view_context.lookup_context
|
75
75
|
|
76
76
|
# required for path helpers in older Rails versions
|
@@ -104,7 +104,7 @@ module ViewComponent
|
|
104
104
|
before_render
|
105
105
|
|
106
106
|
if render?
|
107
|
-
|
107
|
+
perform_render
|
108
108
|
else
|
109
109
|
""
|
110
110
|
end
|
@@ -112,6 +112,20 @@ module ViewComponent
|
|
112
112
|
@current_template = old_current_template
|
113
113
|
end
|
114
114
|
|
115
|
+
def perform_render
|
116
|
+
render_template_for(@__vc_variant).to_s + _output_postamble
|
117
|
+
end
|
118
|
+
|
119
|
+
# :nocov:
|
120
|
+
def render_template_for(variant = nil)
|
121
|
+
# Force compilation here so the compiler always redefines render_template_for.
|
122
|
+
# This is mostly a safeguard to prevent infinite recursion.
|
123
|
+
self.class.compile(raise_errors: true, force: true)
|
124
|
+
# .compile replaces this method; call the new one
|
125
|
+
render_template_for(variant)
|
126
|
+
end
|
127
|
+
# :nocov:
|
128
|
+
|
115
129
|
# EXPERIMENTAL: Optional content to be returned after the rendered template.
|
116
130
|
#
|
117
131
|
# @return [String]
|
@@ -413,6 +427,22 @@ module ViewComponent
|
|
413
427
|
# `compile` defines
|
414
428
|
compile
|
415
429
|
|
430
|
+
# Give the child its own personal #render_template_for to protect against the case when
|
431
|
+
# eager loading is disabled and the parent component is rendered before the child. In
|
432
|
+
# such a scenario, the parent will override ViewComponent::Base#render_template_for,
|
433
|
+
# meaning it will not be called for any children and thus not compile their templates.
|
434
|
+
if !child.instance_methods(false).include?(:render_template_for) && !child.compiled?
|
435
|
+
child.class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
436
|
+
def render_template_for(variant = nil)
|
437
|
+
# Force compilation here so the compiler always redefines render_template_for.
|
438
|
+
# This is mostly a safeguard to prevent infinite recursion.
|
439
|
+
self.class.compile(raise_errors: true, force: true)
|
440
|
+
# .compile replaces this method; call the new one
|
441
|
+
render_template_for(variant)
|
442
|
+
end
|
443
|
+
RUBY
|
444
|
+
end
|
445
|
+
|
416
446
|
# If Rails application is loaded, add application url_helpers to the component context
|
417
447
|
# we need to check this to use this gem as a dependency
|
418
448
|
if defined?(Rails) && Rails.application
|
@@ -445,8 +475,8 @@ module ViewComponent
|
|
445
475
|
# Do as much work as possible in this step, as doing so reduces the amount
|
446
476
|
# of work done each time a component is rendered.
|
447
477
|
# @private
|
448
|
-
def compile(raise_errors: false)
|
449
|
-
compiler.compile(raise_errors: raise_errors)
|
478
|
+
def compile(raise_errors: false, force: false)
|
479
|
+
compiler.compile(raise_errors: raise_errors, force: force)
|
450
480
|
end
|
451
481
|
|
452
482
|
# @private
|
@@ -27,12 +27,11 @@ module ViewComponent
|
|
27
27
|
self.class.mode == DEVELOPMENT_MODE
|
28
28
|
end
|
29
29
|
|
30
|
-
def compile(raise_errors: false)
|
31
|
-
return if compiled?
|
30
|
+
def compile(raise_errors: false, force: false)
|
31
|
+
return if compiled? && !force
|
32
|
+
return if component_class == ViewComponent::Base
|
32
33
|
|
33
34
|
with_lock do
|
34
|
-
CompileCache.invalidate_class!(component_class)
|
35
|
-
|
36
35
|
subclass_instance_methods = component_class.instance_methods(false)
|
37
36
|
|
38
37
|
if subclass_instance_methods.include?(:with_content) && raise_errors
|
@@ -65,13 +64,12 @@ module ViewComponent
|
|
65
64
|
# as Ruby warns when redefining a method.
|
66
65
|
method_name = call_method_name(template[:variant])
|
67
66
|
|
68
|
-
if component_class.instance_methods.include?(method_name.to_sym)
|
69
|
-
component_class.send(:
|
67
|
+
if component_class.instance_methods(false).include?(method_name.to_sym)
|
68
|
+
component_class.send(:remove_method, method_name.to_sym)
|
70
69
|
end
|
71
70
|
|
72
|
-
component_class.class_eval <<-RUBY, template[:path],
|
71
|
+
component_class.class_eval <<-RUBY, template[:path], 0
|
73
72
|
def #{method_name}
|
74
|
-
@output_buffer = ActionView::OutputBuffer.new
|
75
73
|
#{compiled_template(template[:path])}
|
76
74
|
end
|
77
75
|
RUBY
|
@@ -93,14 +91,18 @@ module ViewComponent
|
|
93
91
|
end
|
94
92
|
end
|
95
93
|
|
94
|
+
def reset_render_template_for
|
95
|
+
if component_class.instance_methods(false).include?(:render_template_for)
|
96
|
+
component_class.send(:remove_method, :render_template_for)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
96
100
|
private
|
97
101
|
|
98
102
|
attr_reader :component_class
|
99
103
|
|
100
104
|
def define_render_template_for
|
101
|
-
|
102
|
-
component_class.send(:undef_method, :render_template_for)
|
103
|
-
end
|
105
|
+
reset_render_template_for
|
104
106
|
|
105
107
|
variant_elsifs = variants.compact.uniq.map do |variant|
|
106
108
|
"elsif variant.to_sym == :#{variant}\n #{call_method_name(variant)}"
|
@@ -20,6 +20,7 @@ module ViewComponent
|
|
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
|
23
|
+
options.use_global_output_buffer = false if options.use_global_output_buffer.nil?
|
23
24
|
|
24
25
|
if options.show_previews
|
25
26
|
options.preview_paths << "#{Rails.root}/test/components/previews" if defined?(Rails.root) && Dir.exist?(
|
@@ -57,6 +58,21 @@ module ViewComponent
|
|
57
58
|
end
|
58
59
|
end
|
59
60
|
|
61
|
+
initializer "view_component.enable_global_output_buffer" do |app|
|
62
|
+
ActiveSupport.on_load(:view_component) do
|
63
|
+
env_use_gob = ENV.fetch("VIEW_COMPONENT_USE_GLOBAL_OUTPUT_BUFFER", "false") == "true"
|
64
|
+
config_use_gob = app.config.view_component.use_global_output_buffer
|
65
|
+
|
66
|
+
if config_use_gob || env_use_gob
|
67
|
+
# :nocov:
|
68
|
+
app.config.view_component.use_global_output_buffer = true
|
69
|
+
ViewComponent::Base.prepend(ViewComponent::GlobalOutputBuffer)
|
70
|
+
ActionView::Base.prepend(ViewComponent::GlobalOutputBuffer::ActionViewMods)
|
71
|
+
# :nocov:
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
60
76
|
initializer "view_component.set_autoload_paths" do |app|
|
61
77
|
options = app.config.view_component
|
62
78
|
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ViewComponent
|
4
|
+
module GlobalOutputBuffer
|
5
|
+
def render_in(view_context, &block)
|
6
|
+
unless view_context.output_buffer.is_a?(OutputBufferStack)
|
7
|
+
# use instance_variable_set here to avoid triggering the code in the #output_buffer= method below
|
8
|
+
view_context.instance_variable_set(:@output_buffer, OutputBufferStack.new(view_context.output_buffer))
|
9
|
+
end
|
10
|
+
|
11
|
+
@output_buffer = view_context.output_buffer
|
12
|
+
@global_buffer_in_use = true
|
13
|
+
|
14
|
+
super(view_context, &block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def perform_render
|
18
|
+
# HAML unhelpfully assigns to @output_buffer directly, so we hold onto a reference to
|
19
|
+
# it and restore @output_buffer when the HAML engine is finished. In non-HAML cases,
|
20
|
+
# @output_buffer and orig_buf will point to the same object, making the reassignment
|
21
|
+
# statements no-ops.
|
22
|
+
orig_buf = @output_buffer
|
23
|
+
@output_buffer.push
|
24
|
+
result = render_template_for(@__vc_variant).to_s + _output_postamble
|
25
|
+
@output_buffer = orig_buf
|
26
|
+
@output_buffer.pop
|
27
|
+
result
|
28
|
+
end
|
29
|
+
|
30
|
+
def output_buffer=(other_buffer)
|
31
|
+
@output_buffer.replace(other_buffer)
|
32
|
+
end
|
33
|
+
|
34
|
+
def with_output_buffer(buf = nil)
|
35
|
+
unless buf
|
36
|
+
buf = ActionView::OutputBuffer.new
|
37
|
+
if output_buffer && output_buffer.respond_to?(:encoding)
|
38
|
+
buf.force_encoding(output_buffer.encoding)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
output_buffer.push(buf)
|
43
|
+
result = nil
|
44
|
+
|
45
|
+
begin
|
46
|
+
yield
|
47
|
+
ensure
|
48
|
+
# assign result here to avoid a return statement, which will
|
49
|
+
# immediately return to the caller and swallow any errors
|
50
|
+
result = output_buffer.pop
|
51
|
+
end
|
52
|
+
|
53
|
+
result
|
54
|
+
end
|
55
|
+
|
56
|
+
module ActionViewMods
|
57
|
+
def output_buffer=(other_buffer)
|
58
|
+
if @output_buffer.is_a?(OutputBufferStack)
|
59
|
+
@output_buffer.replace(other_buffer)
|
60
|
+
else
|
61
|
+
super
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def with_output_buffer(buf = nil)
|
66
|
+
unless buf
|
67
|
+
buf = ActionView::OutputBuffer.new
|
68
|
+
if @output_buffer && @output_buffer.respond_to?(:encoding)
|
69
|
+
buf.force_encoding(@output_buffer.encoding)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
result = nil
|
74
|
+
|
75
|
+
if @output_buffer.is_a?(OutputBufferStack)
|
76
|
+
@output_buffer.push(buf)
|
77
|
+
|
78
|
+
begin
|
79
|
+
yield
|
80
|
+
ensure
|
81
|
+
result = @output_buffer.pop
|
82
|
+
end
|
83
|
+
|
84
|
+
result
|
85
|
+
else
|
86
|
+
@output_buffer, old_buffer = buf, output_buffer
|
87
|
+
|
88
|
+
begin
|
89
|
+
yield
|
90
|
+
ensure
|
91
|
+
@output_buffer = old_buffer
|
92
|
+
end
|
93
|
+
|
94
|
+
buf
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ViewComponent
|
4
|
+
class OutputBufferStack
|
5
|
+
delegate_missing_to :@current_buffer
|
6
|
+
delegate :presence, :present?, :html_safe?, to: :@current_buffer
|
7
|
+
|
8
|
+
attr_reader :buffer_stack
|
9
|
+
|
10
|
+
def self.make_frame(*args)
|
11
|
+
ActionView::OutputBuffer.new(*args)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(initial_buffer = nil)
|
15
|
+
if initial_buffer.is_a?(self.class)
|
16
|
+
@current_buffer = self.class.make_frame(initial_buffer.current)
|
17
|
+
@buffer_stack = [*initial_buffer.buffer_stack[0..-2], @current_buffer]
|
18
|
+
else
|
19
|
+
@current_buffer = initial_buffer || self.class.make_frame
|
20
|
+
@buffer_stack = [@current_buffer]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def replace(buffer)
|
25
|
+
return if self == buffer
|
26
|
+
|
27
|
+
@current_buffer = buffer.current
|
28
|
+
@buffer_stack = buffer.buffer_stack
|
29
|
+
end
|
30
|
+
|
31
|
+
def append=(arg)
|
32
|
+
@current_buffer.append = arg
|
33
|
+
end
|
34
|
+
|
35
|
+
def safe_append=(arg)
|
36
|
+
@current_buffer.safe_append = arg
|
37
|
+
end
|
38
|
+
|
39
|
+
def safe_concat(arg)
|
40
|
+
# rubocop:disable Rails/OutputSafety
|
41
|
+
@current_buffer.safe_concat(arg)
|
42
|
+
# rubocop:enable Rails/OutputSafety
|
43
|
+
end
|
44
|
+
|
45
|
+
def length
|
46
|
+
@current_buffer.length
|
47
|
+
end
|
48
|
+
|
49
|
+
def push(buffer = nil)
|
50
|
+
buffer ||= self.class.make_frame
|
51
|
+
@buffer_stack.push(buffer)
|
52
|
+
@current_buffer = buffer
|
53
|
+
end
|
54
|
+
|
55
|
+
def pop
|
56
|
+
@buffer_stack.pop.tap do
|
57
|
+
@current_buffer = @buffer_stack.last
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_s
|
62
|
+
@current_buffer
|
63
|
+
end
|
64
|
+
|
65
|
+
alias_method :current, :to_s
|
66
|
+
end
|
67
|
+
end
|
@@ -45,18 +45,12 @@ module ViewComponent
|
|
45
45
|
if defined?(@__vc_content_set_by_with_content)
|
46
46
|
@__vc_component_instance.with_content(@__vc_content_set_by_with_content)
|
47
47
|
|
48
|
-
view_context
|
49
|
-
@__vc_component_instance.render_in(view_context)
|
50
|
-
end
|
48
|
+
@__vc_component_instance.render_in(view_context)
|
51
49
|
elsif defined?(@__vc_content_block)
|
52
|
-
|
53
|
-
|
54
|
-
@__vc_component_instance.render_in(view_context, &@__vc_content_block)
|
55
|
-
end
|
50
|
+
# render_in is faster than `parent.render`
|
51
|
+
@__vc_component_instance.render_in(view_context, &@__vc_content_block)
|
56
52
|
else
|
57
|
-
view_context
|
58
|
-
@__vc_component_instance.render_in(view_context)
|
59
|
-
end
|
53
|
+
@__vc_component_instance.render_in(view_context)
|
60
54
|
end
|
61
55
|
elsif defined?(@__vc_content)
|
62
56
|
@__vc_content
|
@@ -3,7 +3,6 @@
|
|
3
3
|
require "erb"
|
4
4
|
require "set"
|
5
5
|
require "i18n"
|
6
|
-
require "action_view/helpers/translation_helper"
|
7
6
|
require "active_support/concern"
|
8
7
|
|
9
8
|
module ViewComponent
|
@@ -68,7 +67,10 @@ module ViewComponent
|
|
68
67
|
return key.map { |k| translate(k, **options) } if key.is_a?(Array)
|
69
68
|
|
70
69
|
locale = options.delete(:locale) || ::I18n.locale
|
70
|
+
scope = options.delete(:scope)
|
71
|
+
scope = scope.join(".") if scope.is_a? Array
|
71
72
|
key = key&.to_s unless key.is_a?(String)
|
73
|
+
key = "#{scope}.#{key}" if scope
|
72
74
|
key = "#{i18n_scope}#{key}" if key.start_with?(".")
|
73
75
|
|
74
76
|
if HTML_SAFE_TRANSLATION_KEY.match?(key)
|
data/lib/view_component.rb
CHANGED
@@ -11,7 +11,9 @@ module ViewComponent
|
|
11
11
|
autoload :CompileCache
|
12
12
|
autoload :ComponentError
|
13
13
|
autoload :Deprecation
|
14
|
+
autoload :GlobalOutputBuffer
|
14
15
|
autoload :Instrumentation
|
16
|
+
autoload :OutputBufferStack
|
15
17
|
autoload :Preview
|
16
18
|
autoload :PreviewTemplateError
|
17
19
|
autoload :TestHelpers
|
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.
|
4
|
+
version: 2.53.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitHub Open Source
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-04-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -333,7 +333,9 @@ files:
|
|
333
333
|
- lib/view_component/docs_builder_component.html.erb
|
334
334
|
- lib/view_component/docs_builder_component.rb
|
335
335
|
- lib/view_component/engine.rb
|
336
|
+
- lib/view_component/global_output_buffer.rb
|
336
337
|
- lib/view_component/instrumentation.rb
|
338
|
+
- lib/view_component/output_buffer_stack.rb
|
337
339
|
- lib/view_component/polymorphic_slots.rb
|
338
340
|
- lib/view_component/preview.rb
|
339
341
|
- lib/view_component/preview_template_error.rb
|