view_component 2.51.0 → 2.52.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: 163414625d8e2df6f2afddc821a6c849aef71cc8cd1ba458a533d90840e545cc
4
- data.tar.gz: 3518269c96d4e62d3bcedae22871c7b260d2abf42e0c1e32703115185e69eeea
3
+ metadata.gz: 1eb8c14aae22e2977db7cde77cd64f9ff1d69f14c0f2c95b6b2ebf0fabf67f5a
4
+ data.tar.gz: fe2c729ae013bf1dd2effb17143acfef756e915ea773dcb7eef84fd329c7e002
5
5
  SHA512:
6
- metadata.gz: 531785b833a4d1a83b6929095b6a6a34076cb6576820884487a247d70c37bde2fedc47914633674c59a888dc7270526660e121f4980f2bcb85625ed6009607c4
7
- data.tar.gz: e4c8b2694c7f70c529d56dcced18f66d511e909e1f36abc604a58402d1332caecde59d40c065668454af8abf6d53578469539ac1f4366650ad286856b50776fa
6
+ metadata.gz: 0e72fac02dd38cd39cfa7fe45bc57617a8088d2016add0c769df9075393dc8fde66ea44c96d466019354585da3754b1db2099e9207a46418b011e6ef8765b816
7
+ data.tar.gz: 6ec82473f583cd484e9ee4042683d7d605131279ef855a199f1a89292967dd971c6ed8a43f8ad7fc9d519bae02fb4f2e5cf8d14f4876ffa1b524ad41ce25ea20
data/docs/CHANGELOG.md CHANGED
@@ -7,6 +7,21 @@ title: Changelog
7
7
 
8
8
  ## main
9
9
 
10
+ ## 2.52.0
11
+
12
+ * Add ADR for separate slot getter/setter API.
13
+
14
+ *Blake Williams*
15
+
16
+ * Add the option to use a "global" output buffer so `form_for` and friends can be used with view components.
17
+
18
+ *Cameron Dutro*, *Blake Williams*
19
+
20
+ * Fix fragment caching in partials when global output buffer is enabled.
21
+ * Fix template inheritance when eager loading is disabled.
22
+
23
+ *Cameron Dutro*
24
+
10
25
  ## 2.51.0
11
26
 
12
27
  * Update the docs only when releasing a new version.
@@ -69,6 +69,8 @@ module ViewComponent
69
69
  @view_context = view_context
70
70
  self.__vc_original_view_context ||= view_context
71
71
 
72
+ @output_buffer = ActionView::OutputBuffer.new unless @global_buffer_in_use
73
+
72
74
  @lookup_context ||= view_context.lookup_context
73
75
 
74
76
  # required for path helpers in older Rails versions
@@ -102,7 +104,7 @@ module ViewComponent
102
104
  before_render
103
105
 
104
106
  if render?
105
- render_template_for(@__vc_variant).to_s + _output_postamble
107
+ perform_render
106
108
  else
107
109
  ""
108
110
  end
@@ -110,6 +112,10 @@ module ViewComponent
110
112
  @current_template = old_current_template
111
113
  end
112
114
 
115
+ def perform_render
116
+ render_template_for(@__vc_variant).to_s + _output_postamble
117
+ end
118
+
113
119
  # :nocov:
114
120
  def render_template_for(variant = nil)
115
121
  # Force compilation here so the compiler always redefines render_template_for.
@@ -421,6 +427,22 @@ module ViewComponent
421
427
  # `compile` defines
422
428
  compile
423
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
+
424
446
  # If Rails application is loaded, add application url_helpers to the component context
425
447
  # we need to check this to use this gem as a dependency
426
448
  if defined?(Rails) && Rails.application
@@ -68,9 +68,8 @@ module ViewComponent
68
68
  component_class.send(:remove_method, method_name.to_sym)
69
69
  end
70
70
 
71
- component_class.class_eval <<-RUBY, template[:path], -1
71
+ component_class.class_eval <<-RUBY, template[:path], 0
72
72
  def #{method_name}
73
- @output_buffer = ActionView::OutputBuffer.new
74
73
  #{compiled_template(template[:path])}
75
74
  end
76
75
  RUBY
@@ -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.capture do
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
- view_context.capture do
53
- # render_in is faster than `parent.render`
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.capture do
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,7 @@
3
3
  module ViewComponent
4
4
  module VERSION
5
5
  MAJOR = 2
6
- MINOR = 51
6
+ MINOR = 52
7
7
  PATCH = 0
8
8
 
9
9
  STRING = [MAJOR, MINOR, PATCH].join(".")
@@ -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.51.0
4
+ version: 2.52.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-03-19 00:00:00.000000000 Z
11
+ date: 2022-03-25 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