view_component 2.61.1 → 2.64.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f405812996b460c943dba2ae0802dbc7e924ce3ae45ff69eb917333c77ecb890
4
- data.tar.gz: b50121e18674aa4c5f7b569cc4c2edb5078d4685192e8154d4654b4b21a03ef6
3
+ metadata.gz: 1cee1c1b53cec5fec9d4613416a27999c653c2cf8fb176798b235c7b71dc795d
4
+ data.tar.gz: 5fb8a364788849fecfb8bb2160ebd6dc403f624ba6b3fe13c818706f928b8257
5
5
  SHA512:
6
- metadata.gz: 23b295998753b2f0d17af1ee5cbc18ec0602cafa48c1ca4ee19568073c38453c79b7aadd1ec42160eab0b522d4a5c3c10f8682a224ed771b4ff25da1e97730fa
7
- data.tar.gz: 43e4f27eb614d7de04e6b44747e4d7f2c006367cae62b2b3dc7ad67bdc6a22ea9ea7822308895b704bd19a50879d3bb3ecf2f0209adb9f039eb149217ef479b5
6
+ metadata.gz: d721f8d07af64e314a506afc364941f95d3b824d27e587e12d8c774f4fa168b7ae00ac400c4f73b546ed0d5728aec60d586e52dacaee2b8f10e01230bbe3ab8f
7
+ data.tar.gz: 598ab2f08524a8eb10b17a001ed7429223ca39ab204c25cec4375ea201e3afa1c852ba1578f81a1de5c83211e1eb05f3c1a7d146f83cc1d77e3f4ed0bfdef962
data/docs/CHANGELOG.md CHANGED
@@ -9,6 +9,64 @@ title: Changelog
9
9
 
10
10
  ## main
11
11
 
