view_component 3.0.0.rc1 → 3.0.0.rc2

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.

@@ -1,80 +1,396 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/concern"
4
-
5
4
  require "view_component/slot"
6
5
 
7
6
  module ViewComponent
8
7
  module Slotable
9
8
  extend ActiveSupport::Concern
10
9
 
10
+ RESERVED_NAMES = {
11
+ singular: %i[content render].freeze,
12
+ plural: %i[contents renders].freeze
13
+ }.freeze
14
+
15
+ # Setup component slot state
11
16
  included do
12
17
  # Hash of registered Slots
13
- class_attribute :slots
14
- self.slots = {}
18
+ class_attribute :registered_slots
19
+ self.registered_slots = {}
15
20
  end
16
21
 
17
22
  class_methods do
18
- def inherited(child)
19
- # Clone slot configuration into child class
20
- # see #test_slots_pollution
21
- child.slots = slots.clone
23
+ ##
24
+ # Registers a sub-component
25
+ #
26
+ # = Example
27
+ #
28
+ # renders_one :header -> (classes:) do
29
+ # HeaderComponent.new(classes: classes)
30
+ # end
31
+ #
32
+ # # OR
33
+ #
34
+ # renders_one :header, HeaderComponent
35
+ #
36
+ # where `HeaderComponent` is defined as:
37
+ #
38
+ # class HeaderComponent < ViewComponent::Base
39
+ # def initialize(classes:)
40
+ # @classes = classes
41
+ # end
42
+ # end
43
+ #
44
+ # and has the following template:
45
+ #
46
+ # <header class="<%= @classes %>">
47
+ # <%= content %>
48
+ # </header>
49
+ #
50
+ # = Rendering sub-component content
51
+ #
52
+ # The component's sidecar template can access the sub-component by calling a
53
+ # helper method with the same name as the sub-component.
54
+ #
55
+ # <h1>
56
+ # <%= header do %>
57
+ # My header title
58
+ # <% end %>
59
+ # </h1>
60
+ #
61
+ # = Setting sub-component content
62
+ #
63
+ # Consumers of the component can render a sub-component by calling a
64
+ # helper method with the same name as the slot prefixed with `with_`.
65
+ #
66
+ # <%= render_inline(MyComponent.new) do |component| %>
67
+ # <% component.with_header(classes: "Foo") do %>
68
+ # <p>Bar</p>
69
+ # <% end %>
70
+ # <% end %>
71
+ def renders_one(slot_name, callable = nil)
72
+ validate_singular_slot_name(slot_name)
73
+
74
+ if callable.is_a?(Hash) && callable.key?(:types)
75
+ register_polymorphic_slot(slot_name, callable[:types], collection: false)
76
+ else
77
+ validate_plural_slot_name(ActiveSupport::Inflector.pluralize(slot_name).to_sym)
78
+
79
+ define_method :"with_#{slot_name}" do |*args, &block|
80
+ set_slot(slot_name, nil, *args, &block)
81
+ end
82
+ ruby2_keywords(:"with_#{slot_name}") if respond_to?(:ruby2_keywords, true)
83
+
84
+ define_method slot_name do |*args, &block|
85
+ get_slot(slot_name)
86
+ end
87
+ ruby2_keywords(slot_name.to_sym) if respond_to?(:ruby2_keywords, true)
88
+
89
+ define_method "#{slot_name}?" do
90
+ get_slot(slot_name).present?
91
+ end
92
+
93
+ register_slot(slot_name, collection: false, callable: callable)
94
+ end
95
+ end
96
+
97
+ ##
98
+ # Registers a collection sub-component
99
+ #
100
+ # = Example
101
+ #
102
+ # renders_many :items, -> (name:) { ItemComponent.new(name: name }
103
+ #
104
+ # # OR
105
+ #
106
+ # renders_many :items, ItemComponent
107
+ #
108
+ # = Rendering sub-components
109
+ #
110
+ # The component's sidecar template can access the slot by calling a
111
+ # helper method with the same name as the slot.
112
+ #
113
+ # <h1>
114
+ # <% items.each do |item| %>
115
+ # <%= item %>
116
+ # <% end %>
117
+ # </h1>
118
+ #
119
+ # = Setting sub-component content
120
+ #
121
+ # Consumers of the component can set the content of a slot by calling a
122
+ # helper method with the same name as the slot prefixed with `with_`. The
123
+ # method can be called multiple times to append to the slot.
124
+ #
125
+ # <%= render_inline(MyComponent.new) do |component| %>
126
+ # <% component.with_item(name: "Foo") do %>
127
+ # <p>One</p>
128
+ # <% end %>
129
+ #
130
+ # <% component.with_item(name: "Bar") do %>
131
+ # <p>two</p>
132
+ # <% end %>
133
+ # <% end %>
134
+ def renders_many(slot_name, callable = nil)
135
+ validate_plural_slot_name(slot_name)
136
+
137
+ if callable.is_a?(Hash) && callable.key?(:types)
138
+ register_polymorphic_slot(slot_name, callable[:types], collection: true)
139
+ else
140
+ singular_name = ActiveSupport::Inflector.singularize(slot_name)
141
+ validate_singular_slot_name(ActiveSupport::Inflector.singularize(slot_name).to_sym)
142
+
143
+ define_method :"with_#{singular_name}" do |*args, &block|
144
+ set_slot(slot_name, nil, *args, &block)
145
+ end
146
+ ruby2_keywords(:"with_#{singular_name}") if respond_to?(:ruby2_keywords, true)
147
+
148
+ define_method :"with_#{slot_name}" do |collection_args = nil, &block|
149
+ collection_args.map do |args|
150
+ set_slot(slot_name, nil, **args, &block)
151
+ end
152
+ end
22
153
 
