view_component 2.61.1 → 2.64.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: 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