view_component 4.0.0.alpha5 → 4.0.0.alpha6

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 544de384800ad5d0870666b9b1fdb3a74da9fd59d77f78e0d72ee5e134a3c307
4
- data.tar.gz: 430c3652f5abc6fef9815b937274f66ebac34de297318e1ca6839589fa290ddf
3
+ metadata.gz: dbca6043a0eebf7c6f63358c7b1c193ef59170b81a35eb9290f211d365bfb3cb
4
+ data.tar.gz: 033db44d3dab35956b7ab8ac1a1a12673b7e82a0d4e0bbf3d3e791234dbcf757
5
5
  SHA512:
6
- metadata.gz: f30651af979f6c1c5fd43514d04cef3f6430982198e593375d61ce79190e83b8b2d61d7cccb67607248f0b791346b09db33b4705e81cb257b87bd8255c5b2d63
7
- data.tar.gz: 956af9c486961785821e9401c9c1ce6b187f3f21a0425d5d6d36602f384f3beb79629f9be2e743e13f525bdc15498480565989cbdb37952a7b0e1cbb2aaabbb0
6
+ metadata.gz: ddd3ac59908bf697da69f8f0fa29b2e779e468a07a6f37e96240165797fce088bbf6937473363d349b7e48fa8011434377b52345c8f60204ff94a7a794a4fee8
7
+ data.tar.gz: c8fac0e676aa9632453337a13169b690ed36bc9cd65e0de754c63f77878ba1b598c9a344f8f547a8b7c530c0e12c285de2fc0eee3afa4a1f7fe8805293414b8e
@@ -47,7 +47,7 @@ module ViewComponent
47
47
 
48
48
  # :doc:
49
49
  def default_preview_layout
50
- ViewComponent::Base.config.default_preview_layout
50
+ ViewComponent::Base.config.previews.default_layout
51
51
  end
52
52
 
53
53
  # :doc:
@@ -64,7 +64,11 @@ module ViewComponent
64
64
  if preview
65
65
  @preview = ViewComponent::Preview.find(preview)
66
66
  else
67
+ # TODO: This branch is covered in #test_returns_404_when_preview_does_not_exist,
68
+ # but Simplecov doesn't always mark it as covered.
69
+ # :nocov:
67
70
  raise AbstractController::ActionNotFound, "Component preview '#{params[:path]}' not found."
71
+ # :nocov:
68
72
  end
69
73
  end
70
74
 
@@ -95,7 +99,7 @@ module ViewComponent
95
99
  end
96
100
 
97
101
  def prepend_preview_examples_view_path
98
- prepend_view_path(ViewComponent::Base.preview_paths)
102
+ prepend_view_path(ViewComponent::Base.previews.paths)
99
103
  end
100
104
  end
101
105
  end
@@ -19,9 +19,7 @@ class ViewComponentsSystemTestController < ActionController::Base # :nodoc:
19
19
  def validate_file_path
20
20
  base_path = ::File.realpath(self.class.temp_dir)
21
21
  @path = ::File.realpath(params.permit(:file)[:file], base_path)
22
- unless @path.start_with?(base_path)
23
- raise ViewComponent::SystemTestControllerNefariousPathError
24
- end
22
+ raise ViewComponent::SystemTestControllerNefariousPathError unless @path.start_with?(base_path)
25
23
  end
26
24
  end
27
25
  end
data/docs/CHANGELOG.md CHANGED
@@ -10,6 +10,40 @@ nav_order: 6
10
10
 
11
11
  ## main
12
12
 
13
+ ## 4.0.0.alpha6
14
+
15
+ * BREAKING: Remove `config.test_controller` in favor of `vc_test_controller_class` test helper method.
16
+
17
+ *Joel Hawksley*
18
+
19
+ * BREAKING: `config.component_parent_class` is now `config.generate.component_parent_class`, moving the generator-specific option to the generator configuration namespace.
20
+
21
+ *Joel Hawksley*
22
+
23
+ * BREAKING: Move previews-related configuration (`enabled`, `route`, `paths`, `default_layout`, `controller`) to under `previews` namespace.
24
+
25
+ *Joel Hawksley*
26
+
27
+ * Add template annotations for components with `def call`.
28
+
29
+ *Joel Hawksley*
30
+
31
+ * Add support for including Turbo::StreamsHelper.
32
+
33
+ *Stephen Nelson*
34
+
35
+ * Update documentation on performance to reflect more representative benchmark showing 2-3x speed increase over partials.
36
+
37
+ *Joel Hawksley*
38
+
39
+ * Add documentation note about instrumentation negatively affecting performance.
40
+
41
+ *Joel Hawksley*
42
+
43
+ * Revert object shapes optimization due to lack of evidence of improvement.
44
+
45
+ *Joel Hawksley*
46
+
13
47
  ## 4.0.0.alpha5
14
48
 
15
49
  * BREAKING: `config.view_component_path` is now `config.generate.path`, as components have long since been able to exist in any directory.
@@ -20,10 +54,6 @@ nav_order: 6
20
54
 
21
55
  *Joel Hawksley*
22
56
 
23
- * Add internal optimization for Ruby object shapes.
24
-
25
- *Adam Hess*, *Joel Hawksley*
26
-
27
57
  ## 4.0.0.alpha4
28
58
 
29
59
  * BREAKING: Remove default initializer from `ViewComponent::Base`. Previously, `ViewComponent::Base` defined a catch-all initializer that allowed components without an initializer defined to be passed arbitrary arguments.
@@ -156,6 +186,24 @@ This release makes the following breaking changes:
156
186
 
157
187
  *Simon Fish*
158
188
 