154
+ define_method slot_name do |collection_args = nil, &block|
155
+ get_slot(slot_name)
156
+ end
157
+
158
+ define_method "#{slot_name}?" do
159
+ get_slot(slot_name).present?
160
+ end
161
+
162
+ register_slot(slot_name, collection: true, callable: callable)
163
+ end
164
+ end
165
+
166
+ def slot_type(slot_name)
167
+ registered_slot = registered_slots[slot_name]
168
+ if registered_slot
169
+ registered_slot[:collection] ? :collection : :single
170
+ else
171
+ plural_slot_name = ActiveSupport::Inflector.pluralize(slot_name).to_sym
172
+ plural_registered_slot = registered_slots[plural_slot_name]
173
+ plural_registered_slot&.fetch(:collection) ? :collection_item : nil
174
+ end
175
+ end
176
+
177
+ # Clone slot configuration into child class
178
+ # see #test_slots_pollution
179
+ def inherited(child)
180
+ child.registered_slots = registered_slots.clone
23
181
  super
24
182
  end
25
- end
26
183
 
27
- # Build a Slot instance on a component,
28
- # exposing it for use inside the
29
- # component template.
30
- #
31
- # slot: Name of Slot, in symbol form
32
- # **args: Arguments to be passed to Slot initializer
33
- #
34
- # For example:
35
- # <%= render(SlotsComponent.new) do |component| %>
36
- # <% component.slot(:footer, class_names: "footer-class") do %>
37
- # <p>This is my footer!</p>
38
- # <% end %>
39
- # <% end %>
40
- #
41
- def slot(slot_name, **args, &block)
42
- # Raise ArgumentError if `slot` doesn't exist
43
- unless slots.key?(slot_name)
44
- raise ArgumentError.new "Unknown slot '#{slot_name}' - expected one of '#{slots.keys}'"
184
+ def register_polymorphic_slot(slot_name, types, collection:)
185
+ unless types.empty?
186
+ getter_name = slot_name
187
+
188
+ define_method(getter_name) do
189
+ get_slot(slot_name)
190
+ end
191
+
192
+ define_method("#{getter_name}?") do
193
+ get_slot(slot_name).present?
194
+ end
195
+ end
196
+
197
+ renderable_hash = types.each_with_object({}) do |(poly_type, poly_callable), memo|
198
+ memo[poly_type] = define_slot(
199
+ "#{slot_name}_#{poly_type}", collection: collection, callable: poly_callable
200
+ )
201
+
202
+ setter_name =
203
+ if collection
204
+ "#{ActiveSupport::Inflector.singularize(slot_name)}_#{poly_type}"
205
+ else
206
+ "#{slot_name}_#{poly_type}"
207
+ end
208
+
209
+ define_method("with_#{setter_name}") do |*args, &block|
210
+ set_polymorphic_slot(slot_name, poly_type, *args, &block)
211
+ end
212
+ ruby2_keywords(:"with_#{setter_name}") if respond_to?(:ruby2_keywords, true)
213
+ end
214
+
215
+ registered_slots[slot_name] = {
216
+ collection: collection,
217
+ renderable_hash: renderable_hash
218
+ }
45
219
  end
