view_component 2.6.0 → 2.11.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 +4 -4
- data/CHANGELOG.md +38 -0
- data/README.md +81 -5
- data/lib/view_component/base.rb +49 -12
- data/lib/view_component/compile_cache.rb +24 -0
- data/lib/view_component/engine.rb +4 -0
- data/lib/view_component/test_helpers.rb +5 -3
- data/lib/view_component/version.rb +1 -1
- metadata +31 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eec4666da3c79cf466b01641169db9055d1201684acf30169d05cfb9129a7dc2
|
4
|
+
data.tar.gz: 6cfe7e0dee9f5becab077ba05093ea04a38927d2b4cf22d58bff9c4a3cbcd0e3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f3a49b502f6f62c611ff8f700d4a84a2fe30e2a203a57a81964a2d99b005bd20ce431a93ba2319d7ac0dd1a358147d4bec97747b97ea3c386b2e27b5a4a4c241
|
7
|
+
data.tar.gz: 4c98f7b14919f9d56ec3a73e179be99f19cd2ec4157591bf3d1661cd5e8fd9a893110b4ba67dc8f0e79095b8d13974ed8a59904df41016f59515998316c03f2b
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,43 @@
|
|
1
1
|
# master
|
2
2
|
|
3
|
+
# 2.11.0
|
4
|
+
|
5
|
+
* Ensure Rails configuration is available within components.
|
6
|
+
|
7
|
+
*Trevor Broaddus*
|
8
|
+
|
9
|
+
* Fix bug where global Rails helpers are inaccessible from nested components. Before, `helpers` was pointing to parent component.
|
10
|
+
|
11
|
+
*Franco Sebregondi*
|
12
|
+
|
13
|
+
# 2.10.0
|
14
|
+
|
15
|
+
* Raise an `ArgumentError` with a helpful message when Ruby cannot parse a component class.
|
16
|
+
|
17
|
+
*Max Beizer*
|
18
|
+
|
19
|
+
# 2.9.0
|
20
|
+
|
21
|
+
* Cache components per-request in development, preventing unnecessary recompilation during a single request.
|
22
|
+
|
23
|
+
*Felipe Sateler*
|
24
|
+
|
25
|
+
# 2.8.0
|
26
|
+
|
27
|
+
* Add `before_render`, deprecating `before_render_check`.
|
28
|
+
|
29
|
+
*Joel Hawksley*
|
30
|
+
|
31
|
+
# 2.7.0
|
32
|
+
|
33
|
+
* Add `rendered_component` method to `ViewComponent::TestHelpers` which exposes the raw output of the rendered component.
|
34
|
+
|
35
|
+
*Richard Macklin*
|
36
|
+
|
37
|
+
* Support sidecar directories for views and other assets.
|
38
|
+
|
39
|
+
*Jon Palmer*
|
40
|
+
|
3
41
|
# 2.6.0
|
4
42
|
|
5
43
|
* Add `config.view_component.preview_route` to set the endpoint for component previews. By default `/rails/view_components` is used.
|
data/README.md
CHANGED
@@ -191,6 +191,38 @@ class InlineVariantComponent < ViewComponent::Base
|
|
191
191
|
end
|
192
192
|
```
|
193
193
|
|
194
|
+
### Sidecar Assets
|
195
|
+
|
196
|
+
ViewComponents supports two options for defining view files.
|
197
|
+
|
198
|
+
#### Sidecar view
|
199
|
+
|
200
|
+
The simplest option is to place the view next to the Ruby component:
|
201
|
+
|
202
|
+
```
|
203
|
+
app/components
|
204
|
+
├── ...
|
205
|
+
├── test_component.rb
|
206
|
+
├── test_component.html.erb
|
207
|
+
├── ...
|
208
|
+
```
|
209
|
+
|
210
|
+
#### Sidecar directory
|
211
|
+
|
212
|
+
As an alternative, views and other assets can be placed in a sidecar directory with the same name as the component, which can be useful for organizing views alongside other assets like Javascript and CSS.
|
213
|
+
|
214
|
+
```
|
215
|
+
app/components
|
216
|
+
├── ...
|
217
|
+
├── test_component.rb
|
218
|
+
├── test_component
|
219
|
+
| ├── test_component.css
|
220
|
+
| ├── test_component.html.erb
|
221
|
+
| └── test_component.js
|
222
|
+
├── ...
|
223
|
+
|
224
|
+
```
|
225
|
+
|
194
226
|
### Conditional Rendering
|
195
227
|
|
196
228
|
Components can implement a `#render?` method to be called after initialization to determine if the component should render.
|
@@ -242,6 +274,19 @@ end
|
|
242
274
|
|
243
275
|
_To assert that a component has not been rendered, use `refute_component_rendered` from `ViewComponent::TestHelpers`._
|
244
276
|
|
277
|
+
### `before_render`
|
278
|
+
|
279
|
+
Components can define a `before_render` method to be called before a component is rendered, when `helpers` is able to be used:
|
280
|
+
|
281
|
+
`app/components/confirm_email_component.rb`
|
282
|
+
```ruby
|
283
|
+
class MyComponent < ViewComponent::Base
|
284
|
+
def before_render
|
285
|
+
@my_icon = helpers.star_icon
|
286
|
+
end
|
287
|
+
end
|
288
|
+
```
|
289
|
+
|
245
290
|
### Rendering collections
|
246
291
|
|
247
292
|
Use `with_collection` to render a ViewComponent with a collection:
|
@@ -387,7 +432,7 @@ class MyComponentTest < ViewComponent::TestCase
|
|
387
432
|
end
|
388
433
|
```
|
389
434
|
|
390
|
-
In the absence of `capybara`,
|
435
|
+
In the absence of `capybara`, assert against the return value of `render_inline`, which is an instance of `Nokogiri::HTML::DocumentFragment`:
|
391
436
|
|
392
437
|
```ruby
|
393
438
|
test "render component" do
|
@@ -397,6 +442,31 @@ test "render component" do
|
|
397
442
|
end
|
398
443
|
```
|
399
444
|
|
445
|
+
Alternatively, assert against the raw output of the component, which is exposed as `rendered_component`:
|
446
|
+
|
447
|
+
```ruby
|
448
|
+
test "render component" do
|
449
|
+
render_inline(TestComponent.new(title: "my title")) { "Hello, World!" }
|
450
|
+
|
451
|
+
assert_includes rendered_component, "Hello, World!"
|
452
|
+
end
|
453
|
+
```
|
454
|
+
|
455
|
+
To test components that use `with_content_areas`:
|
456
|
+
|
457
|
+
```ruby
|
458
|
+
test "renders content_areas template with content " do
|
459
|
+
render_inline(ContentAreasComponent.new(footer: "Bye!")) do |component|
|
460
|
+
component.with(:title, "Hello!")
|
461
|
+
component.with(:body) { "Have a nice day." }
|
462
|
+
end
|
463
|
+
|
464
|
+
assert_selector(".title", text: "Hello!")
|
465
|
+
assert_selector(".body", text: "Have a nice day.")
|
466
|
+
assert_selector(".footer", text: "Bye!")
|
467
|
+
end
|
468
|
+
```
|
469
|
+
|
400
470
|
#### Action Pack Variants
|
401
471
|
|
402
472
|
Use the `with_variant` helper to test specific variants:
|
@@ -661,6 +731,7 @@ ViewComponent is far from a novel idea! Popular implementations of view componen
|
|
661
731
|
## Resources
|
662
732
|
|
663
733
|
- [Encapsulating Views, RailsConf 2020](https://youtu.be/YVYRus_2KZM)
|
734
|
+
- [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/)
|
664
735
|
- [ViewComponent at GitHub with Joel Hawksley](https://the-ruby-blend.fireside.fm/9)
|
665
736
|
- [Components, HAML vs ERB, and Design Systems](https://the-ruby-blend.fireside.fm/4)
|
666
737
|
- [Choosing the Right Tech Stack with Dave Paola](https://5by5.tv/rubyonrails/307)
|
@@ -708,10 +779,15 @@ ViewComponent is built by:
|
|
708
779
|
|@blakewilliams|@seanpdoyle|@tclem|@nashby|@jaredcwhite|
|
709
780
|
|Boston, MA|New York, NY|San Francisco, CA|Minsk|Portland, OR|
|
710
781
|
|
711
|
-
|<img src="https://avatars.githubusercontent.com/simonrand?s=256" alt="simonrand" width="128" />|<img src="https://avatars.githubusercontent.com/fugufish?s=256" alt="fugufish" width="128" />|<img src="https://avatars.githubusercontent.com/cover?s=256" alt="cover" width="128" />|<img src="https://avatars.githubusercontent.com/franks921?s=256" alt="franks921" width="128" />|
|
712
|
-
|
713
|
-
|@simonrand|@fugufish|@cover|@franks921|
|
714
|
-
|Dublin, Ireland|Salt Lake City, Utah|Barcelona|South Africa|
|
782
|
+
|<img src="https://avatars.githubusercontent.com/simonrand?s=256" alt="simonrand" width="128" />|<img src="https://avatars.githubusercontent.com/fugufish?s=256" alt="fugufish" width="128" />|<img src="https://avatars.githubusercontent.com/cover?s=256" alt="cover" width="128" />|<img src="https://avatars.githubusercontent.com/franks921?s=256" alt="franks921" width="128" />|<img src="https://avatars.githubusercontent.com/fsateler?s=256" alt="fsateler" width="128" />|
|
783
|
+
|:---:|:---:|:---:|:---:|:---:|
|
784
|
+
|@simonrand|@fugufish|@cover|@franks921|@fsateler|
|
785
|
+
|Dublin, Ireland|Salt Lake City, Utah|Barcelona|South Africa|Chile|
|
786
|
+
|
787
|
+
|<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" />|
|
788
|
+
|:---:|:---:|:---:|
|
789
|
+
|@maxbeizer|@franco|@tbroad-ramsey|
|
790
|
+
|Nashville, TN|Switzerland|Spring Hill, TN|
|
715
791
|
|
716
792
|
## License
|
717
793
|
|
data/lib/view_component/base.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require "action_view"
|
4
4
|
require "active_support/configurable"
|
5
5
|
require "view_component/collection"
|
6
|
+
require "view_component/compile_cache"
|
6
7
|
require "view_component/previewable"
|
7
8
|
|
8
9
|
module ViewComponent
|
@@ -11,7 +12,7 @@ module ViewComponent
|
|
11
12
|
include ViewComponent::Previewable
|
12
13
|
|
13
14
|
# For CSRF authenticity tokens in forms
|
14
|
-
delegate :form_authenticity_token, :protect_against_forgery?, to: :helpers
|
15
|
+
delegate :form_authenticity_token, :protect_against_forgery?, :config, to: :helpers
|
15
16
|
|
16
17
|
class_attribute :content_areas
|
17
18
|
self.content_areas = [] # class_attribute:default doesn't work until Rails 5.2
|
@@ -66,7 +67,7 @@ module ViewComponent
|
|
66
67
|
# Assign captured content passed to component as a block to @content
|
67
68
|
@content = view_context.capture(self, &block) if block_given?
|
68
69
|
|
69
|
-
|
70
|
+
before_render
|
70
71
|
|
71
72
|
if render?
|
72
73
|
send(self.class.call_method_name(@variant))
|
@@ -77,6 +78,10 @@ module ViewComponent
|
|
77
78
|
@current_template = old_current_template
|
78
79
|
end
|
79
80
|
|
81
|
+
def before_render
|
82
|
+
before_render_check
|
83
|
+
end
|
84
|
+
|
80
85
|
def before_render_check
|
81
86
|
# noop
|
82
87
|
end
|
@@ -101,9 +106,9 @@ module ViewComponent
|
|
101
106
|
@controller ||= view_context.controller
|
102
107
|
end
|
103
108
|
|
104
|
-
# Provides a proxy to access helper methods
|
109
|
+
# Provides a proxy to access helper methods from the context of the current controller
|
105
110
|
def helpers
|
106
|
-
@helpers ||= view_context
|
111
|
+
@helpers ||= controller.view_context
|
107
112
|
end
|
108
113
|
|
109
114
|
# Removes the first part of the path and the extension.
|
@@ -188,9 +193,7 @@ module ViewComponent
|
|
188
193
|
end
|
189
194
|
|
190
195
|
def compiled?
|
191
|
-
|
192
|
-
|
193
|
-
@compiled && ActionView::Base.cache_template_loading
|
196
|
+
CompileCache.compiled?(self)
|
194
197
|
end
|
195
198
|
|
196
199
|
# Compile templates to instance methods, assuming they haven't been compiled already.
|
@@ -205,6 +208,12 @@ module ViewComponent
|
|
205
208
|
return false
|
206
209
|
end
|
207
210
|
|
211
|
+
if instance_methods(false).include?(:before_render_check)
|
212
|
+
ActiveSupport::Deprecation.warn(
|
213
|
+
"`before_render_check` will be removed in v3.0.0. Use `before_render` instead."
|
214
|
+
)
|
215
|
+
end
|
216
|
+
|
208
217
|
# Remove any existing singleton methods,
|
209
218
|
# as Ruby warns when redefining a method.
|
210
219
|
remove_possible_singleton_method(:variants)
|
@@ -239,8 +248,8 @@ module ViewComponent
|
|
239
248
|
# starting line number so errors that are raised will point to the
|
240
249
|
# correct line in the component template.
|
241
250
|
line_number =
|
242
|
-
if ActionView::Base.respond_to?(:
|
243
|
-
ActionView::Base.
|
251
|
+
if ActionView::Base.respond_to?(:annotate_rendered_view_with_filenames) &&
|
252
|
+
ActionView::Base.annotate_rendered_view_with_filenames
|
244
253
|
-2
|
245
254
|
else
|
246
255
|
-1
|
@@ -260,7 +269,7 @@ module ViewComponent
|
|
260
269
|
RUBY
|
261
270
|
end
|
262
271
|
|
263
|
-
|
272
|
+
CompileCache.register self
|
264
273
|
end
|
265
274
|
|
266
275
|
# we'll eventually want to update this to support other types
|
@@ -298,7 +307,16 @@ module ViewComponent
|
|
298
307
|
parameter = validate_default ? collection_parameter : provided_collection_parameter
|
299
308
|
|
300
309
|
return unless parameter
|
301
|
-
return if
|
310
|
+
return if initialize_parameters.map(&:last).include?(parameter)
|
311
|
+
|
312
|
+
# If Ruby cannot parse the component class, then the initalize
|
313
|
+
# parameters will be empty and ViewComponent will not be able to render
|
314
|
+
# the component.
|
315
|
+
if initialize_parameters.empty?
|
316
|
+
raise ArgumentError.new(
|
317
|
+
"#{self} initializer is empty or invalid."
|
318
|
+
)
|
319
|
+
end
|
302
320
|
|
303
321
|
raise ArgumentError.new(
|
304
322
|
"#{self} initializer must accept " \
|
@@ -308,6 +326,10 @@ module ViewComponent
|
|
308
326
|
|
309
327
|
private
|
310
328
|
|
329
|
+
def initialize_parameters
|
330
|
+
instance_method(:initialize).parameters
|
331
|
+
end
|
332
|
+
|
311
333
|
def provided_collection_parameter
|
312
334
|
@provided_collection_parameter ||= nil
|
313
335
|
end
|
@@ -341,7 +363,22 @@ module ViewComponent
|
|
341
363
|
|
342
364
|
def matching_views_in_source_location
|
343
365
|
return [] unless source_location
|
344
|
-
|
366
|
+
|
367
|
+
location_without_extension = source_location.chomp(File.extname(source_location))
|
368
|
+
|
369
|
+
extenstions = ActionView::Template.template_handler_extensions.join(",")
|
370
|
+
|
371
|
+
# view files in the same directory as te component
|
372
|
+
sidecar_files = Dir["#{location_without_extension}.*{#{extenstions}}"]
|
373
|
+
|
374
|
+
# view files in a directory named like the component
|
375
|
+
directory = File.dirname(source_location)
|
376
|
+
filename = File.basename(source_location, ".rb")
|
377
|
+
component_name = name.demodulize.underscore
|
378
|
+
|
379
|
+
sidecar_directory_files = Dir["#{directory}/#{component_name}/#{filename}.*{#{extenstions}}"]
|
380
|
+
|
381
|
+
(sidecar_files - [source_location] + sidecar_directory_files)
|
345
382
|
end
|
346
383
|
|
347
384
|
def templates
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ViewComponent
|
4
|
+
# Keeps track of which templates have already been compiled
|
5
|
+
# This is not part of the public API
|
6
|
+
module CompileCache
|
7
|
+
mattr_accessor :cache, instance_reader: false, instance_accessor: false do
|
8
|
+
Set.new
|
9
|
+
end
|
10
|
+
module_function
|
11
|
+
|
12
|
+
def register(klass)
|
13
|
+
cache << klass
|
14
|
+
end
|
15
|
+
|
16
|
+
def compiled?(klass)
|
17
|
+
cache.include? klass
|
18
|
+
end
|
19
|
+
|
20
|
+
def invalidate!
|
21
|
+
cache.clear
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -69,6 +69,10 @@ module ViewComponent
|
|
69
69
|
get "#{options.preview_route}/*path", to: "view_components#previews", as: :preview_view_component, internal: true
|
70
70
|
end
|
71
71
|
end
|
72
|
+
|
73
|
+
app.executor.to_run :before do
|
74
|
+
CompileCache.invalidate! unless ActionView::Base.cache_template_loading
|
75
|
+
end
|
72
76
|
end
|
73
77
|
end
|
74
78
|
end
|
@@ -7,7 +7,7 @@ module ViewComponent
|
|
7
7
|
include Capybara::Minitest::Assertions
|
8
8
|
|
9
9
|
def page
|
10
|
-
Capybara::Node::Simple.new(@
|
10
|
+
Capybara::Node::Simple.new(@rendered_component)
|
11
11
|
end
|
12
12
|
|
13
13
|
def refute_component_rendered
|
@@ -17,10 +17,12 @@ module ViewComponent
|
|
17
17
|
warn "WARNING in `ViewComponent::TestHelpers`: You must add `capybara` to your Gemfile to use Capybara assertions."
|
18
18
|
end
|
19
19
|
|
20
|
+
attr_reader :rendered_component
|
21
|
+
|
20
22
|
def render_inline(component, **args, &block)
|
21
|
-
@
|
23
|
+
@rendered_component = controller.view_context.render(component, args, &block)
|
22
24
|
|
23
|
-
Nokogiri::HTML.fragment(@
|
25
|
+
Nokogiri::HTML.fragment(@rendered_component)
|
24
26
|
end
|
25
27
|
|
26
28
|
def controller
|
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.
|
4
|
+
version: 2.11.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-
|
11
|
+
date: 2020-06-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -142,6 +142,34 @@ dependencies:
|
|
142
142
|
- - "~>"
|
143
143
|
- !ruby/object:Gem::Version
|
144
144
|
version: 0.13.0
|
145
|
+
- !ruby/object:Gem::Dependency
|
146
|
+
name: simplecov
|
147
|
+
requirement: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - "~>"
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: 0.18.0
|
152
|
+
type: :development
|
153
|
+
prerelease: false
|
154
|
+
version_requirements: !ruby/object:Gem::Requirement
|
155
|
+
requirements:
|
156
|
+
- - "~>"
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: 0.18.0
|
159
|
+
- !ruby/object:Gem::Dependency
|
160
|
+
name: simplecov-erb
|
161
|
+
requirement: !ruby/object:Gem::Requirement
|
162
|
+
requirements:
|
163
|
+
- - "~>"
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0.1'
|
166
|
+
type: :development
|
167
|
+
prerelease: false
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
requirements:
|
170
|
+
- - "~>"
|
171
|
+
- !ruby/object:Gem::Version
|
172
|
+
version: '0.1'
|
145
173
|
description:
|
146
174
|
email:
|
147
175
|
- opensource+view_component@github.com
|
@@ -172,6 +200,7 @@ files:
|
|
172
200
|
- lib/view_component.rb
|
173
201
|
- lib/view_component/base.rb
|
174
202
|
- lib/view_component/collection.rb
|
203
|
+
- lib/view_component/compile_cache.rb
|
175
204
|
- lib/view_component/engine.rb
|
176
205
|
- lib/view_component/preview.rb
|
177
206
|
- lib/view_component/previewable.rb
|