12
+ ## 2.64.0
13
+
14
+ * Add `warn_on_deprecated_slot_setter` flag to opt-in to deprecation warning.
15
+
16
+ In [v2.54.0](https://viewcomponent.org/CHANGELOG.html#2540), the Slots API was updated to require the `with_*` prefix for setting Slots. The non-`with_*` setters will be deprecated in a coming version and removed in `v3.0`.
17
+
18
+ To enable the coming deprecation warning, add `warn_on_deprecated_slot_setter`:
19
+
20
+ ```ruby
21
+ class DeprecatedSlotsSetterComponent < ViewComponent::Base
22
+ warn_on_deprecated_slot_setter
23
+ end
24
+ ```
25
+
26
+ *Joel Hawksley*
27
+
28
+ * Add [`m`](https://rubygems.org/gems/m) to development environment.
29
+
30
+ *Joel Hawksley*
31
+
32
+ * Fix potential deadlock scenario in the compiler's development mode.
33
+
34
+ *Blake Williams*
35
+
36
+ ## 2.63.0
37
+
38
+ * Fixed typo in `renders_many` documentation.
39
+
40
+ *Graham Rogers*
41
+
42
+ * Add documentation about working with `turbo-rails`.
43
+
44
+ *Matheus Poli Camilo*
45
+
46
+ * Fix issue causing helper methods to not be available in nested components when the render monkey patch is disabled and `render_component` is used.
47
+
48
+ *Daniel Scheffknecht*
49
+
50
+ ## 2.62.0
51
+
52
+ * Remove the experimental global output buffer feature.
53
+ * Restore functionality that used to attempt to compile templates on each call to `#render_in`.
54
+ * Un-pin `rails` `main` dependency.
55
+
56
+ *Cameron Dutro*
57
+
58
+ * Add blank space between "in" and "ViewComponent" in a deprecation warning.
59
+
60
+ *Vikram Dighe*
61
+
62
+ * Add HappyCo to list of companies using ViewComponent.
63
+
64
+ *Josh Clayton*
65
+
66
+ * Add predicate method support to polymorphic slots.
67
+
68
+ *Graham Rogers*
69
+
12
70
  ## 2.61.1
13
71
 
14
72
  * Revert `Expose Capybara DSL methods directly inside tests.` This change unintentionally broke other Capybara methods and thus introduced a regression. We aren't confident that we can fail forward so we have decided to revert this change.
@@ -86,10 +86,12 @@ module ViewComponent
86
86
  #
87
87
  # @return [String]
88
88
  def render_in(view_context, &block)
89
+ self.class.compile(raise_errors: true)
90
+
89
91
  @view_context = view_context
90
92
  self.__vc_original_view_context ||= view_context
91
93
 
92
- @output_buffer = ActionView::OutputBuffer.new unless defined?(@global_buffer_in_use) && @global_buffer_in_use
94
+ @output_buffer = ActionView::OutputBuffer.new
93
95
 
94
96
  @lookup_context ||= view_context.lookup_context
95
97
 
@@ -124,7 +126,7 @@ module ViewComponent
124
126
  before_render
125
127
 
126
128
  if render?
127
- perform_render
129
+ render_template_for(@__vc_variant).to_s + _output_postamble
128
130
  else
129
131
  ""
130
132
  end
@@ -132,11 +134,6 @@ module ViewComponent
132
134
  @current_template = old_current_template
133
135
  end
134
136
 
135
- # @private
136
- def perform_render
137
- render_template_for(@__vc_variant).to_s + _output_postamble
138
- end
139
-
140
137
  # Subclass components that call `super` inside their template code will cause a
141
138
  # double render if they emit the result:
142
139
  #
@@ -152,17 +149,6 @@ module ViewComponent
152
149
  nil
153
150
  end
154
151
 
155
- # @private
156
- # :nocov:
157
- def render_template_for(variant = nil)
158
- # Force compilation here so the compiler always redefines render_template_for.
159
- # This is mostly a safeguard to prevent infinite recursion.
160
- self.class.compile(raise_errors: true, force: true)
161
- # .compile replaces this method; call the new one
162
- render_template_for(variant)
163
- end
164
- # :nocov:
165
-
166
152
  # EXPERIMENTAL: Optional content to be returned after the rendered template.
167
153
  #
168
154
  # @return [String]
@@ -20,11 +20,10 @@ module ViewComponent
20
20
 
21
21
  def invalidate_class!(klass)
22
22
  cache.delete(klass)
23
- klass.compiler.reset_render_template_for
24
23
  end
25
24
 
26
25
  def invalidate!
27
- cache.each { |klass| invalidate_class!(klass) }
26
+ cache.clear
28
27
  end
29
28
  end
30
29
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "concurrent-ruby"
4
+
3
5
  module ViewComponent
4
6
  class Compiler
5
7
  # Lock required to be obtained before compiling the component
@@ -16,7 +18,7 @@ module ViewComponent
16
18
 
17
19
  def initialize(component_class)
18
20
  @component_class = component_class
19
- @__vc_compiler_lock = Monitor.new
21
+ @__vc_compiler_lock = Concurrent::ReadWriteLock.new
20
22
  end
21
23
 
22
24
  def compiled?
@@ -33,7 +35,9 @@ module ViewComponent
33
35
 
34
36
  component_class.superclass.compile(raise_errors: raise_errors) if should_compile_superclass?
35
37
 
36
- with_lock do
38
+ with_write_lock do
39
+ CompileCache.invalidate_class!(component_class)
40
+
37
41
  subclass_instance_methods = component_class.instance_methods(false)
38
42
 
39
43
  if subclass_instance_methods.include?(:with_content) && raise_errors
@@ -66,8 +70,8 @@ module ViewComponent
66
70
  # as Ruby warns when redefining a method.
67
71
  method_name = call_method_name(template[:variant])
68
72
 
69
- if component_class.instance_methods(false).include?(method_name.to_sym)
70
- component_class.send(:remove_method, method_name.to_sym)
73
+ if component_class.instance_methods.include?(method_name.to_sym)
74
+ component_class.send(:undef_method, method_name.to_sym)
71
75
  end
72
76
 
73
77
  # rubocop:disable Style/EvalWithLocation
@@ -88,18 +92,16 @@ module ViewComponent
88
92
  end
89
93
  end
90
94
 
91
- def with_lock(&block)
95
+ def with_write_lock(&block)
92
96
  if development?
93
- __vc_compiler_lock.synchronize(&block)
97
+ __vc_compiler_lock.with_write_lock(&block)
94
98
  else
95
99
  block.call
96
100
  end
97
101
  end
98
102
 
99
- def reset_render_template_for
100
- if component_class.instance_methods(false).include?(:render_template_for)
101
- component_class.send(:remove_method, :render_template_for)
102
- end
103
+ def with_read_lock(&block)
104
+ __vc_compiler_lock.with_read_lock(&block)
103
105
  end
104
106
 
105
107
  private
@@ -107,7 +109,9 @@ module ViewComponent
107
109
  attr_reader :component_class
108
110
 
109
111
  def define_render_template_for
110
- reset_render_template_for
112
+ if component_class.instance_methods.include?(:render_template_for)
113
+ component_class.send(:undef_method, :render_template_for)
114
+ end
111
115
 
112
116
  variant_elsifs = variants.compact.uniq.map do |variant|
113
117
  "elsif variant.to_sym == :#{variant}\n #{call_method_name(variant)}"
@@ -125,7 +129,7 @@ module ViewComponent
125
129
  if development?
126
130
  component_class.class_eval <<-RUBY, __FILE__, __LINE__ + 1
127
131
  def render_template_for(variant = nil)
128
- self.class.compiler.with_lock do
132
+ self.class.compiler.with_read_lock do
129
133
  #{body}
130
134
  end
131
135
  end
@@ -20,7 +20,6 @@ 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?
24
23
 
25
24
  if options.show_previews
26
25
  options.preview_paths << "#{Rails.root}/test/components/previews" if defined?(Rails.root) && Dir.exist?(
@@ -58,21 +57,6 @@ module ViewComponent
58
57
  end
59
58
  end
60
59
 
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
-
76
60
  initializer "view_component.set_autoload_paths" do |app|
77
61
  options = app.config.view_component
78
62
 
@@ -42,6 +42,10 @@ module ViewComponent
42
42
  define_method(getter_name) do
43
43
  get_slot(slot_name)
44
44
  end
45
+
46
+ define_method("#{getter_name}?") do
47
+ get_slot(slot_name).present?
48
+ end
45
49
  end
46
50
 
47
51
  renderable_hash = types.each_with_object({}) do |(poly_type, poly_callable), memo|
@@ -57,10 +61,12 @@ module ViewComponent
57
61
  end
58
62
 
59
63
  define_method(setter_name) do |*args, &block|
60
- ViewComponent::Deprecation.warn(
61
- "polymorphic slot setters like `#{setter_name}` are deprecated and will be removed in" \
62
- "ViewComponent v3.0.0.\n\nUse `with_#{setter_name}` instead."
63
- )
64
+ if _warn_on_deprecated_slot_setter
65
+ ViewComponent::Deprecation.warn(
66
+ "polymorphic slot setters like `#{setter_name}` are deprecated and will be removed in " \
67
+ "ViewComponent v3.0.0.\n\nUse `with_#{setter_name}` instead."
68
+ )
69
+ end
64
70
 
65
71
  set_polymorphic_slot(slot_name, poly_type, *args, &block)
66
72
  end
@@ -3,6 +3,7 @@
3
3
  module ViewComponent
4
4
  module RenderComponentHelper # :nodoc:
5
5
  def render_component(component, &block)
6
+ component.set_original_view_context(__vc_original_view_context) if is_a?(ViewComponent::Base)
6
7
  component.render_in(self, &block)
7
8
  end
8
9
  end
@@ -17,9 +17,19 @@ module ViewComponent
17
17
  # Hash of registered Slots
18
18
  class_attribute :registered_slots
19
19
  self.registered_slots = {}
20
+
21
+ class_attribute :_warn_on_deprecated_slot_setter
22
+ self._warn_on_deprecated_slot_setter = false
20
23
  end
21
24
 
22
25
  class_methods do
26
+ ##
27
+ # Enables deprecations coming to the Slots API in ViewComponent v3
28
+ #
29
+ def warn_on_deprecated_slot_setter
30
+ self._warn_on_deprecated_slot_setter = true
31
+ end
32
+
23
33
  ##
24
34
  # Registers a sub-component
25
35
  #
@@ -80,7 +90,13 @@ module ViewComponent
80
90
  if args.empty? && block.nil?
81
91
  get_slot(slot_name)
82
92
  else
83
- # Deprecated: Will remove in 3.0
93
+ if _warn_on_deprecated_slot_setter
94
+ ViewComponent::Deprecation.warn(
95
+ "Setting a slot with `##{slot_name}` is deprecated and will be removed in ViewComponent v3.0.0. " \
96
+ "Use `#with_#{slot_name}` to set the slot instead."
97
+ )
98
+ end
99
+
84
100
  set_slot(slot_name, nil, *args, &block)
85
101
  end
86
102
  end
@@ -98,11 +114,11 @@ module ViewComponent
98
114
  #
99
115
  # = Example
100
116
  #
101
- # render_many :items, -> (name:) { ItemComponent.new(name: name }
117
+ # renders_many :items, -> (name:) { ItemComponent.new(name: name }
102
118
  #
103
119
  # # OR
104
120
  #
105
- # render_many :items, ItemComponent
121
+ # renders_many :items, ItemComponent
106
122
  #
107
123
  # = Rendering sub-components
108
124
  #
@@ -138,9 +154,15 @@ module ViewComponent
138
154
  # Define setter for singular names
139
155
  # for example `renders_many :items` allows fetching all tabs with
140
156
  # `component.tabs` and setting a tab with `component.tab`
141
- #
142
- # Deprecated: Will remove in 3.0
157
+
143
158
  define_method singular_name do |*args, &block|
159
+ if _warn_on_deprecated_slot_setter
160
+ ViewComponent::Deprecation.warn(
161
+ "Setting a slot with `##{singular_name}` is deprecated and will be removed in ViewComponent v3.0.0. " \
162
+ "Use `#with_#{singular_name}` to set the slot instead."
163
+ )
164
+ end
165
+
144
166
  set_slot(slot_name, nil, *args, &block)
145
167
  end
146
168
  ruby2_keywords(singular_name.to_sym) if respond_to?(:ruby2_keywords, true)
@@ -162,7 +184,13 @@ module ViewComponent
162
184
  if collection_args.nil? && block.nil?
163
185
  get_slot(slot_name)
164
186
  else
165
- # Deprecated: Will remove in 3.0
187
+ if _warn_on_deprecated_slot_setter
188
+ ViewComponent::Deprecation.warn(
189
+ "Setting a slot with `##{slot_name}` is deprecated and will be removed in ViewComponent v3.0.0. " \
190
+ "Use `#with_#{slot_name}` to set the slot instead."
191
+ )
192
+ end
193
+
166
194
  collection_args.map do |args|
167
195
  set_slot(slot_name, nil, **args, &block)
168
196
  end
@@ -3,8 +3,8 @@
3
3
  module ViewComponent
4
4
  module VERSION
5
5
  MAJOR = 2
6
- MINOR = 61
7
- PATCH = 1
6
+ MINOR = 64
7
+ PATCH = 0
8
8
 
9
9
  STRING = [MAJOR, MINOR, PATCH].join(".")
10
10
  end
@@ -11,9 +11,7 @@ module ViewComponent
11
11
  autoload :CompileCache
12
12
  autoload :ComponentError
13
13
  autoload :Deprecation
14
- autoload :GlobalOutputBuffer
15
14
  autoload :Instrumentation
16
- autoload :OutputBufferStack
17
15
  autoload :Preview
18
16
  autoload :PreviewTemplateError
19
17
  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.61.1
4
+ version: 2.64.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-07-21 00:00:00.000000000 Z
11
+ date: 2022-08-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -44,6 +44,20 @@ dependencies:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
46
  version: '1.0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: concurrent-ruby
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '1.0'
47
61
  - !ruby/object:Gem::Dependency
48
62
  name: appraisal
49
63
  requirement: !ruby/object:Gem::Requirement
@@ -142,6 +156,20 @@ dependencies:
142
156
  - - "~>"
143
157
  - !ruby/object:Gem::Version
144
158
  version: '2'
159
+ - !ruby/object:Gem::Dependency
160
+ name: m
161
+ requirement: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - "~>"
164
+ - !ruby/object:Gem::Version
165
+ version: '1'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - "~>"
171
+ - !ruby/object:Gem::Version
172
+ version: '1'
145
173
  - !ruby/object:Gem::Dependency
146
174
  name: minitest
147
175
  requirement: !ruby/object:Gem::Requirement
@@ -334,9 +362,7 @@ files:
334
362
  - lib/view_component/docs_builder_component.html.erb
335
363
  - lib/view_component/docs_builder_component.rb
336
364
  - lib/view_component/engine.rb
337
- - lib/view_component/global_output_buffer.rb
338
365
  - lib/view_component/instrumentation.rb
339
- - lib/view_component/output_buffer_stack.rb
340
366
  - lib/view_component/polymorphic_slots.rb
341
367
  - lib/view_component/preview.rb
342
368
  - lib/view_component/preview_template_error.rb
@@ -1,100 +0,0 @@
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
- # rubocop:disable Style/SafeNavigation
38
- if output_buffer && output_buffer.respond_to?(:encoding)
39
- buf.force_encoding(output_buffer.encoding)
40
- end
41
- # rubocop:enable Style/SafeNavigation
42
- end
43
-
44
- output_buffer.push(buf)
45
-
46
- begin
47
- yield
48
- ensure
49
- # assign result here to avoid a return statement, which will
50
- # immediately return to the caller and swallow any errors
51
- result = output_buffer.pop
52
- end
53
-
54
- result
55
- end
56
-
57
- module ActionViewMods
58
- def output_buffer=(other_buffer)
59
- if @output_buffer.is_a?(OutputBufferStack)
60
- @output_buffer.replace(other_buffer)
61
- else
62
- super
63
- end
64
- end
65
-
66
- def with_output_buffer(buf = nil)
67
- unless buf
68
- buf = ActionView::OutputBuffer.new
69
- # rubocop:disable Style/SafeNavigation
70
- if @output_buffer && @output_buffer.respond_to?(:encoding)
71
- buf.force_encoding(@output_buffer.encoding)
72
- end
73
- # rubocop:enable Style/SafeNavigation
74
- end
75
-
76
- if @output_buffer.is_a?(OutputBufferStack)
77
- @output_buffer.push(buf)
78
-
79
- begin
80
- yield
81
- ensure
82
- result = @output_buffer.pop
83
- end
84
-
85
- result
86
- else
87
- @output_buffer, old_buffer = buf, output_buffer
88
-
89
- begin
90
- yield
91
- ensure
92
- @output_buffer = old_buffer
93
- end
94
-
95
- buf
96
- end
97
- end
98
- end
99
- end
100
- end
@@ -1,65 +0,0 @@
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
- @current_buffer.safe_concat(arg)
41
- end
42
-
43
- def length
44
- @current_buffer.length
45
- end
46
-
47
- def push(buffer = nil)
48
- buffer ||= self.class.make_frame
49
- @buffer_stack.push(buffer)
50
- @current_buffer = buffer
51
- end
52
-
53
- def pop
54
- @buffer_stack.pop.tap do
55
- @current_buffer = @buffer_stack.last
56
- end
57
- end
58
-
59
- def to_s
60
- @current_buffer
61
- end
62
-
63
- alias_method :current, :to_s
64
- end
65
- end