view_component 2.9.0 → 2.13.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: bc3f38e0c12dafc80bf6c2c9b74d09d3a3f019ee4d4a9dfa59d1a638a1228898
4
- data.tar.gz: 48f1df9b4f54a0049b96e37b1ca16de5125ef13163838aa94cba4fb5cb6f8a1f
3
+ metadata.gz: 3950532a26d2d6d1ddcf412383bfd9cde269c1f69c3d6f7f03dd4e51e7d638e2
4
+ data.tar.gz: ee2524ef62b335376287f30709e62f2d39a71e43c0f5ec9853db8142b6c8fb3c
5
5
  SHA512:
6
- metadata.gz: 7efa511f6cc6b76fed913823eb54d3ec709e3d8ca99adbcbc6cf6a667c407be4885701b68fc174fa2af3908d6da708c77c9cffa58de23f3f11307510e7cceb57
7
- data.tar.gz: 238f0fd897c7125652cf80df6c59dcaa5d8bf5a1eb39d994a48a876be49cf0c43d5274eed3b2fbcf8915461ef1334dec9f92835b886e152e7eb64ab320b7049f
6
+ metadata.gz: c4cc6925445d3486e4c9a6ef915254cf3f28d973ad6a2876fca4fe3435b19ac273f18e89432622eaa38c8bba22ab9e9cafa07e417299dab8537848742175d1c8
7
+ data.tar.gz: 66d2a44a74a07e78f694adc4790218ee7a9d729e8cd8bb9546d154669a79d5abaa20e7d0bffa0801dfc276ef539b93d5a13dd2b4d3d3093cd8d806972d2dac81
@@ -1,5 +1,39 @@
1
1
  # master
2
2
 
3
+ # 2.13.0
4
+
5
+ * Add the ability to disable the render monkey patch with `config.view_component.render_monkey_patch_enabled`. In versions of Rails < 6.1, add `render_component` and `render_component_to_string` methods which can be used for rendering components instead of `render`.
6
+
7
+ *Johannes Engl*
8
+
9
+ # 2.12.0
10
+
11
+ * Implement Slots as potential successor to Content Areas.
12
+
13
+ *Jens Ljungblad, Brian Bugh, Jon Palmer, Joel Hawksley*
14
+
15
+ # 2.11.1
16
+
17
+ * Fix kwarg warnings in Ruby 2.7.
18
+
19
+ *Joel Hawksley*
20
+
21
+ # 2.11.0
22
+
23
+ * Ensure Rails configuration is available within components.
24
+
25
+ *Trevor Broaddus*
26
+
27
+ * Fix bug where global Rails helpers are inaccessible from nested components. Before, `helpers` was pointing to parent component.
28
+
29
+ *Franco Sebregondi*
30
+
31
+ # 2.10.0
32
+
33
+ * Raise an `ArgumentError` with a helpful message when Ruby cannot parse a component class.
34
+
35
+ *Max Beizer*
36
+
3
37
  # 2.9.0
4
38
 
5
39
  * Cache components per-request in development, preventing unnecessary recompilation during a single request.
@@ -16,7 +50,7 @@
16
50
 
17
51
  * Add `rendered_component` method to `ViewComponent::TestHelpers` which exposes the raw output of the rendered component.
18
52
 
19
- *Richard Macklin*
53
+ *Richard Macklin*
20
54
 
21
55
  * Support sidecar directories for views and other assets.
22
56
 
data/README.md CHANGED
@@ -160,6 +160,133 @@ Returning:
160
160
  </div>