46
220
 
47
- slot = slots[slot_name]
221
+ private
222
+
223
+ def register_slot(slot_name, **kwargs)
224
+ registered_slots[slot_name] = define_slot(slot_name, **kwargs)
225
+ end
48
226
 
49
- # The class name of the Slot, such as Header
50
- slot_class = self.class.const_get(slot[:class_name])
227
+ def define_slot(slot_name, collection:, callable:)
228
+ # Setup basic slot data
229
+ slot = {
230
+ collection: collection
231
+ }
232
+ return slot unless callable
51
233
 
52
- unless slot_class <= ViewComponent::Slot
53
- raise ArgumentError.new "#{slot[:class_name]} must inherit from ViewComponent::Slot"
234
+ # If callable responds to `render_in`, we set it on the slot as a renderable
235
+ if callable.respond_to?(:method_defined?) && callable.method_defined?(:render_in)
236
+ slot[:renderable] = callable
237
+ elsif callable.is_a?(String)
238
+ # If callable is a string, we assume it's referencing an internal class
239
+ slot[:renderable_class_name] = callable
240
+ elsif callable.respond_to?(:call)
241
+ # If slot doesn't respond to `render_in`, we assume it's a proc,
242
+ # define a method, and save a reference to it to call when setting
243
+ method_name = :"_call_#{slot_name}"
244
+ define_method method_name, &callable
245
+ slot[:renderable_function] = instance_method(method_name)
246
+ else
247
+ raise(
248
+ ArgumentError,
249
+ "invalid slot definition. Please pass a class, string, or callable (i.e. proc, lambda, etc)"
250
+ )
251
+ end
252
+
253
+ slot
254
+ end
255
+
256
+ def validate_plural_slot_name(slot_name)
257
+ if RESERVED_NAMES[:plural].include?(slot_name.to_sym)
258
+ raise ArgumentError.new(
259
+ "#{self} declares a slot named #{slot_name}, which is a reserved word in the ViewComponent framework.\n\n" \
260
+ "To fix this issue, choose a different name."
261
+ )
262
+ end
263
+
264
+ raise_if_slot_ends_with_question_mark(slot_name)
265
+ raise_if_slot_registered(slot_name)
266
+ end
267
+
268
+ def validate_singular_slot_name(slot_name)
269
+ if slot_name.to_sym == :content
270
+ raise ArgumentError.new(
271
+ "#{self} declares a slot named content, which is a reserved word in ViewComponent.\n\n" \
272
+ "Content passed to a ViewComponent as a block is captured and assigned to the `content` accessor without having to create an explicit slot.\n\n" \
273
+ "To fix this issue, either use the `content` accessor directly or choose a different slot name."
274
+ )
275
+ end
276
+
277
+ if RESERVED_NAMES[:singular].include?(slot_name.to_sym)
278
+ raise ArgumentError.new(
279
+ "#{self} declares a slot named #{slot_name}, which is a reserved word in the ViewComponent framework.\n\n" \
280
+ "To fix this issue, choose a different name."
281
+ )
282
+ end
283
+
284
+ raise_if_slot_ends_with_question_mark(slot_name)
285
+ raise_if_slot_registered(slot_name)
286
+ end
287
+
288
+ def raise_if_slot_registered(slot_name)
289
+ if registered_slots.key?(slot_name)
290
+ # TODO remove? This breaks overriding slots when slots are inherited
291
+ raise ArgumentError.new(
292
+ "#{self} declares the #{slot_name} slot multiple times.\n\n" \
293
+ "To fix this issue, choose a different slot name."
294
+ )
295
+ end
296
+ end
297
+
298
+ def raise_if_slot_ends_with_question_mark(slot_name)
299
+ if slot_name.to_s.ends_with?("?")
300
+ raise ArgumentError.new(
301
+ "#{self} declares a slot named #{slot_name}, which ends with a question mark.\n\n" \
302
+ "This is not allowed because the ViewComponent framework already provides predicate " \
303
+ "methods ending in `?`.\n\n" \
304
+ "To fix this issue, choose a different name."
305
+ )
306
+ end
54
307
  end