189
+ * Reduce string allocations during compilation.
190
+
191
+ *Jonathan del Strother*
192
+
193
+ ## 3.23.2
194
+
195
+ * Include .tt files in published gem. Fixes templates not being available when using generators.
196
+
197
+ *Florian Aßmann*
198
+
199
+ ## 3.23.1
200
+
201
+ * Restore Rake tasks in published gem.
202
+
203
+ *Franz Liedke*
204
+
205
+ ## 3.23.0
206
+
159
207
  * Add docs about Slack channel in Ruby Central workspace. (Join us! #oss-view-component). Email joelhawksley@github.com for an invite.
160
208
 
161
209
  *Joel Hawksley
@@ -5,7 +5,6 @@ require "active_support/configurable"
5
5
  require "view_component/collection"
6
6
  require "view_component/compile_cache"
7
7
  require "view_component/compiler"
8
- require "view_component/component_local_config"
9
8
  require "view_component/config"
10
9
  require "view_component/errors"
11
10
  require "view_component/inline_template"
@@ -35,15 +34,6 @@ module ViewComponent
35
34
  class << self
36
35
  delegate(*ViewComponent::Config.defaults.keys, to: :config)
37
36
 
38
- # Redefine `new` so we can pre-allocate instance variables to optimize
39
- # for Ruby object shapes.
40
- def new(...)
41
- instance = allocate
42
- instance.__vc_pre_allocate_instance_variables
43
- instance.send(:initialize, ...)
44
- instance
45
- end
46
-
47
37
  # Returns the current config.
48
38
  #
49
39
  # @return [ActiveSupport::OrderedOptions]
@@ -57,29 +47,6 @@ module ViewComponent
57
47
  end
58
48
  end
59
49
 
60
- def __vc_pre_allocate_instance_variables
61
- @__vc_parent_render_level = 0
62
- @__vc_set_slots = {}
63
- @__vc_content_evaluated = false
64
- @current_template = nil
65
- @output_buffer = nil
66
- @lookup_context = nil
67
- @view_flow = nil
68
- @view_context = nil
69
- @virtual_path = nil
70
- @__vc_ancestor_calls = nil
71
- @__vc_controller = nil
72
- @__vc_content = :unset # some behaviors depend on checking for nil
73
- @__vc_content_set_by_with_content = nil
74
- @__vc_helpers = nil
75
- @__vc_inline_template = nil
76
- @__vc_inline_template_defined = nil
77
- @__vc_render_in_block = nil
78
- @__vc_request = nil
79
- @__vc_requested_details = nil
80
- @__vc_original_view_context = nil
81
- end
82
-
83
50
  include ActionView::Helpers
84
51
  include ERB::Escape
85
52
  include ActiveSupport::CoreExt::ERBUtil
@@ -89,7 +56,6 @@ module ViewComponent
89
56
  include ViewComponent::Slotable
90
57
  include ViewComponent::Translatable
91
58
  include ViewComponent::WithContentHelper
92
- include ViewComponent::ComponentLocalConfig
93
59
 
94
60
  # For CSRF authenticity tokens in forms
95
61
  delegate :form_authenticity_token, :protect_against_forgery?, :config, to: :helpers
@@ -97,9 +63,16 @@ module ViewComponent
97
63
  # HTML construction methods
98
64
  delegate :output_buffer, :lookup_context, :view_renderer, :view_flow, to: :helpers
99
65
 
66
+ # For Turbo::StreamsHelper
67
+ delegate :formats, :formats=, to: :helpers
68
+
100
69
  # For Content Security Policy nonces
101
70
  delegate :content_security_policy_nonce, to: :helpers
102
71
 
72
+ # Config option that strips trailing whitespace in templates before compiling them.
73
+ class_attribute :__vc_strip_trailing_whitespace, instance_accessor: false, instance_predicate: false
74
+ self.__vc_strip_trailing_whitespace = false # class_attribute:default doesn't work until Rails 5.2
75
+
103
76
  attr_accessor :__vc_original_view_context
104
77
  attr_reader :current_template
105
78
 
@@ -146,12 +119,14 @@ module ViewComponent
146
119
  @__vc_requested_details ||= @lookup_context.vc_requested_details
147
120
 
148
121
  # For caching, such as #cache_if
122
+ @current_template = nil unless defined?(@current_template)
149
123
  old_current_template = @current_template
150
124
 
151
- if block && __vc_content_set_by_with_content?
125
+ if block && defined?(@__vc_content_set_by_with_content)
152
126
  raise DuplicateContentError.new(self.class.name)
153
127
  end
154
128
 
129
+ @__vc_content_evaluated = false
155
130
  @__vc_render_in_block = block
156
131
 
157
132
  before_render
@@ -170,6 +145,11 @@ module ViewComponent
170
145
  end
171
146
  end
172
147
 
148
+ if ActionView::Base.annotate_rendered_view_with_filenames && current_template.inline_call? && request&.format == :html
149
+ identifier = defined?(Rails.root) ? self.class.identifier.sub("#{Rails.root}/", "") : self.class.identifier
150
+ value = "<!-- BEGIN #{identifier} -->".html_safe + value + "<!-- END #{identifier} -->".html_safe
151
+ end
152
+
173
153
  value
174
154
  else
175
155
  ""
@@ -205,12 +185,16 @@ module ViewComponent
205
185
  #
206
186
  # When rendering the parent inside an .erb template, use `#render_parent` instead.
207
187
  def render_parent_to_string
208
- target_render = self.class.instance_variable_get(:@__vc_ancestor_calls)[@__vc_parent_render_level]
209
- @__vc_parent_render_level += 1
188
+ @__vc_parent_render_level ||= 0 # ensure a good starting value
210
189
 
211
- target_render.bind_call(self, @__vc_requested_details)
212
- ensure
213
- @__vc_parent_render_level -= 1
190
+ begin
191
+ target_render = self.class.instance_variable_get(:@__vc_ancestor_calls)[@__vc_parent_render_level]
192
+ @__vc_parent_render_level += 1
193
+
194
+ target_render.bind_call(self, @__vc_requested_details)
195
+ ensure
196
+ @__vc_parent_render_level -= 1
197
+ end
214
198
  end
215
199
 
216
200
  # Optional content to be returned before the rendered template.
@@ -333,12 +317,12 @@ module ViewComponent
333
317
  # @return [String]
334
318
  def content
335
319
  @__vc_content_evaluated = true
336
- return @__vc_content if @__vc_content != :unset
320
+ return @__vc_content if defined?(@__vc_content)
337
321
 
338
322
  @__vc_content =
339
323
  if __vc_render_in_block_provided?
340
324
  view_context.capture(self, &@__vc_render_in_block)
341
- elsif __vc_content_set_by_with_content?
325
+ elsif __vc_content_set_by_with_content_defined?
342
326
  @__vc_content_set_by_with_content
343
327
  end
344
328
  end
@@ -347,7 +331,7 @@ module ViewComponent
347
331
  #
348
332
  # @return [Boolean]
349
333
  def content?
350
- __vc_render_in_block_provided? || __vc_content_set_by_with_content?
334
+ __vc_render_in_block_provided? || __vc_content_set_by_with_content_defined?
351
335
  end
352
336
 
353
337
  private
@@ -355,15 +339,15 @@ module ViewComponent
355
339
  attr_reader :view_context
356
340
 
357
341
  def __vc_render_in_block_provided?
358
- @view_context && @__vc_render_in_block
342
+ defined?(@view_context) && @view_context && @__vc_render_in_block
359
343
  end
360
344
 
361
- def __vc_content_set_by_with_content?
362
- !@__vc_content_set_by_with_content.nil?
345
+ def __vc_content_set_by_with_content_defined?
346
+ defined?(@__vc_content_set_by_with_content)
363
347
  end
364
348
 
365
349
  def content_evaluated?
366
- @__vc_content_evaluated
350
+ defined?(@__vc_content_evaluated) && @__vc_content_evaluated
367
351
  end
368
352
 
369
353
  def maybe_escape_html(text)
@@ -390,26 +374,6 @@ module ViewComponent
390
374
  end
391
375
  end
392
376
 
393
- # Set the controller used for testing components:
394
- #
395
- # ```ruby
396
- # config.view_component.test_controller = "MyTestController"
397
- # ```
398
- #
399
- # Defaults to `nil`. If this is falsy, `"ApplicationController"` is used. Can also be
400
- # configured on a per-test basis using `with_controller_class`.
401
- #
402
-
403
- # Parent class for generated components
404
- #
405
- # ```ruby
406
- # config.view_component.component_parent_class = "MyBaseComponent"
407
- # ```
408
- #
409
- # Defaults to nil. If this is falsy, generators will use
410
- # "ApplicationComponent" if defined, "ViewComponent::Base" otherwise.
411
- #
412
-
413
377
  # Configuration for generators.
414
378
  #
415
379
  # All options under this namespace default to `false` unless otherwise
@@ -467,6 +431,18 @@ module ViewComponent
467
431
  # ```
468
432
  #
469
433
  # Defaults to `false`.
434
+ #
435
+ # #### component_parent_class
436
+ #
437
+ # Parent class for generated components
438
+ #
439
+ # ```ruby
440
+ # config.view_component.generate.component_parent_class = "MyBaseComponent"
441
+ # ```
442
+ #
443
+ # Defaults to nil. If this is falsy, generators will use
444
+ # "ApplicationComponent" if defined, "ViewComponent::Base" otherwise.
445
+ #
470
446
 
471
447
  class << self
472
448
  # The file path of the component Ruby file.
@@ -575,7 +551,7 @@ module ViewComponent
575
551
  child.with_collection_parameter provided_collection_parameter
576
552
 
577
553
  if instance_methods(false).include?(:render_template_for)
578
- vc_ancestor_calls = (!@__vc_ancestor_calls.nil?) ? @__vc_ancestor_calls.dup : []
554
+ vc_ancestor_calls = defined?(@__vc_ancestor_calls) ? @__vc_ancestor_calls.dup : []
579
555
 
580
556
  vc_ancestor_calls.unshift(instance_method(:render_template_for))
581
557
  child.instance_variable_set(:@__vc_ancestor_calls, vc_ancestor_calls)
@@ -624,38 +600,16 @@ module ViewComponent
624
600
  # end
625
601
  # ```
626
602
  #
627
- # @deprecated Use the new component-local configuration option instead.
628
- #
629
- # ```ruby
630
- # class MyComponent < ViewComponent::Base
631
- # configure_view_component do |config|
632
- # config.strip_trailing_whitespace = true
633
- # end
634
- # end
635
- # ```
636
- #
637
603
  # @param value [Boolean] Whether to strip newlines.
638
604
  def strip_trailing_whitespace(value = true)
639
- ViewComponent::Deprecation.deprecation_warning(
640
- "strip_trailing_whitespace",
641
- <<~DOC
642
- Use the new component-local configuration option instead:
643
-
644
- class #{self.class.name} < ViewComponent::Base
645
- configure_view_component do |config|
646
- config.strip_trailing_whitespace = #{value}
647
- end
648
- end
649
- DOC
650
- )
651
- view_component_config.strip_trailing_whitespace = value
605
+ self.__vc_strip_trailing_whitespace = value
652
606
  end
653
607
 
654
608
  # Whether trailing whitespace will be stripped before compilation.
655
609
  #
656
610
  # @return [Boolean]
657
611
  def strip_trailing_whitespace?
658
- view_component_config.strip_trailing_whitespace
612
+ __vc_strip_trailing_whitespace
659
613
  end
660
614
 
661
615
  # Ensure the component initializer accepts the
@@ -108,10 +108,7 @@ module ViewComponent
108
108
 
109
109
  errors << "Couldn't find a template file or inline render method for #{@component}." if @templates.empty?
110
110
 
111
- # We currently allow components to have both an inline call method and a template for a variant, with the
112
- # inline call method overriding the template. We should aim to change this in v4 to instead
113
- # raise an error.
114
- @templates.reject(&:inline_call?)
111
+ @templates
115
112
  .map { |template| [template.variant, template.format] }
116
113
  .tally
117
114
  .select { |_, count| count > 1 }
@@ -13,14 +13,8 @@ module ViewComponent
13
13
  def defaults
14
14
  ActiveSupport::OrderedOptions.new.merge!({
15
15
  generate: default_generate_options,
16
- preview_controller: "ViewComponentsController",
17
- preview_route: "/rails/view_components",
18
- instrumentation_enabled: false,
19
- component_parent_class: nil,
20
- show_previews: Rails.env.development? || Rails.env.test?,
21
- preview_paths: default_preview_paths,
22
- test_controller: "ApplicationController",
23
- default_preview_layout: nil
16
+ previews: default_previews_options,
17
+ instrumentation_enabled: false
24
18
  })
25
19
  end
26
20
 
@@ -84,7 +78,7 @@ module ViewComponent
84
78
  #
85
79
  # Required when there is more than one path defined in preview_paths.
86
80
  # Defaults to `""`. If this is blank, the generator will use
87
- # `ViewComponent.config.preview_paths` if defined,
81
+ # `ViewComponent.config.previews.paths` if defined,
88
82
  # `"test/components/previews"` otherwise
89
83
  #
90
84
  # #### `#use_component_path_for_rspec_tests`
@@ -100,49 +94,46 @@ module ViewComponent
100
94
  # `app/views/components`, then the generator will create a new spec file
101
95
  # in `spec/views/components/` rather than the default `spec/components/`.
102
96
 
103
- # @!attribute preview_controller
104
- # @return [String]
105
- # The controller used for previewing components.
106
- # Defaults to `ViewComponentsController`.
107
-
108
- # @!attribute preview_route
109
- # @return [String]
110
- # The entry route for component previews.
111
- # Defaults to `"/rails/view_components"`.
97
+ # @!attribute previews
98
+ # @return [ActiveSupport::OrderedOptions]
99
+ # The subset of configuration options relating to previews.
100
+ #
101
+ # #### `#controller`
102
+ #
103
+ # The controller used for previewing components. Defaults to `ViewComponentsController`:
104
+ #
105
+ # config.view_component.previews.controller = "MyPreviewController"
106
+ #
107
+ # #### `#route`
108
+ #
109
+ # The entry route for component previews. Defaults to `/rails/view_components`:
110
+ #
111
+ # config.view_component.previews.route = "/my_previews"
112
+ #
113
+ # #### `#enabled`
114
+ #
115
+ # Whether component previews are enabled. Defaults to `true` in development and test environments:
116
+ #
117
+ # config.view_component.previews.enabled = false
118
+ #
119
+ # #### `#default_layout`
120
+ #
121
+ # A custom default layout used for the previews index page and individual previews. Defaults to `false`:
122
+ #
123
+ # config.view_component.previews.default_layout = false
124
+ #
112
125
 
113
126
  # @!attribute instrumentation_enabled
114
127
  # @return [Boolean]
115
128
  # Whether ActiveSupport notifications are enabled.
116
129
  # Defaults to `false`.
117
130
 
118
- # @!attribute component_parent_class
119
- # @return [String]
120
- # The parent class from which generated components will inherit.
121
- # Defaults to `nil`. If this is falsy, generators will use
122
- # `"ApplicationComponent"` if defined, `"ViewComponent::Base"` otherwise.
123
-
124
- # @!attribute show_previews
125
- # @return [Boolean]
126
- # Whether component previews are enabled.
127
- # Defaults to `true` in development and test environments.
128
-
129
- # @!attribute preview_paths
130
- # @return [Array<String>]
131
- # The locations in which component previews will be looked up.
132
- # Defaults to `['test/components/previews']` relative to your Rails root.
133
-
134
131
  # @!attribute test_controller
135
132
  # @return [String]
136
133
  # The controller used for testing components.
137
134
  # Can also be configured on a per-test basis using `#with_controller_class`.
138
135
  # Defaults to `ApplicationController`.
139
136
 
140
- # @!attribute default_preview_layout
141
- # @return [String]
142
- # A custom default layout used for the previews index page and individual
143
- # previews.
144
- # Defaults to `nil`. If this is falsy, `"component_preview"` is used.
145
-
146
137
  def default_preview_paths
147
138
  (default_rails_preview_paths + default_rails_engines_preview_paths).uniq
148
139
  end
@@ -173,6 +164,16 @@ module ViewComponent
173
164
  options.path = "app/components"
174
165
  options
175
166
  end
167
+
168
+ def default_previews_options
169
+ options = ActiveSupport::OrderedOptions.new
170
+ options.controller = "ViewComponentsController"
171
+ options.route = "/rails/view_components"
172
+ options.enabled = Rails.env.development? || Rails.env.test?
173
+ options.default_layout = false
174
+ options.paths = default_preview_paths
175
+ options
176
+ end
176
177
  end
177
178
 
178
179
  # @!attribute current
@@ -11,7 +11,7 @@ module ViewComponent
11
11
  initializer "view_component.set_configs" do |app|
12
12
  options = app.config.view_component
13
13
 
14
- %i[generate preview_controller preview_route].each do |config_option|
14
+ %i[generate previews].each do |config_option|
15
15
  options[config_option] ||= ViewComponent::Base.public_send(config_option)
16
16
  end
17
17
  options.instrumentation_enabled = false if options.instrumentation_enabled.nil?
@@ -19,7 +19,7 @@ module ViewComponent
19
19
 
20
20
  if options.show_previews
21
21
  # This is still necessary because when `config.view_component` is declared, `Rails.root` is unspecified.
22
- options.preview_paths << "#{Rails.root}/test/components/previews" if defined?(Rails.root) && Dir.exist?(
22
+ options.previews.paths << "#{Rails.root}/test/components/previews" if defined?(Rails.root) && Dir.exist?(
23
23
  "#{Rails.root}/test/components/previews"
24
24
  )
25
25
  end
@@ -36,8 +36,8 @@ module ViewComponent
36
36
  initializer "view_component.set_autoload_paths" do |app|
37
37
  options = app.config.view_component
38
38
 
39
- if options.show_previews && !options.preview_paths.empty?
40
- paths_to_add = options.preview_paths - ActiveSupport::Dependencies.autoload_paths
39
+ if options.show_previews && !options.previews.paths.empty?
40
+ paths_to_add = options.previews.paths - ActiveSupport::Dependencies.autoload_paths
41
41
  ActiveSupport::Dependencies.autoload_paths.concat(paths_to_add) if paths_to_add.any?
42
42
  end
43
43
  end
@@ -90,17 +90,17 @@ module ViewComponent
90
90
 
91
91
  if options.show_previews
92
92
  app.routes.prepend do
93
- preview_controller = options.preview_controller.sub(/Controller$/, "").underscore
93
+ preview_controller = options.previews.controller.sub(/Controller$/, "").underscore
94
94
 
95
95
  get(
96
- options.preview_route,
96
+ options.previews.route,
97
97
  to: "#{preview_controller}#index",
98
98
  as: :preview_view_components,
99
99
  internal: true
100
100
  )
101
101
 
102
102
  get(
103
- "#{options.preview_route}/*path",
103
+ "#{options.previews.route}/*path",
104
104
  to: "#{preview_controller}#previews",
105
105
  as: :preview_view_component,
106
106
  internal: true
@@ -9,7 +9,7 @@ module ViewComponent # :nodoc:
9
9
  def method_missing(method, *args)
10
10
  return super if !method.end_with?("_template")
11
11
 
12
- if @__vc_inline_template_defined
12
+ if defined?(@__vc_inline_template_defined) && @__vc_inline_template_defined
13
13
  raise MultipleInlineTemplatesError
14
14
  end
15
15
 
@@ -38,11 +38,11 @@ module ViewComponent # :nodoc:
38
38
  end
39
39
 
40
40
  def inline_template
41
- @__vc_inline_template
41
+ @__vc_inline_template if defined?(@__vc_inline_template)
42
42
  end
43
43
 
44
44
  def __vc_inline_template_language
45
- @__vc_inline_template_language
45
+ @__vc_inline_template_language if defined?(@__vc_inline_template_language)
46
46
  end
47
47
 
48
48
  def inherited(subclass)
@@ -102,7 +102,7 @@ module ViewComponent # :nodoc:
102
102
  private
103
103
 
104
104
  def preview_paths
105
- Base.preview_paths
105
+ Base.previews.paths
106
106
  end
107
107
  end
108
108
  end
@@ -9,18 +9,13 @@ module ViewComponent
9
9
  attr_writer :__vc_component_instance, :__vc_content_block, :__vc_content
10
10
 
11
11
  def initialize(parent)
12
- @content = nil
13
- @__vc_component_instance = nil
14
- @__vc_content = nil
15
- @__vc_content_block = nil
16
- @__vc_content_set_by_with_content = nil
17
12
  @parent = parent
18
13
  end
19
14
 
20
15
  def content?
21
- return true if @__vc_content.present?
22
- return true if @__vc_content_set_by_with_content.present?
23
- return true if @__vc_content_block.present?
16
+ return true if defined?(@__vc_content) && @__vc_content.present?
17
+ return true if defined?(@__vc_content_set_by_with_content) && @__vc_content_set_by_with_content.present?
18
+ return true if defined?(@__vc_content_block) && @__vc_content_block.present?
24
19
  return false if !__vc_component_instance?
25
20
 
26
21
  @__vc_component_instance.content?
@@ -48,11 +43,11 @@ module ViewComponent
48
43
  # If there is no slot renderable, we evaluate the block passed to
49
44
  # the slot and return it.
50
45
  def to_s
51
- return @content if !@content.nil?
46
+ return @content if defined?(@content)
52
47
 
53
48
  view_context = @parent.send(:view_context)
54
49
 
55
- if !@__vc_content_block.nil? && !@__vc_content_set_by_with_content.nil? && !@__vc_content_set_by_with_content.nil?
50
+ if defined?(@__vc_content_block) && defined?(@__vc_content_set_by_with_content)
56
51
  raise DuplicateSlotContentError.new(self.class.name)
57
52
  end
58
53
 
@@ -60,7 +55,7 @@ module ViewComponent
60
55
  if __vc_component_instance?
61
56
  @__vc_component_instance.__vc_original_view_context = @parent.__vc_original_view_context
62
57
 
63
- if !@__vc_content_block.nil?
58
+ if defined?(@__vc_content_block)
64
59
  # render_in is faster than `parent.render`
65
60
  @__vc_component_instance.render_in(view_context) do |*args|
66
61
  @__vc_content_block.call(*args)
@@ -68,11 +63,11 @@ module ViewComponent
68
63
  else
69
64
  @__vc_component_instance.render_in(view_context)
70
65
  end
71
- elsif !@__vc_content.nil?
66
+ elsif defined?(@__vc_content)
72
67
  @__vc_content
73
- elsif !@__vc_content_block.nil?
68
+ elsif defined?(@__vc_content_block)
74
69
  view_context.capture(&@__vc_content_block)
75
- elsif !@__vc_content_set_by_with_content.nil?
70
+ elsif defined?(@__vc_content_set_by_with_content)
76
71
  @__vc_content_set_by_with_content
77
72
  end
78
73
 
@@ -113,7 +108,7 @@ module ViewComponent
113
108
  private
114
109
 
115
110
  def __vc_component_instance?
116
- !@__vc_component_instance.nil?
111
+ defined?(@__vc_component_instance)
117
112
  end
118
113
  end
119
114
  end
@@ -214,7 +214,8 @@ module ViewComponent
214
214
  # Called by the compiler, as instance methods are not defined when slots are first registered
215
215
  def register_default_slots
216
216
  registered_slots.each do |slot_name, config|
217
- config[:default_method] = instance_methods.find { |method_name| method_name == :"default_#{slot_name}" }
217
+ default_method_name = :"default_#{slot_name}"
218
+ config[:default_method] = instance_methods.find { |method_name| method_name == default_method_name }
218
219
 
219
220
  registered_slots[slot_name] = config
220
221
  end
@@ -420,6 +421,8 @@ module ViewComponent
420
421
  end
421
422
  end
422
423
 
424
+ @__vc_set_slots ||= {}
425
+
423
426
  if slot_definition[:collection]
424
427
  @__vc_set_slots[slot_name] ||= []
425
428
  @__vc_set_slots[slot_name].push(slot)
@@ -433,7 +436,7 @@ module ViewComponent
433
436
  def set_polymorphic_slot(slot_name, poly_type = nil, *args, **kwargs, &block)
434
437
  slot_definition = self.class.registered_slots[slot_name]
435
438
 
436
- if !slot_definition[:collection] && @__vc_set_slots[slot_name]
439
+ if !slot_definition[:collection] && defined?(@__vc_set_slots) && @__vc_set_slots[slot_name]
437
440
  raise ContentAlreadySetForPolymorphicSlotError.new(slot_name)
438
441
  end
439
442
 
@@ -73,7 +73,7 @@ module ViewComponent
73
73
  # @param params [Hash] Parameters to be passed to the preview.
74
74
  # @return [Nokogiri::HTML5]
75
75
  def render_preview(name, from: __vc_test_helpers_preview_class, params: {})
76
- previews_controller = __vc_test_helpers_build_controller(Rails.application.config.view_component.preview_controller.constantize)
76
+ previews_controller = __vc_test_helpers_build_controller(Rails.application.config.view_component.previews.controller.constantize)
77
77
 
78
78
  # From what I can tell, it's not possible to overwrite all request parameters
79
79
  # at once, so we set them individually here.
@@ -233,7 +233,20 @@ module ViewComponent
233
233
  #
234
234
  # @return [ActionController::Base]
235
235
  def vc_test_controller
236
- @vc_test_controller ||= __vc_test_helpers_build_controller(Base.test_controller.constantize)
236
+ @vc_test_controller ||= __vc_test_helpers_build_controller(vc_test_controller_class)
237
+ end
238
+
239
+ # Set the controller used by `render_inline`:
240
+ #
241
+ # ```ruby
242
+ # def vc_test_controller_class
243
+ # MyTestController
244
+ # end
245
+ # ```
246
+ def vc_test_controller_class
247
+ return @__vc_test_controller_class if defined?(@__vc_test_controller_class)
248
+
249
+ defined?(ApplicationController) ? ApplicationController : ActionController::Base
237
250
  end
238
251
 
239
252
  # Access the request used by `render_inline`:
@@ -5,7 +5,7 @@ module ViewComponent
5
5
  MAJOR = 4
6
6
  MINOR = 0
7
7
  PATCH = 0
8
- PRE = "alpha5"
8
+ PRE = "alpha6"
9
9
 
10
10
  STRING = [MAJOR, MINOR, PATCH, PRE].compact.join(".")
11
11
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: view_component
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0.alpha5
4
+ version: 4.0.0.alpha6
5
5
  platform: ruby
6
6
  authors:
7
7
  - ViewComponent Team
@@ -43,412 +43,6 @@ dependencies:
43
43
  - - "~>"
44
44
  - !ruby/object:Gem::Version
45
45
  version: '1'
46
- - !ruby/object:Gem::Dependency
47
- name: allocation_stats
48
- requirement: !ruby/object:Gem::Requirement
49
- requirements:
50
- - - ">="
51
- - !ruby/object:Gem::Version
52
- version: '0'
53
- type: :development
54
- prerelease: false
55
- version_requirements: !ruby/object:Gem::Requirement
56
- requirements:
57
- - - ">="
58
- - !ruby/object:Gem::Version
59
- version: '0'
60
- - !ruby/object:Gem::Dependency
61
- name: appraisal
62
- requirement: !ruby/object:Gem::Requirement
63
- requirements:
64
- - - "~>"
65
- - !ruby/object:Gem::Version
66
- version: '2'
67
- type: :development
68
- prerelease: false
69
- version_requirements: !ruby/object:Gem::Requirement
70
- requirements:
71
- - - "~>"
72
- - !ruby/object:Gem::Version
73
- version: '2'
74
- - !ruby/object:Gem::Dependency
75
- name: benchmark-ips
76
- requirement: !ruby/object:Gem::Requirement
77
- requirements:
78
- - - "~>"
79
- - !ruby/object:Gem::Version
80
- version: '2'
81
- type: :development
82
- prerelease: false
83
- version_requirements: !ruby/object:Gem::Requirement
84
- requirements:
85
- - - "~>"
86
- - !ruby/object:Gem::Version
87
- version: '2'
88
- - !ruby/object:Gem::Dependency
89
- name: better_html
90
- requirement: !ruby/object:Gem::Requirement
91
- requirements:
92
- - - ">="
93
- - !ruby/object:Gem::Version
94
- version: '0'
95
- type: :development
96
- prerelease: false
97
- version_requirements: !ruby/object:Gem::Requirement
98
- requirements:
99
- - - ">="
100
- - !ruby/object:Gem::Version
101
- version: '0'
102
- - !ruby/object:Gem::Dependency
103
- name: bundler
104
- requirement: !ruby/object:Gem::Requirement
105
- requirements:
106
- - - "~>"
107
- - !ruby/object:Gem::Version
108
- version: '2'
109
- type: :development
110
- prerelease: false
111
- version_requirements: !ruby/object:Gem::Requirement
112
- requirements:
113
- - - "~>"
114
- - !ruby/object:Gem::Version
115
- version: '2'
116
- - !ruby/object:Gem::Dependency
117
- name: capybara
118
- requirement: !ruby/object:Gem::Requirement
119
- requirements:
120
- - - "~>"
121
- - !ruby/object:Gem::Version
122
- version: '3'
123
- type: :development
124
- prerelease: false
125
- version_requirements: !ruby/object:Gem::Requirement
126
- requirements:
127
- - - "~>"
128
- - !ruby/object:Gem::Version
129
- version: '3'
130
- - !ruby/object:Gem::Dependency
131
- name: cuprite
132
- requirement: !ruby/object:Gem::Requirement
133
- requirements:
134
- - - ">="
135
- - !ruby/object:Gem::Version
136
- version: '0'
137
- type: :development
138
- prerelease: false
139
- version_requirements: !ruby/object:Gem::Requirement
140
- requirements:
141
- - - ">="
142
- - !ruby/object:Gem::Version
143
- version: '0'
144
- - !ruby/object:Gem::Dependency
145
- name: erb_lint
146
- requirement: !ruby/object:Gem::Requirement
147
- requirements:
148
- - - ">="
149
- - !ruby/object:Gem::Version
150
- version: '0'
151
- type: :development
152
- prerelease: false
153
- version_requirements: !ruby/object:Gem::Requirement
154
- requirements:
155
- - - ">="
156
- - !ruby/object:Gem::Version
157
- version: '0'
158
- - !ruby/object:Gem::Dependency
159
- name: haml
160
- requirement: !ruby/object:Gem::Requirement
161
- requirements:
162
- - - "~>"
163
- - !ruby/object:Gem::Version
164
- version: '6'
165
- type: :development
166
- prerelease: false
167
- version_requirements: !ruby/object:Gem::Requirement
168
- requirements:
169
- - - "~>"
170
- - !ruby/object:Gem::Version
171
- version: '6'
172
- - !ruby/object:Gem::Dependency
173
- name: jbuilder
174
- requirement: !ruby/object:Gem::Requirement
175
- requirements:
176
- - - "~>"
177
- - !ruby/object:Gem::Version
178
- version: '2'
179
- type: :development
180
- prerelease: false
181
- version_requirements: !ruby/object:Gem::Requirement
182
- requirements:
183
- - - "~>"
184
- - !ruby/object:Gem::Version
185
- version: '2'
186
- - !ruby/object:Gem::Dependency
187
- name: m
188
- requirement: !ruby/object:Gem::Requirement
189
- requirements:
190
- - - "~>"
191
- - !ruby/object:Gem::Version
192
- version: '1'
193
- type: :development
194
- prerelease: false
195
- version_requirements: !ruby/object:Gem::Requirement
196
- requirements:
197
- - - "~>"
198
- - !ruby/object:Gem::Version
199
- version: '1'
200
- - !ruby/object:Gem::Dependency
201
- name: method_source
202
- requirement: !ruby/object:Gem::Requirement
203
- requirements:
204
- - - "~>"
205
- - !ruby/object:Gem::Version
206
- version: '1'
207
- type: :development
208
- prerelease: false
209
- version_requirements: !ruby/object:Gem::Requirement
210
- requirements:
211
- - - "~>"
212
- - !ruby/object:Gem::Version
213
- version: '1'
214
- - !ruby/object:Gem::Dependency
215
- name: minitest
216
- requirement: !ruby/object:Gem::Requirement
217
- requirements:
218
- - - "~>"
219
- - !ruby/object:Gem::Version
220
- version: '5'
221
- type: :development
222
- prerelease: false
223
- version_requirements: !ruby/object:Gem::Requirement
224
- requirements:
225
- - - "~>"
226
- - !ruby/object:Gem::Version
227
- version: '5'
228
- - !ruby/object:Gem::Dependency
229
- name: puma
230
- requirement: !ruby/object:Gem::Requirement
231
- requirements:
232
- - - "~>"
233
- - !ruby/object:Gem::Version
234
- version: '6'
235
- type: :development
236
- prerelease: false
237
- version_requirements: !ruby/object:Gem::Requirement
238
- requirements:
239
- - - "~>"
240
- - !ruby/object:Gem::Version
241
- version: '6'
242
- - !ruby/object:Gem::Dependency
243
- name: rake
244
- requirement: !ruby/object:Gem::Requirement
245
- requirements:
246
- - - "~>"
247
- - !ruby/object:Gem::Version
248
- version: '13'
249
- type: :development
250
- prerelease: false
251
- version_requirements: !ruby/object:Gem::Requirement
252
- requirements:
253
- - - "~>"
254
- - !ruby/object:Gem::Version
255
- version: '13'
256
- - !ruby/object:Gem::Dependency
257
- name: redis
258
- requirement: !ruby/object:Gem::Requirement
259
- requirements:
260
- - - ">="
261
- - !ruby/object:Gem::Version
262
- version: '0'
263
- type: :development
264
- prerelease: false
265
- version_requirements: !ruby/object:Gem::Requirement
266
- requirements:
267
- - - ">="
268
- - !ruby/object:Gem::Version
269
- version: '0'
270
- - !ruby/object:Gem::Dependency
271
- name: rspec-rails
272
- requirement: !ruby/object:Gem::Requirement
273
- requirements:
274
- - - "~>"
275
- - !ruby/object:Gem::Version
276
- version: '7'
277
- type: :development
278
- prerelease: false
279
- version_requirements: !ruby/object:Gem::Requirement
280
- requirements:
281
- - - "~>"
282
- - !ruby/object:Gem::Version
283
- version: '7'
284
- - !ruby/object:Gem::Dependency
285
- name: rubocop-md
286
- requirement: !ruby/object:Gem::Requirement
287
- requirements:
288
- - - "~>"
289
- - !ruby/object:Gem::Version
290
- version: '2'
291
- type: :development
292
- prerelease: false
293
- version_requirements: !ruby/object:Gem::Requirement
294
- requirements:
295
- - - "~>"
296
- - !ruby/object:Gem::Version
297
- version: '2'
298
- - !ruby/object:Gem::Dependency
299
- name: selenium-webdriver
300
- requirement: !ruby/object:Gem::Requirement
301
- requirements:
302
- - - "~>"
303
- - !ruby/object:Gem::Version
304
- version: '4'
305
- type: :development
306
- prerelease: false
307
- version_requirements: !ruby/object:Gem::Requirement
308
- requirements:
309
- - - "~>"
310
- - !ruby/object:Gem::Version
311
- version: '4'
312
- - !ruby/object:Gem::Dependency
313
- name: simplecov-console
314
- requirement: !ruby/object:Gem::Requirement
315
- requirements:
316
- - - "<"
317
- - !ruby/object:Gem::Version
318
- version: '1'
319
- type: :development
320
- prerelease: false
321
- version_requirements: !ruby/object:Gem::Requirement
322
- requirements:
323
- - - "<"
324
- - !ruby/object:Gem::Version
325
- version: '1'
326
- - !ruby/object:Gem::Dependency
327
- name: simplecov
328
- requirement: !ruby/object:Gem::Requirement
329
- requirements:
330
- - - "<"
331
- - !ruby/object:Gem::Version
332
- version: '1'
333
- type: :development
334
- prerelease: false
335
- version_requirements: !ruby/object:Gem::Requirement
336
- requirements:
337
- - - "<"
338
- - !ruby/object:Gem::Version
339
- version: '1'
340
- - !ruby/object:Gem::Dependency
341
- name: slim
342
- requirement: !ruby/object:Gem::Requirement
343
- requirements:
344
- - - "~>"
345
- - !ruby/object:Gem::Version
346
- version: '5'
347
- type: :development
348
- prerelease: false
349
- version_requirements: !ruby/object:Gem::Requirement
350
- requirements:
351
- - - "~>"
352
- - !ruby/object:Gem::Version
353
- version: '5'
354
- - !ruby/object:Gem::Dependency
355
- name: sprockets-rails
356
- requirement: !ruby/object:Gem::Requirement
357
- requirements:
358
- - - "~>"
359
- - !ruby/object:Gem::Version
360
- version: '3'
361
- type: :development
362
- prerelease: false
363
- version_requirements: !ruby/object:Gem::Requirement
364
- requirements:
365
- - - "~>"
366
- - !ruby/object:Gem::Version
367
- version: '3'
368
- - !ruby/object:Gem::Dependency
369
- name: standard
370
- requirement: !ruby/object:Gem::Requirement
371
- requirements:
372
- - - "~>"
373
- - !ruby/object:Gem::Version
374
- version: '1'
375
- type: :development
376
- prerelease: false
377
- version_requirements: !ruby/object:Gem::Requirement
378
- requirements:
379
- - - "~>"
380
- - !ruby/object:Gem::Version
381
- version: '1'
382
- - !ruby/object:Gem::Dependency
383
- name: turbo-rails
384
- requirement: !ruby/object:Gem::Requirement
385
- requirements:
386
- - - "~>"
387
- - !ruby/object:Gem::Version
388
- version: '2'
389
- type: :development
390
- prerelease: false
391
- version_requirements: !ruby/object:Gem::Requirement
392
- requirements:
393
- - - "~>"
394
- - !ruby/object:Gem::Version
395
- version: '2'
396
- - !ruby/object:Gem::Dependency
397
- name: warning
398
- requirement: !ruby/object:Gem::Requirement
399
- requirements:
400
- - - ">="
401
- - !ruby/object:Gem::Version
402
- version: '0'
403
- type: :development
404
- prerelease: false
405
- version_requirements: !ruby/object:Gem::Requirement
406
- requirements:
407
- - - ">="
408
- - !ruby/object:Gem::Version
409
- version: '0'
410
- - !ruby/object:Gem::Dependency
411
- name: yard-activesupport-concern
412
- requirement: !ruby/object:Gem::Requirement
413
- requirements:
414
- - - "<"
415
- - !ruby/object:Gem::Version
416
- version: '1'
417
- type: :development
418
- prerelease: false
419
- version_requirements: !ruby/object:Gem::Requirement
420
- requirements:
421
- - - "<"
422
- - !ruby/object:Gem::Version
423
- version: '1'
424
- - !ruby/object:Gem::Dependency
425
- name: yard
426
- requirement: !ruby/object:Gem::Requirement
427
- requirements:
428
- - - "<"
429
- - !ruby/object:Gem::Version
430
- version: '1'
431
- type: :development
432
- prerelease: false
433
- version_requirements: !ruby/object:Gem::Requirement
434
- requirements:
435
- - - "<"
436
- - !ruby/object:Gem::Version
437
- version: '1'
438
- - !ruby/object:Gem::Dependency
439
- name: propshaft
440
- requirement: !ruby/object:Gem::Requirement
441
- requirements:
442
- - - "~>"
443
- - !ruby/object:Gem::Version
444
- version: '1'
445
- type: :development
446
- prerelease: false
447
- version_requirements: !ruby/object:Gem::Requirement
448
- requirements:
449
- - - "~>"
450
- - !ruby/object:Gem::Version
451
- version: '1'
452
46
  executables: []
453
47
  extensions: []
454
48
  extra_rdoc_files: []
@@ -469,7 +63,6 @@ files:
469
63
  - lib/view_component/collection.rb
470
64
  - lib/view_component/compile_cache.rb
471
65
  - lib/view_component/compiler.rb
472
- - lib/view_component/component_local_config.rb
473
66
  - lib/view_component/config.rb
474
67
  - lib/view_component/configurable.rb
475
68
  - lib/view_component/deprecation.rb
@@ -481,7 +74,6 @@ files:
481
74
  - lib/view_component/request_details.rb
482
75
  - lib/view_component/slot.rb
483
76
  - lib/view_component/slotable.rb
484
- - lib/view_component/slotable_default.rb
485
77
  - lib/view_component/system_test_case.rb
486
78
  - lib/view_component/system_test_helpers.rb
487
79
  - lib/view_component/template.rb
@@ -1,60 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ViewComponent
4
- module ComponentLocalConfig
5
- class Configuration
6
- def self.defaults
7
- ActiveSupport::Configurable::Configuration[
8
- strip_trailing_whitespace: false
9
- ]
10
- end
11
-
12
- def initialize(config = defaults)
13
- @config = config
14
- end
15
-
16
- delegate_missing_to :@config
17
-
18
- def inheritable_copy
19
- self.class.new(@config.inheritable_copy)
20
- end
21
-
22
- private
23
-
24
- delegate :defaults, to: :class
25
- end
26
-
27
- extend ActiveSupport::Concern
28
-
29
- included do
30
- # :nocov:
31
- def view_component_config
32
- @__vc_config ||= self.class.view_component_config.inheritable_copy
33
- end
34
-
35
- private
36
-
37
- def inherited(child)
38
- child.instance_variable_set(:@__vc_config, nil)
39
- super
40
- end
41
- # :nocov:
42
- end
43
-
44
- class_methods do
45
- def view_component_config
46
- @__vc_config ||= if respond_to?(:superclass) && superclass.respond_to?(:view_component_config)
47
- superclass.view_component_config.inheritable_copy
48
- else
49
- # create a new "anonymous" class that will host the compiled reader methods
50
- ViewComponent::ComponentLocalConfig::Configuration.new
51
- end
52
- end
53
-
54
- def configure_view_component(&block)
55
- view_component_config.instance_eval(&block)
56
- view_component_config.compile_methods!
57
- end
58
- end
59
- end
60
- end
@@ -1,18 +0,0 @@
1
- module ViewComponent
2
- module SlotableDefault
3
- def get_slot(slot_name)
4
- return super unless !@__vc_set_slots[slot_name] && (default_method = registered_slots[slot_name][:default_method])
5
-
6
- renderable_value = send(default_method)
7
- slot = Slot.new(self)
8
-
9
- if renderable_value.respond_to?(:render_in)
10
- slot.__vc_component_instance = renderable_value
11
- else
12
- slot.__vc_content = renderable_value
13
- end
14
-
15
- slot
16
- end
17
- end
18
- end