161
161
  ```
162
162
 
163
+ #### Slots (experimental)
164
+
165
+ _Slots are currently under development as a successor to Content Areas. The Slot APIs should be considered unfinished and subject to breaking changes in non-major releases of ViewComponent._
166
+
167
+ Slots enable multiple blocks of content to be passed to a single ViewComponent.
168
+
169
+ Slots exist in two forms: normal slots and collection slots.
170
+
171
+ Normal slots can be rendered once per component. They expose an accessor with the name of the slot that returns an instance of `ViewComponent::Slot`, etc.
172
+
173
+ Collection slots can be rendered multiple times. They expose an accessor with the pluralized name of the slot (`#rows`), which is an Array of `ViewComponent::Slot` instances.
174
+
175
+ To learn more about the design of the Slots API, see https://github.com/github/view_component/pull/348.
176
+
177
+ ##### Defining Slots
178
+
179
+ Slots are defined by the `with_slot` macro:
180
+
181
+ `with_slot :header`
182
+
183
+ To define a collection slot, add `collection: true`:
184
+
185
+ `with_slot :row, collection: true`
186
+
187
+ To define a slot with a custom class, pass `class_name`:
188
+
189
+ `with_slot :body, class_name: 'BodySlot`
190
+
191
+ Slot classes should be subclasses of `ViewComponent::Slot`.
192
+
193
+ ##### Example ViewComponent with Slots
194
+
195
+ `# box_component.rb`
196
+ ```ruby
197
+ class BoxComponent < ViewComponent::Base
198
+ include ViewComponent::Slotable
199
+
200
+ with_slot :body, :footer
201
+ with_slot :header, class_name: "Header"
202
+ with_slot :row, collection: true, class_name: "Row"
203
+
204
+ class Header < ViewComponent::Slot
205
+ def initialize(class_names: "")
206
+ @class_names = class_names
207
+ end
208
+
209
+ def class_names
210
+ "Box-header #{@class_names}"
211
+ end
212
+ end
213
+
214
+ class Row < ViewComponent::Slot
215
+ def initialize(theme: :gray)
216
+ @theme = theme
217
+ end
218
+
219
+ def theme_class_name
220
+ case @theme
221
+ when :gray
222
+ "Box-row--gray"
223
+ when :hover_gray
224
+ "Box-row--hover-gray"
225
+ when :yellow
226
+ "Box-row--yellow"
227
+ when :blue
228
+ "Box-row--blue"
229
+ when :hover_blue
230
+ "Box-row--hover-blue"
231
+ else
232
+ "Box-row--gray"
233
+ end
234
+ end
235
+ end
236
+ end
237
+ ```
238
+
239
+ `# box_component.html.erb`
240
+ ```erb
241
+ <div class="Box">
242
+ <% if header %>
243
+ <div class="<%= header.class_names %>">
244
+ <%= header.content %>
245
+ </div>
246
+ <% end %>
247
+ <% if body %>
248
+ <div class="Box-body">
249
+ <%= body.content %>
250
+ </div>
251
+ <% end %>
252
+ <% if rows.any? %>
253
+ <ul>
254
+ <% rows.each do |row| %>
255
+ <li class="Box-row <%= row.theme_class_name %>">
256
+ <%= row.content %>
257
+ </li>
258
+ <% end %>
259
+ </ul>
260
+ <% end %>
261
+ <% if footer %>
262
+ <div class="Box-footer">
263
+ <%= footer %>
264
+ </div>
265
+ <% end %>
266
+ </div>
267
+ ```
268
+
269
+ `# index.html.erb`
270
+ ```erb
271
+ <%= render(BoxComponent.new) do |component| %>
272
+ <% component.slot(:header, class_names: "my-class-name") do %>
273
+ This is my header!
274
+ <% end %>
275
+ <% component.slot(:body) do %>
276
+ This is the body.
277
+ <% end %>
278
+ <% component.slot(:row) do %>
279
+ Row one
280
+ <% end %>
281
+ <% component.slot(:row, theme: :yellow) do %>
282
+ Yellow row
283
+ <% end %>
284
+ <% component.slot(:footer) do %>
285
+ This is the footer.
286
+ <% end %>
287
+ <% end %>
288
+ ```
289
+
163
290
  ### Inline Component
164
291
 
165
292
  ViewComponents can render without a template file, by defining a `call` method:
@@ -584,6 +711,18 @@ To use component previews:
584
711
  config.view_component.preview_path = "#{Rails.root}/spec/components/previews"