308
+ end
309
+
310
+ def get_slot(slot_name)
311
+ content unless content_evaluated? # ensure content is loaded so slots will be defined
55
312
 
56
- # Instantiate Slot class, accommodating Slots that don't accept arguments
57
- slot_instance = args.present? ? slot_class.new(**args) : slot_class.new
313
+ slot = self.class.registered_slots[slot_name]
314
+ @__vc_set_slots ||= {}
58
315
 
59
- # Capture block and assign to slot_instance#content
60
- slot_instance.content = view_context.capture(&block).to_s.strip.html_safe if block
316
+ if @__vc_set_slots[slot_name]
317
+ return @__vc_set_slots[slot_name]
318
+ end
61
319
 
62
320
  if slot[:collection]
63
- # Initialize instance variable as an empty array
64
- # if slot is a collection and has yet to be initialized
65
- unless instance_variable_defined?(slot[:instance_variable_name])
66
- instance_variable_set(slot[:instance_variable_name], [])
321
+ []
322
+ end
323
+ end
324
+
325
+ def set_slot(slot_name, slot_definition = nil, *args, &block)
326
+ slot_definition ||= self.class.registered_slots[slot_name]
327
+ slot = Slot.new(self)
328
+
329
+ # Passing the block to the sub-component wrapper like this has two
330
+ # benefits:
331
+ #
332
+ # 1. If this is a `content_area` style sub-component, we will render the
333
+ # block via the `slot`
334
+ #
335
+ # 2. Since we've to pass block content to components when calling
336
+ # `render`, evaluating the block here would require us to call
337
+ # `view_context.capture` twice, which is slower
338
+ slot.__vc_content_block = block if block
339
+
340
+ # If class
341
+ if slot_definition[:renderable]
342
+ slot.__vc_component_instance = slot_definition[:renderable].new(*args)
343
+ # If class name as a string
344
+ elsif slot_definition[:renderable_class_name]
345
+ slot.__vc_component_instance =
346
+ self.class.const_get(slot_definition[:renderable_class_name]).new(*args)
347
+ # If passed a lambda
348
+ elsif slot_definition[:renderable_function]
349
+ # Use `bind(self)` to ensure lambda is executed in the context of the
350
+ # current component. This is necessary to allow the lambda to access helper
351
+ # methods like `content_tag` as well as parent component state.
352
+ renderable_function = slot_definition[:renderable_function].bind(self)
353
+ renderable_value =
354
+ if block
355
+ renderable_function.call(*args) do |*rargs|
356
+ view_context.capture(*rargs, &block)
357
+ end
358
+ else
359
+ renderable_function.call(*args)
360
+ end
361
+
362
+ # Function calls can return components, so if it's a component handle it specially
363
+ if renderable_value.respond_to?(:render_in)
364
+ slot.__vc_component_instance = renderable_value
365
+ else
366
+ slot.__vc_content = renderable_value
67
367
  end
368
+ end
369
+
370
+ @__vc_set_slots ||= {}
68
371
 
