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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8bc0f4ab66dfca6a89f9882d0df39f5f4a84f479f26dfd0b3334b43fe4f9b4ed
4
- data.tar.gz: 39128211a1338af6c78a54085b59f48a98411dc282e6248f03fc56229a6d7cd0
3
+ metadata.gz: eec4666da3c79cf466b01641169db9055d1201684acf30169d05cfb9129a7dc2
4
+ data.tar.gz: 6cfe7e0dee9f5becab077ba05093ea04a38927d2b4cf22d58bff9c4a3cbcd0e3
5
5
  SHA512:
6
- metadata.gz: 1aa9fb3d7361efa6a98465c1513e3296abc4495ad30393ed9fbf3febc919a8669184fe2e28fe5afa9666a42b71958787b2bf091b71d4e9889b020d0495769abf
7
- data.tar.gz: 2ce5ece866ddc50d8aa01cd139c230c10b26aee9d8bbc49f7421b8d191e5b84dbd57d1247b9973f936932dcde9bd925f96c2d3114ac201d98511157b7643bac0
6
+ metadata.gz: f3a49b502f6f62c611ff8f700d4a84a2fe30e2a203a57a81964a2d99b005bd20ce431a93ba2319d7ac0dd1a358147d4bec97747b97ea3c386b2e27b5a4a4c241
7
+ data.tar.gz: 4c98f7b14919f9d56ec3a73e179be99f19cd2ec4157591bf3d1661cd5e8fd9a893110b4ba67dc8f0e79095b8d13974ed8a59904df41016f59515998316c03f2b
@@ -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`, assertion against the return values of `render_inline`, which is an instance of `Nokogiri::HTML::DocumentFragment`:
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
 
@@ -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
- before_render_check
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
- @compiled ||= false
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?(:annotate_template_file_names) &&
243
- ActionView::Base.annotate_template_file_names
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
- @compiled = true
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 instance_method(:initialize).parameters.map(&:last).include?(parameter)
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
- (Dir["#{source_location.chomp(File.extname(source_location))}.*{#{ActionView::Template.template_handler_extensions.join(',')}}"] - [source_location])
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(@raw)
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
- @raw = controller.view_context.render(component, args, &block)
23
+ @rendered_component = controller.view_context.render(component, args, &block)
22
24
 
23
- Nokogiri::HTML.fragment(@raw)
25
+ Nokogiri::HTML.fragment(@rendered_component)
24
26
  end
25
27
 
26
28
  def controller
@@ -3,7 +3,7 @@
3
3
  module ViewComponent
4
4
  module VERSION
5
5
  MAJOR = 2
6
- MINOR = 6
6
+ MINOR = 11
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.6.0
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-05-14 00:00:00.000000000 Z
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