585
712
  ```
586
713
 
714
+ ### Disabling the render monkey patch (Rails < 6.1)
715
+
716
+ In order to [avoid conflicts](https://github.com/github/view_component/issues/288) between ViewComponent and other gems that also monkey patch the `render` method, it is possible to configure ViewComponent to not include the render monkey patch:
717
+
718
+ `config.view_component.render_monkey_patch_enabled = false # defaults to true`
719
+
720
+ With the monkey patch disabled, use `render_component` (or `render_component_to_string`) instead:
721
+
722
+ ```
723
+ <%= render_component Component.new(message: "bar") %>
724
+ ```
725
+
587
726
  ### Sidecar assets (experimental)
588
727
 
589
728
  It’s possible to include Javascript and CSS alongside components, sometimes called "sidecar" assets or files.
@@ -731,6 +870,8 @@ ViewComponent is far from a novel idea! Popular implementations of view componen
731
870
  ## Resources
732
871
 
733
872
  - [Encapsulating Views, RailsConf 2020](https://youtu.be/YVYRus_2KZM)
873
+ - [Rethinking the View Layer with Components, Ruby Rogues Podcast](https://devchat.tv/ruby-rogues/rr-461-rethinking-the-view-layer-with-components-with-joel-hawksley/)
874
+ - [ViewComponents in Action with Andrew Mason, Ruby on Rails Podcast](https://5by5.tv/rubyonrails/320)
734
875
  - [ViewComponent at GitHub with Joel Hawksley](https://the-ruby-blend.fireside.fm/9)
735
876
  - [Components, HAML vs ERB, and Design Systems](https://the-ruby-blend.fireside.fm/4)
736
877
  - [Choosing the Right Tech Stack with Dave Paola](https://5by5.tv/rubyonrails/307)
@@ -783,6 +924,16 @@ ViewComponent is built by:
783
924
  |@simonrand|@fugufish|@cover|@franks921|@fsateler|
784
925
  |Dublin, Ireland|Salt Lake City, Utah|Barcelona|South Africa|Chile|
785
926
 
927
+ |<img src="https://avatars.githubusercontent.com/maxbeizer?s=256" alt="maxbeizer" width="128" />|<img src="https://avatars.githubusercontent.com/franco?s=256" alt="franco" width="128" />|<img src="https://avatars.githubusercontent.com/tbroad-ramsey?s=256" alt="tbroad-ramsey" width="128" />|<img src="https://avatars.githubusercontent.com/jensljungblad?s=256" alt="jensljungblad" width="128" />|<img src="https://avatars.githubusercontent.com/bbugh?s=256" alt="bbugh" width="128" />|
928
+ |:---:|:---:|:---:|:---:|:---:|
929
+ |@maxbeizer|@franco|@tbroad-ramsey|@jensljungblad|@bbugh|
930
+ |Nashville, TN|Switzerland|Spring Hill, TN|New York, NY|Austin, TX|
931
+
932
+ |<img src="https://avatars.githubusercontent.com/johannesengl?s=256" alt="johannesengl" width="128" />|
933
+ |:---:|
934
+ |@johannesengl|
935
+ |Berlin, Germany|
936
+
786
937
  ## License
787
938
 
788
939
  ViewComponent is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -5,6 +5,7 @@ require "active_support/configurable"
5
5
  require "view_component/collection"
6
6
  require "view_component/compile_cache"
7
7
  require "view_component/previewable"
8
+ require "view_component/slotable"
8
9
 
9
10
  module ViewComponent
10
11
  class Base < ActionView::Base
@@ -12,11 +13,15 @@ module ViewComponent
12
13
  include ViewComponent::Previewable
13
14
 
14
15
  # For CSRF authenticity tokens in forms
15
- delegate :form_authenticity_token, :protect_against_forgery?, to: :helpers
16
+ delegate :form_authenticity_token, :protect_against_forgery?, :config, to: :helpers
16
17
 
17
18
  class_attribute :content_areas
18
19
  self.content_areas = [] # class_attribute:default doesn't work until Rails 5.2
19
20
 
21
+ # Hash of registered Slots
22
+ class_attribute :slots
23
+ self.slots = {}
24
+
20
25
  # Entrypoint for rendering components.
21
26
  #
22
27
  # view_context: ActionView context from calling view
@@ -106,9 +111,9 @@ module ViewComponent
106
111
  @controller ||= view_context.controller
107
112
  end
108
113
 
109
- # Provides a proxy to access helper methods
114
+ # Provides a proxy to access helper methods from the context of the current controller
110
115
  def helpers
111
- @helpers ||= view_context
116
+ @helpers ||= controller.view_context
112
117
  end
113
118
 
114
119
  # Removes the first part of the path and the extension.
@@ -157,12 +162,15 @@ module ViewComponent
157
162
  mattr_accessor :test_controller
158
163
  @@test_controller = "ApplicationController"
159
164
 
165
+ # Configure if render monkey patches should be included or not in Rails <6.1.
166
+ mattr_accessor :render_monkey_patch_enabled, instance_writer: false, default: true
167
+
160
168
  class << self
161
169
  attr_accessor :source_location
162
170
 
163
171
  # Render a component collection.
164
- def with_collection(*args)
165
- Collection.new(self, *args)
172
+ def with_collection(collection, **args)
173
+ Collection.new(self, collection, **args)
166
174
  end
167
175
 
168
176
  # Provide identifier for ActionView template annotations
@@ -181,6 +189,10 @@ module ViewComponent
181
189
  # has been re-defined by the consuming application, likely in ApplicationComponent.
182
190
  child.source_location = caller_locations(1, 10).reject { |l| l.label == "inherited" }[0].absolute_path
183
191
 
192
+ # Clone slot configuration into child class
193
+ # see #test_slots_pollution
194
+ child.slots = self.slots.clone
195
+
184
196
  super
185
197
  end
186
198
 
@@ -307,7 +319,16 @@ module ViewComponent
307
319
  parameter = validate_default ? collection_parameter : provided_collection_parameter
308
320
 
309
321
  return unless parameter
310
- return if instance_method(:initialize).parameters.map(&:last).include?(parameter)
322
+ return if initialize_parameters.map(&:last).include?(parameter)
323
+
324
+ # If Ruby cannot parse the component class, then the initalize
325
+ # parameters will be empty and ViewComponent will not be able to render
326
+ # the component.
327
+ if initialize_parameters.empty?
328
+ raise ArgumentError.new(
329
+ "#{self} initializer is empty or invalid."
330
+ )
331
+ end
311
332
 
312
333
  raise ArgumentError.new(
313
334
  "#{self} initializer must accept " \
@@ -317,6 +338,10 @@ module ViewComponent
317
338
 
318
339
  private
319
340
 
341
+ def initialize_parameters
342
+ instance_method(:initialize).parameters
343
+ end
344
+
320
345
  def provided_collection_parameter
321
346
  @provided_collection_parameter ||= nil
322
347
  end
@@ -9,7 +9,7 @@ module ViewComponent
9
9
  @component.validate_collection_parameter!(validate_default: true)
10
10
 
11
11
  @collection.map do |item|
12
- content = @component.new(component_options(item, iterator)).render_in(view_context, &block)
12
+ content = @component.new(**component_options(item, iterator)).render_in(view_context, &block)
13
13
  iterator.iterate!
14
14
  content
15
15
  end.join.html_safe
@@ -10,6 +10,7 @@ module ViewComponent
10
10
  initializer "view_component.set_configs" do |app|
11
11
  options = app.config.view_component
12
12
 
13
+ options.render_monkey_patch_enabled = true if options.render_monkey_patch_enabled.nil?
13
14
  options.show_previews = Rails.env.development? if options.show_previews.nil?
14
15
  options.preview_route ||= ViewComponent::Base.preview_route
15
16
 
@@ -42,21 +43,35 @@ module ViewComponent
42
43
  end
43
44
  end
44
45
 
45
- initializer "view_component.monkey_patch_render" do
46
+ initializer "view_component.monkey_patch_render" do |app|
47
+ next if Rails.version.to_f >= 6.1 || !app.config.view_component.render_monkey_patch_enabled
48
+
46
49
  ActiveSupport.on_load(:action_view) do
47
- if Rails.version.to_f < 6.1
48
- require "view_component/render_monkey_patch"
49
- ActionView::Base.prepend ViewComponent::RenderMonkeyPatch
50
- end
50
+ require "view_component/render_monkey_patch"
51
+ ActionView::Base.prepend ViewComponent::RenderMonkeyPatch
51
52
  end
52
53
 
53
54
  ActiveSupport.on_load(:action_controller) do
54
- if Rails.version.to_f < 6.1
55
- require "view_component/rendering_monkey_patch"
56
- require "view_component/render_to_string_monkey_patch"
57
- ActionController::Base.prepend ViewComponent::RenderingMonkeyPatch
58
- ActionController::Base.prepend ViewComponent::RenderToStringMonkeyPatch
59
- end
55
+ require "view_component/rendering_monkey_patch"
56
+ require "view_component/render_to_string_monkey_patch"
57
+ ActionController::Base.prepend ViewComponent::RenderingMonkeyPatch
58
+ ActionController::Base.prepend ViewComponent::RenderToStringMonkeyPatch
59
+ end
60
+ end
61
+
62
+ initializer "view_component.include_render_component" do |app|
63
+ next if Rails.version.to_f >= 6.1
64
+
65
+ ActiveSupport.on_load(:action_view) do
66
+ require "view_component/render_component_helper"
67
+ ActionView::Base.include ViewComponent::RenderComponentHelper
68
+ end
69
+
70
+ ActiveSupport.on_load(:action_controller) do
71
+ require "view_component/rendering_component_helper"
72
+ require "view_component/render_component_to_string_helper"
73
+ ActionController::Base.include ViewComponent::RenderingComponentHelper
74
+ ActionController::Base.include ViewComponent::RenderComponentToStringHelper
60
75
  end
61
76
  end
62
77
 
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module RenderComponentHelper # :nodoc:
5
+ def render_component(component, &block)
6
+ component.render_in(self, &block)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module RenderComponentToStringHelper # :nodoc:
5
+ def render_component_to_string(component)
6
+ component.render_in(self.view_context)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module RenderingComponentHelper # :nodoc:
5
+ def render_component(component)
6
+ self.response_body = component.render_in(self.view_context)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ class Slot
5
+ attr_accessor :content
6
+ end
7
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ require "view_component/slot"
6
+
7
+ module ViewComponent
8
+ module Slotable
9
+ extend ActiveSupport::Concern
10
+
11
+ class_methods do
12
+ # support initializing slots as:
13
+ #
14
+ # with_slot(
15
+ # :header,
16
+ # collection: true|false,
17
+ # class_name: "Header" # class name string, used to instantiate Slot
18
+ # )
19
+ def with_slot(*slot_names, collection: false, class_name: nil)
20
+ slot_names.each do |slot_name|
21
+ # Ensure slot_name is not already declared
22
+ if self.slots.key?(slot_name)
23
+ raise ArgumentError.new("#{slot_name} slot declared multiple times")
24
+ end
25
+
26
+ # Ensure slot name is not :content
27
+ if slot_name == :content
28
+ raise ArgumentError.new ":content is a reserved slot name. Please use another name, such as ':body'"
29
+ end
30
+
31
+ # Set the name of the method used to access the Slot(s)
32
+ accessor_name =
33
+ if collection
34
+ # If Slot is a collection, set the accessor
35
+ # name to the pluralized form of the slot name
36
+ # For example: :tab => :tabs
37
+ ActiveSupport::Inflector.pluralize(slot_name)
38
+ else
39
+ slot_name
40
+ end
41
+
42
+ instance_variable_name = "@#{accessor_name}"
43
+
44
+ # If the slot is a collection, define an accesor that defaults to an empty array
45
+ if collection
46
+ class_eval <<-RUBY
47
+ def #{accessor_name}
48
+ #{instance_variable_name} ||= []
49
+ end
50
+ RUBY
51
+ else
52
+ attr_reader accessor_name
53
+ end
54
+
55
+ # Default class_name to ViewComponent::Slot
56
+ class_name = "ViewComponent::Slot" unless class_name.present?
57
+
58
+ # Register the slot on the component
59
+ self.slots[slot_name] = {
60
+ class_name: class_name,
61
+ instance_variable_name: instance_variable_name,
62
+ collection: collection
63
+ }
64
+ end
65
+ end
66
+ end
67
+
68
+ # Build a Slot instance on a component,
69
+ # exposing it for use inside the
70
+ # component template.
71
+ #
72
+ # slot: Name of Slot, in symbol form
73
+ # **args: Arguments to be passed to Slot initializer
74
+ #
75
+ # For example:
76
+ # <%= render(SlotsComponent.new) do |component| %>
77
+ # <% component.slot(:footer, class_names: "footer-class") do %>
78
+ # <p>This is my footer!</p>
79
+ # <% end %>
80
+ # <% end %>
81
+ #
82
+ def slot(slot_name, **args, &block)
83
+ # Raise ArgumentError if `slot` does not exist
84
+ unless slots.keys.include?(slot_name)
85
+ raise ArgumentError.new "Unknown slot '#{slot_name}' - expected one of '#{slots.keys}'"
86
+ end
87
+
88
+ slot = slots[slot_name]
89
+
90
+ # The class name of the Slot, such as Header
91
+ slot_class = self.class.const_get(slot[:class_name])
92
+
93
+ unless slot_class <= ViewComponent::Slot
94
+ raise ArgumentError.new "#{slot[:class_name]} must inherit from ViewComponent::Slot"
95
+ end
96
+
97
+ # Instantiate Slot class, accommodating Slots that don't accept arguments
98
+ slot_instance = args.present? ? slot_class.new(args) : slot_class.new
99
+
100
+ # Capture block and assign to slot_instance#content
101
+ slot_instance.content = view_context.capture(&block) if block_given?
102
+
103
+ if slot[:collection]
104
+ # Initialize instance variable as an empty array
105
+ # if slot is a collection and has yet to be initialized
106
+ unless instance_variable_defined?(slot[:instance_variable_name])
107
+ instance_variable_set(slot[:instance_variable_name], [])
108
+ end
109
+
110
+ # Append Slot instance to collection accessor Array
111
+ instance_variable_get(slot[:instance_variable_name]) << slot_instance
112
+ else
113
+ # Assign the Slot instance to the slot accessor
114
+ instance_variable_set(slot[:instance_variable_name], slot_instance)
115
+ end
116
+
117
+ # Return nil, as this method should not output anything to the view itself.
118
+ nil
119
+ end
120
+ end
121
+ end
@@ -3,7 +3,7 @@
3
3
  module ViewComponent
4
4
  module VERSION
5
5
  MAJOR = 2
6
- MINOR = 9
6
+ MINOR = 13
7
7
  PATCH = 0
8
8
 
9
9
  STRING = [MAJOR, MINOR, PATCH].join(".")
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.9.0
4
+ version: 2.13.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: 2020-06-12 00:00:00.000000000 Z
11
+ date: 2020-07-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -204,9 +204,14 @@ files:
204
204
  - lib/view_component/engine.rb
205
205
  - lib/view_component/preview.rb
206
206
  - lib/view_component/previewable.rb
207
+ - lib/view_component/render_component_helper.rb
208
+ - lib/view_component/render_component_to_string_helper.rb
207
209
  - lib/view_component/render_monkey_patch.rb
208
210
  - lib/view_component/render_to_string_monkey_patch.rb
211
+ - lib/view_component/rendering_component_helper.rb
209
212
  - lib/view_component/rendering_monkey_patch.rb
213
+ - lib/view_component/slot.rb
214
+ - lib/view_component/slotable.rb
210
215
  - lib/view_component/template_error.rb
211
216
  - lib/view_component/test_case.rb
212
217
  - lib/view_component/test_helpers.rb