69
- # Append Slot instance to collection accessor Array
70
- instance_variable_get(slot[:instance_variable_name]) << slot_instance
372
+ if slot_definition[:collection]
373
+ @__vc_set_slots[slot_name] ||= []
374
+ @__vc_set_slots[slot_name].push(slot)
71
375
  else
72
- # Assign the Slot instance to the slot accessor
73
- instance_variable_set(slot[:instance_variable_name], slot_instance)
376
+ @__vc_set_slots[slot_name] = slot
377
+ end
378
+
379
+ slot
380
+ end
381
+ ruby2_keywords(:set_slot) if respond_to?(:ruby2_keywords, true)
382
+
383
+ def set_polymorphic_slot(slot_name, poly_type = nil, *args, &block)
384
+ slot_definition = self.class.registered_slots[slot_name]
385
+
386
+ if !slot_definition[:collection] && (defined?(@__vc_set_slots) && @__vc_set_slots[slot_name])
387
+ raise ArgumentError, "content for slot '#{slot_name}' has already been provided"
74
388
  end
75
389
 
76
- # Return nil, as this method shouldn't output anything to the view itself.
77
- nil
390
+ poly_def = slot_definition[:renderable_hash][poly_type]
391
+
392
+ set_slot(slot_name, poly_def, *args, &block)
78
393
  end
394
+ ruby2_keywords(:set_polymorphic_slot) if respond_to?(:ruby2_keywords, true)
79
395
  end
80
396
  end
@@ -15,7 +15,7 @@ module ViewComponent
15
15
 
16
16
  file = Tempfile.new(["rendered_#{fragment.class.name}", ".html"], "tmp/view_components/")
17
17
  begin
18
- file.write(controller.render_to_string(html: fragment.to_html.html_safe, layout: layout))
18
+ file.write(__vc_test_helpers_controller.render_to_string(html: fragment.to_html.html_safe, layout: layout))
19
19
  file.rewind
20
20
 
21
21
  block.call("/_system_test_entrypoint?file=#{file.path.split("/").last}")
@@ -28,7 +28,9 @@ module ViewComponent
28
28
  # :nocov:
29
29
  end
30
30
 
31
- # @private
31
+ # Returns the result of a render_inline call.
32
+ #
33
+ # @return [ActionView::OutputBuffer]
32
34
  attr_reader :rendered_content
33
35
 
34
36
  # Render a component inline. Internally sets `page` to be a `Capybara::Node::Simple`,
@@ -45,9 +47,9 @@ module ViewComponent
45
47
  @page = nil
46
48
  @rendered_content =
47
49
  if Rails.version.to_f >= 6.1
48
- controller.view_context.render(component, args, &block)
50
+ __vc_test_helpers_controller.view_context.render(component, args, &block)
49
51
  else
50
- controller.view_context.render_component(component, &block)
52
+ __vc_test_helpers_controller.view_context.render_component(component, &block)
51
53
  end
52
54
 
53
55
  Nokogiri::HTML.fragment(@rendered_content)
@@ -72,8 +74,8 @@ module ViewComponent
72
74
  # @param from [ViewComponent::Preview] The class of the preview to be rendered.
73
75
  # @param params [Hash] Parameters to be passed to the preview.
74
76
  # @return [Nokogiri::HTML]
75
- def render_preview(name, from: preview_class, params: {})
76
- previews_controller = build_controller(Rails.application.config.view_component.preview_controller.constantize)
77
+ def render_preview(name, from: __vc_test_helpers_preview_class, params: {})
78
+ previews_controller = __vc_test_helpers_build_controller(Rails.application.config.view_component.preview_controller.constantize)
77
79
 
78
80
  # From what I can tell, it's not possible to overwrite all request parameters
79
81
  # at once, so we set them individually here.
@@ -103,26 +105,11 @@ module ViewComponent
103
105
  # ```
104
106
  def render_in_view_context(*args, &block)
105
107
  @page = nil
106
- @rendered_content = controller.view_context.instance_exec(*args, &block)
108
+ @rendered_content = __vc_test_helpers_controller.view_context.instance_exec(*args, &block)
107
109
  Nokogiri::HTML.fragment(@rendered_content)
108
110
  end
109
111
  ruby2_keywords(:render_in_view_context) if respond_to?(:ruby2_keywords, true)
110
112
 
111
- # @private
112
- def controller
113
- @controller ||= build_controller(Base.test_controller.constantize)
114
- end
115
-
116
- # @private
117
- def request
118
- @request ||=
119
- begin
120
- request = ActionDispatch::TestRequest.create
121
- request.session = ActionController::TestSession.new
122
- request
123
- end
124
- end
125
-
126
113
  # Set the Action Pack request variant for the given block:
127
114
  #
128
115
  # ```ruby
@@ -133,12 +120,12 @@ module ViewComponent
133
120
  #
134
121
  # @param variant [Symbol] The variant to be set for the provided block.
135
122
  def with_variant(variant)
136
- old_variants = controller.view_context.lookup_context.variants
123
+ old_variants = __vc_test_helpers_controller.view_context.lookup_context.variants
137
124
 
138
- controller.view_context.lookup_context.variants = variant
125
+ __vc_test_helpers_controller.view_context.lookup_context.variants = variant
139
126
  yield
140
127
  ensure
141
- controller.view_context.lookup_context.variants = old_variants
128
+ __vc_test_helpers_controller.view_context.lookup_context.variants = old_variants
142
129
  end
143
130
 
144
131
  # Set the controller to be used while executing the given block,
@@ -152,12 +139,12 @@ module ViewComponent
152
139
  #
153
140
  # @param klass [ActionController::Base] The controller to be used.
154
141
  def with_controller_class(klass)
155
- old_controller = defined?(@controller) && @controller
142
+ old_controller = defined?(@__vc_test_helpers_controller) && @__vc_test_helpers_controller
156
143
 
157
- @controller = build_controller(klass)
144
+ @__vc_test_helpers_controller = __vc_test_helpers_build_controller(klass)
158
145
  yield
159
146
  ensure
160
- @controller = old_controller
147
+ @__vc_test_helpers_controller = old_controller
161
148
  end
162
149
 
163
150
  # Set the URL of the current request (such as when using request-dependent path helpers):
@@ -170,34 +157,47 @@ module ViewComponent
170
157
  #
171
158
  # @param path [String] The path to set for the current request.
172
159
  def with_request_url(path)
173
- old_request_path_info = request.path_info
174
- old_request_path_parameters = request.path_parameters
175
- old_request_query_parameters = request.query_parameters
176
- old_request_query_string = request.query_string
177
- old_controller = defined?(@controller) && @controller
160
+ old_request_path_info = __vc_test_helpers_request.path_info
161
+ old_request_path_parameters = __vc_test_helpers_request.path_parameters
162
+ old_request_query_parameters = __vc_test_helpers_request.query_parameters
163
+ old_request_query_string = __vc_test_helpers_request.query_string
164
+ old_controller = defined?(@__vc_test_helpers_controller) && @__vc_test_helpers_controller
178
165
 
179
166
  path, query = path.split("?", 2)
180
- request.path_info = path
181
- request.path_parameters = Rails.application.routes.recognize_path_with_request(request, path, {})
182
- request.set_header("action_dispatch.request.query_parameters", Rack::Utils.parse_nested_query(query))
183
- request.set_header(Rack::QUERY_STRING, query)
167
+ __vc_test_helpers_request.path_info = path
168
+ __vc_test_helpers_request.path_parameters = Rails.application.routes.recognize_path_with_request(__vc_test_helpers_request, path, {})
169
+ __vc_test_helpers_request.set_header("action_dispatch.request.query_parameters", Rack::Utils.parse_nested_query(query))
170
+ __vc_test_helpers_request.set_header(Rack::QUERY_STRING, query)
184
171
  yield
185
172
  ensure
186
- request.path_info = old_request_path_info
187
- request.path_parameters = old_request_path_parameters
188
- request.set_header("action_dispatch.request.query_parameters", old_request_query_parameters)
189
- request.set_header(Rack::QUERY_STRING, old_request_query_string)
190
- @controller = old_controller
173
+ __vc_test_helpers_request.path_info = old_request_path_info
174
+ __vc_test_helpers_request.path_parameters = old_request_path_parameters
175
+ __vc_test_helpers_request.set_header("action_dispatch.request.query_parameters", old_request_query_parameters)
176
+ __vc_test_helpers_request.set_header(Rack::QUERY_STRING, old_request_query_string)
177
+ @__vc_test_helpers_controller = old_controller
191
178
  end
192
179
 
193
- # @private
194
- def build_controller(klass)
195
- klass.new.tap { |c| c.request = request }.extend(Rails.application.routes.url_helpers)
180
+ # Note: We prefix private methods here to prevent collisions in consumer's tests.
181
+ private
182
+
183
+ def __vc_test_helpers_controller
184
+ @__vc_test_helpers_controller ||= __vc_test_helpers_build_controller(Base.test_controller.constantize)
196
185
  end
197
186
 
198
- private
187
+ def __vc_test_helpers_request
188
+ @__vc_test_helpers_request ||=
189
+ begin
190
+ out = ActionDispatch::TestRequest.create
191
+ out.session = ActionController::TestSession.new
192
+ out
193
+ end
194
+ end
195
+
196
+ def __vc_test_helpers_build_controller(klass)
197
+ klass.new.tap { |c| c.request = __vc_test_helpers_request }.extend(Rails.application.routes.url_helpers)
198
+ end
199
199
 
200
- def preview_class
200
+ def __vc_test_helpers_preview_class
201
201
  result = if respond_to?(:described_class)
202
202
  raise "`render_preview` expected a described_class, but it is nil." if described_class.nil?
203
203
 
@@ -5,7 +5,7 @@ module ViewComponent
5
5
  MAJOR = 3
6
6
  MINOR = 0
7
7
  PATCH = 0
8
- PRE = "rc1"
8
+ PRE = "rc2"
9
9
 
10
10
  STRING = [MAJOR, MINOR, PATCH, PRE].compact.join(".")
11
11
  end
@@ -7,6 +7,7 @@ module ViewComponent
7
7
  extend ActiveSupport::Autoload
8
8
 
9
9
  autoload :Base
10
+ autoload :CaptureCompatibility
10
11
  autoload :Compiler
11
12
  autoload :CompileCache
12
13
  autoload :ComponentError
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: 3.0.0.rc1
4
+ version: 3.0.0.rc2
5
5
  platform: ruby
6
6
  authors:
7
7
  - ViewComponent Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-17 00:00:00.000000000 Z
11
+ date: 2023-02-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -353,6 +353,7 @@ files:
353
353
  - lib/rails/generators/test_unit/templates/component_test.rb.tt
354
354
  - lib/view_component.rb
355
355
  - lib/view_component/base.rb
356
+ - lib/view_component/capture_compatibility.rb
356
357
  - lib/view_component/collection.rb
357
358
  - lib/view_component/compile_cache.rb
358
359
  - lib/view_component/compiler.rb
@@ -363,7 +364,6 @@ files:
363
364
  - lib/view_component/docs_builder_component.rb
364
365
  - lib/view_component/engine.rb
365
366
  - lib/view_component/instrumentation.rb
366
- - lib/view_component/polymorphic_slots.rb
367
367
  - lib/view_component/preview.rb
368
368
  - lib/view_component/preview_template_error.rb
369
369
  - lib/view_component/rails/tasks/view_component.rake
@@ -373,9 +373,8 @@ files:
373
373
  - lib/view_component/render_to_string_monkey_patch.rb
374
374
  - lib/view_component/rendering_component_helper.rb
375
375
  - lib/view_component/rendering_monkey_patch.rb
376
- - lib/view_component/slot_v2.rb
376
+ - lib/view_component/slot.rb
377
377
  - lib/view_component/slotable.rb
378
- - lib/view_component/slotable_v2.rb
379
378
  - lib/view_component/system_test_case.rb
380
379
  - lib/view_component/system_test_helpers.rb
381
380
  - lib/view_component/template_error.rb