view_component 2.63.0 → 2.66.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of view_component might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 78c8a36fb19eb4c8a1c3737815113323acc9cd5a9c89a270163ca8336e44c41e
4
- data.tar.gz: 6e4135faf54dfc7c519d7ef62080d6e779d04bbbc1efea32b7871fe65bd2c685
3
+ metadata.gz: 426a776552c0355ba0fb4ccc066c05a218b9998acb39489485375624b347c170
4
+ data.tar.gz: d17d69cb6e1c08fe31545eb5cf878579dd4aaf11e803790130c83ec7b4c178d2
5
5
  SHA512:
6
- metadata.gz: 0f207e3d9e6980880876afa36521a3f63abafead81da8d4f5574c9e9164139fbd8029bf8b9eae3f034e2da849541b959025612b21dca548477a38327fe5aa97b
7
- data.tar.gz: d65662034f4abf33feeae814b60dcfb82683302be1cf427a5c6f93297a575185963ca8d3a1df81fa9daf7855d25297c303c68e5abc225594be842e12fd657702
6
+ metadata.gz: 4b001cf58f8586b3e83cf8129c23d0bb5a499182189db4a99dfd747d6767f529a631eb2bf439a32f8a91dbb5861eb16ef637b514bffccacd476d577e2c5c27aa
7
+ data.tar.gz: 24d147cfbb226ff11114631721507bfed5a73fe25e1963186fe106a9c0f485596b65349457685967e7df0edea4d78841646f36683764244673e4281ff56741fd
@@ -10,9 +10,7 @@ module ViewComponent
10
10
  around_action :set_locale, only: :previews
11
11
  before_action :require_local!, unless: :show_previews?
12
12
 
13
- if respond_to?(:content_security_policy)
14
- content_security_policy(false)
15
- end
13
+ content_security_policy(false) if respond_to?(:content_security_policy)
16
14
  end
17
15
 
18
16
  def index
@@ -45,18 +43,18 @@ module ViewComponent
45
43
 
46
44
  # :doc:
47
45
  def default_preview_layout
48
- ViewComponent::Base.default_preview_layout
46
+ Rails.application.config.view_component.default_preview_layout
49
47
  end
50
48
 
51
49
  # :doc:
52
50
  def show_previews?
53
- ViewComponent::Base.show_previews
51
+ Rails.application.config.view_component.show_previews
54
52
  end
55
53
 
56
54
  # :doc:
57
55
  def find_preview
58
56
  candidates = []
59
- params[:path].to_s.scan(%r{/|$}) { candidates << $` }
57
+ params[:path].to_s.scan(%r{/|$}) { candidates << Regexp.last_match.pre_match }
60
58
  preview = candidates.detect { |candidate| ViewComponent::Preview.exists?(candidate) }
61
59
 
62
60
  if preview
@@ -66,10 +64,8 @@ module ViewComponent
66
64
  end
67
65
  end
68
66
 
69
- def set_locale
70
- I18n.with_locale(params[:locale] || I18n.default_locale) do
71
- yield
72
- end
67
+ def set_locale(&block)
68
+ I18n.with_locale(params[:locale] || I18n.default_locale, &block)
73
69
  end
74
70
 
75
71
  # Returns either {} or {layout: value} depending on configuration
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PreviewHelper
4
- AVAILABLE_PRISM_LANGUAGES = ["ruby", "erb", "haml"]
4
+ AVAILABLE_PRISM_LANGUAGES = %w[ruby erb haml]
5
5
  FALLBACK_LANGUAGE = "ruby"
6
6
 
7
7
  def preview_source
@@ -22,7 +22,7 @@ module PreviewHelper
22
22
  # Fetch template source via finding it through preview paths
23
23
  # to accomodate source view when exclusively using templates
24
24
  # for previews for Rails < 6.1.
25
- all_template_paths = ViewComponent::Base.preview_paths.map do |preview_path|
25
+ all_template_paths = Rails.application.config.view_component.preview_paths.map do |preview_path|
26
26
  Dir.glob("#{preview_path}/**/*")
27
27
  end.flatten
28
28
 
@@ -1,5 +1,5 @@
1
1
  <% if @render_args[:component] %>
2
- <% if ViewComponent::Base.render_monkey_patch_enabled || Rails.version.to_f >= 6.1 %>
2
+ <% if Rails.application.config.view_component.render_monkey_patch_enabled || Rails.version.to_f >= 6.1 %>
3
3
  <%= render(@render_args[:component], @render_args[:args], &@render_args[:block]) %>
4
4
  <% else %>
5
5
  <%= render_component(@render_args[:component], &@render_args[:block]) %>
@@ -8,6 +8,6 @@
8
8
  <%= render template: @render_args[:template], locals: @render_args[:locals] || {} %>
9
9
  <% end %>
10
10
 
11
- <% if ViewComponent::Base.show_previews_source %>
11
+ <% if Rails.application.config.view_component.show_previews_source %>
12
12
  <%= preview_source %>
13
13
  <% end %>
data/docs/CHANGELOG.md CHANGED
@@ -9,6 +9,51 @@ title: Changelog
9
9
 
10
10
  ## main
11
11
 
12
+ ## 2.66.0
13
+
14
+ * Add missing `generate.sidecar`, `generate.stimulus_controller`, `generate.locale`, `generate.distinct_locale_files`, `generate.preview` config options to `config.view_component`.
15
+
16
+ *Simon Fish*
17
+
18
+ ## 2.65.0
19
+
20
+ * Raise `ArgumentError` when conflicting Slots are defined.
21
+
22
+ Before this change it was possible to define Slots with conflicting names, for example:
23
+
24
+ ```ruby
25
+ class MyComponent < ViewComponent::Base
26
+ renders_one :item
27
+ renders_many :items
28
+ end
29
+ ```
30
+
31
+ *Joel Hawksley*
32
+
33
+ ## 2.64.0
34
+
35
+ * Add `warn_on_deprecated_slot_setter` flag to opt-in to deprecation warning.
36
+
37
+ In [v2.54.0](https://viewcomponent.org/CHANGELOG.html#2540), the Slots API was updated to require the `with_*` prefix for setting Slots. The non-`with_*` setters will be deprecated in a coming version and removed in `v3.0`.
38
+
39
+ To enable the coming deprecation warning, add `warn_on_deprecated_slot_setter`:
40
+
41
+ ```ruby
42
+ class DeprecatedSlotsSetterComponent < ViewComponent::Base
43
+ warn_on_deprecated_slot_setter
44
+ end
45
+ ```
46
+
47
+ *Joel Hawksley*
48
+
49
+ * Add [`m`](https://rubygems.org/gems/m) to development environment.
50
+
51
+ *Joel Hawksley*
52
+
53
+ * Fix potential deadlock scenario in the compiler's development mode.
54
+
55
+ *Blake Williams*
56
+
12
57
  ## 2.63.0
13
58
 
14
59
  * Fixed typo in `renders_many` documentation.
@@ -3,9 +3,7 @@
3
3
  module ViewComponent
4
4
  module AbstractGenerator
5
5
  def copy_view_file
6
- unless options["inline"]
7
- template "component.html.#{engine_name}", destination
8
- end
6
+ template "component.html.#{engine_name}", destination unless options["inline"]
9
7
  end
10
8
 
11
9
  private
@@ -31,7 +29,7 @@ module ViewComponent
31
29
  end
32
30
 
33
31
  def component_path
34
- ViewComponent::Base.view_component_path
32
+ Rails.application.config.view_component.view_component_path
35
33
  end
36
34
 
37
35
  def stimulus_controller
@@ -44,7 +42,7 @@ module ViewComponent
44
42
  end
45
43
 
46
44
  def sidecar?
47
- options["sidecar"] || ViewComponent::Base.generate.sidecar
45
+ options["sidecar"] || Rails.application.config.view_component.generate.sidecar
48
46
  end
49
47
  end
50
48
  end
@@ -13,11 +13,12 @@ module Rails
13
13
  check_class_collision suffix: "Component"
14
14
 
15
15
  class_option :inline, type: :boolean, default: false
16
- class_option :locale, type: :boolean, default: ViewComponent::Base.generate.locale
16
+ class_option :locale, type: :boolean, default: Rails.application.config.view_component.generate.locale
17
17
  class_option :parent, type: :string, desc: "The parent class for the generated component"
18
- class_option :preview, type: :boolean, default: ViewComponent::Base.generate.preview
18
+ class_option :preview, type: :boolean, default: Rails.application.config.view_component.generate.preview
19
19
  class_option :sidecar, type: :boolean, default: false
20
- class_option :stimulus, type: :boolean, default: ViewComponent::Base.generate.stimulus_controller
20
+ class_option :stimulus, type: :boolean,
21
+ default: Rails.application.config.view_component.generate.stimulus_controller
21
22
 
22
23
  def create_component_file
23
24
  template "component.rb", File.join(component_path, class_path, "#{file_name}_component.rb")
@@ -40,7 +41,7 @@ module Rails
40
41
  def parent_class
41
42
  return options[:parent] if options[:parent]
42
43
 
43
- ViewComponent::Base.component_parent_class || default_parent_class
44
+ Rails.application.config.view_component.component_parent_class || default_parent_class
44
45
  end
45
46
 
46
47
  def initialize_signature
@@ -12,7 +12,7 @@ module Locale
12
12
  class_option :sidecar, type: :boolean, default: false
13
13
 
14
14
  def create_locale_file
15
- if ViewComponent::Base.generate.distinct_locale_files
15
+ if Rails.application.config.view_component.generate.distinct_locale_files
16
16
  I18n.available_locales.each do |locale|
17
17
  create_file destination(locale), translations_hash([locale]).to_yaml
18
18
  end
@@ -4,9 +4,9 @@ require "action_view"
4
4
  require "active_support/configurable"
5
5
  require "view_component/collection"
6
6
  require "view_component/compile_cache"
7
+ require "view_component/config"
7
8
  require "view_component/content_areas"
8
9
  require "view_component/polymorphic_slots"
9
- require "view_component/previewable"
10
10
  require "view_component/slotable"
11
11
  require "view_component/slotable_v2"
12
12
  require "view_component/translatable"
@@ -14,10 +14,16 @@ require "view_component/with_content_helper"
14
14
 
15
15
  module ViewComponent
16
16
  class Base < ActionView::Base
17
- include ActiveSupport::Configurable
17
+ class << self
18
+ delegate(*ViewComponent::Config.defaults.keys, to: :config)
19
+
20
+ def config
21
+ Rails.application.config.view_component
22
+ end
23
+ end
24
+
18
25
  include ViewComponent::ContentAreas
19
26
  include ViewComponent::PolymorphicSlots
20
- include ViewComponent::Previewable
21
27
  include ViewComponent::SlotableV2
22
28
  include ViewComponent::Translatable
23
29
  include ViewComponent::WithContentHelper
@@ -113,11 +119,9 @@ module ViewComponent
113
119
  @current_template = self
114
120
 
115
121
  if block && defined?(@__vc_content_set_by_with_content)
116
- raise ArgumentError.new(
117
- "It looks like a block was provided after calling `with_content` on #{self.class.name}, " \
122
+ raise ArgumentError, "It looks like a block was provided after calling `with_content` on #{self.class.name}, " \
118
123
  "which means that ViewComponent doesn't know which content to use.\n\n" \
119
124
  "To fix this issue, use either `with_content` or a block."
120
- )
121
125
  end
122
126
 
123
127
  @__vc_content_evaluated = false
@@ -262,9 +266,7 @@ module ViewComponent
262
266
  # @private
263
267
  def format
264
268
  # Ruby 2.6 throws a warning without checking `defined?`, 2.7 doesn't
265
- if defined?(@__vc_variant)
266
- @__vc_variant
267
- end
269
+ @__vc_variant if defined?(@__vc_variant)
268
270
  end
269
271
 
270
272
  # Use the provided variant instead of the one determined by the current request.
@@ -313,11 +315,9 @@ module ViewComponent
313
315
  # config.view_component.test_controller = "MyTestController"
314
316
  # ```
315
317
  #
316
- # Defaults to ApplicationController. Can also be configured on a per-test
317
- # basis using `with_controller_class`.
318
+ # Defaults to `nil`. If this is falsy, `"ApplicationController"` is used. Can also be
319
+ # configured on a per-test basis using `with_controller_class`.
318
320
  #
319
- mattr_accessor :test_controller
320
- @@test_controller = "ApplicationController"
321
321
 
322
322
  # Set if render monkey patches should be included or not in Rails <6.1:
323
323
  #
@@ -325,7 +325,6 @@ module ViewComponent
325
325
  # config.view_component.render_monkey_patch_enabled = false
326
326
  # ```
327
327
  #
328
- mattr_accessor :render_monkey_patch_enabled, instance_writer: false, default: true
329
328
 
330
329
  # Path for component files
331
330
  #
@@ -333,9 +332,8 @@ module ViewComponent
333
332
  # config.view_component.view_component_path = "app/my_components"
334
333
  # ```
335
334
  #
336
- # Defaults to `app/components`.
335
+ # Defaults to `nil`. If this is falsy, `app/components` is used.
337
336
  #
338
- mattr_accessor :view_component_path, instance_writer: false, default: "app/components"
339
337
 
340
338
  # Parent class for generated components
341
339
  #
@@ -346,7 +344,6 @@ module ViewComponent
346
344
  # Defaults to nil. If this is falsy, generators will use
347
345
  # "ApplicationComponent" if defined, "ViewComponent::Base" otherwise.
348
346
  #
349
- mattr_accessor :component_parent_class, instance_writer: false
350
347
 
351
348
  # Configuration for generators.
352
349
  #
@@ -397,7 +394,6 @@ module ViewComponent
397
394
  # ```
398
395
  #
399
396
  # Defaults to `false`.
400
- mattr_accessor :generate, instance_writer: false, default: ActiveSupport::OrderedOptions.new(false)
401
397
 
402
398
  class << self
403
399
  # @private
@@ -489,8 +485,8 @@ module ViewComponent
489
485
 
490
486
  # If Rails application is loaded, add application url_helpers to the component context
491
487
  # we need to check this to use this gem as a dependency
492
- if defined?(Rails) && Rails.application
493
- child.include Rails.application.routes.url_helpers unless child < Rails.application.routes.url_helpers
488
+ if defined?(Rails) && Rails.application && !(child < Rails.application.routes.url_helpers)
489
+ child.include Rails.application.routes.url_helpers
494
490
  end
495
491
 
496
492
  # Derive the source location of the component Ruby file from the call stack.
@@ -500,7 +496,7 @@ module ViewComponent
500
496
 
501
497
  # Removes the first part of the path and the extension.
502
498
  child.virtual_path = child.source_location.gsub(
503
- %r{(.*#{Regexp.quote(ViewComponent::Base.view_component_path)})|(\.rb)}, ""
499
+ /(.*#{Regexp.quote(Rails.application.config.view_component.view_component_path)})|(\.rb)/, ""
504
500
  )
505
501
 
506
502
  # Set collection parameter to the extended component
@@ -591,20 +587,16 @@ module ViewComponent
591
587
  # parameters will be empty and ViewComponent will not be able to render
592
588
  # the component.
593
589
  if initialize_parameters.empty?
594
- raise ArgumentError.new(
595
- "The #{self} initializer is empty or invalid." \
590
+ raise ArgumentError, "The #{self} initializer is empty or invalid." \
596
591
  "It must accept the parameter `#{parameter}` to render it as a collection.\n\n" \
597
592
  "To fix this issue, update the initializer to accept `#{parameter}`.\n\n" \
598
593
  "See https://viewcomponent.org/guide/collections.html for more information on rendering collections."
599
- )
600
594
  end
601
595
 
602
- raise ArgumentError.new(
603
- "The initializer for #{self} doesn't accept the parameter `#{parameter}`, " \
596
+ raise ArgumentError, "The initializer for #{self} doesn't accept the parameter `#{parameter}`, " \
604
597
  "which is required in order to render it as a collection.\n\n" \
605
598
  "To fix this issue, update the initializer to accept `#{parameter}`.\n\n" \
606
599
  "See https://viewcomponent.org/guide/collections.html for more information on rendering collections."
607
- )
608
600
  end
609
601
 
610
602
  # Ensure the component initializer doesn't define
@@ -614,10 +606,8 @@ module ViewComponent
614
606
  def validate_initialization_parameters!
615
607
  return unless initialize_parameter_names.include?(RESERVED_PARAMETER)
616
608
 
617
- raise ViewComponent::ComponentError.new(
618
- "#{self} initializer can't accept the parameter `#{RESERVED_PARAMETER}`, as it will override a " \
609
+ raise ViewComponent::ComponentError, "#{self} initializer can't accept the parameter `#{RESERVED_PARAMETER}`, as it will override a " \
619
610
  "public ViewComponent method. To fix this issue, rename the parameter."
620
- )
621
611
  end
622
612
 
623
613
  # @private
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "concurrent-ruby"
4
+
3
5
  module ViewComponent
4
6
  class Compiler
5
7
  # Lock required to be obtained before compiling the component
@@ -16,7 +18,7 @@ module ViewComponent
16
18
 
17
19
  def initialize(component_class)
18
20
  @component_class = component_class
19
- @__vc_compiler_lock = Monitor.new
21
+ @__vc_compiler_lock = Concurrent::ReadWriteLock.new
20
22
  end
21
23
 
22
24
  def compiled?
@@ -33,7 +35,7 @@ module ViewComponent
33
35
 
34
36
  component_class.superclass.compile(raise_errors: raise_errors) if should_compile_superclass?
35
37
 
36
- with_lock do
38
+ with_write_lock do
37
39
  CompileCache.invalidate_class!(component_class)
38
40
 
39
41
  subclass_instance_methods = component_class.instance_methods(false)
@@ -90,14 +92,18 @@ module ViewComponent
90
92
  end
91
93
  end
92
94
 
93
- def with_lock(&block)
95
+ def with_write_lock(&block)
94
96
  if development?
95
- __vc_compiler_lock.synchronize(&block)
97
+ __vc_compiler_lock.with_write_lock(&block)
96
98
  else
97
99
  block.call
98
100
  end
99
101
  end
100
102
 
103
+ def with_read_lock(&block)
104
+ __vc_compiler_lock.with_read_lock(&block)
105
+ end
106
+
101
107
  private
102
108
 
103
109
  attr_reader :component_class
@@ -123,7 +129,7 @@ module ViewComponent
123
129
  if development?
124
130
  component_class.class_eval <<-RUBY, __FILE__, __LINE__ + 1
125
131
  def render_template_for(variant = nil)
126
- self.class.compiler.with_lock do
132
+ self.class.compiler.with_read_lock do
127
133
  #{body}
128
134
  end
129
135
  end
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "view_component/deprecation"
4
+
5
+ module ViewComponent
6
+ class Config
7
+ class << self
8
+ # `new` without any arguments initializes the default configuration, but
9
+ # it's important to differentiate in case that's no longer the case in
10
+ # future.
11
+ alias_method :default, :new
12
+
13
+ def defaults
14
+ ActiveSupport::OrderedOptions.new.merge!({
15
+ generate: ActiveSupport::OrderedOptions.new(false),
16
+ preview_controller: "ViewComponentsController",
17
+ preview_route: "/rails/view_components",
18
+ show_previews_source: false,
19
+ instrumentation_enabled: false,
20
+ render_monkey_patch_enabled: true,
21
+ view_component_path: "app/components",
22
+ component_parent_class: nil,
23
+ show_previews: Rails.env.development? || Rails.env.test?,
24
+ preview_paths: default_preview_paths,
25
+ test_controller: "ApplicationController",
26
+ default_preview_layout: nil
27
+ })
28
+ end
29
+
30
+ # @!attribute generate
31
+ # @return [ActiveSupport::OrderedOptions]
32
+ # The subset of configuration options relating to generators.
33
+ #
34
+ # All options under this namespace default to `false` unless otherwise
35
+ # stated.
36
+ #
37
+ # #### #sidecar
38
+ #
39
+ # Always generate a component with a sidecar directory:
40
+ #
41
+ # config.view_component.generate.sidecar = true
42
+ #
43
+ # #### #stimulus_controller
44
+ #
45
+ # Always generate a Stimulus controller alongside the component:
46
+ #
47
+ # config.view_component.generate.stimulus_controller = true
48
+ #
49
+ # #### #locale
50
+ #
51
+ # Always generate translations file alongside the component:
52
+ #
53
+ # config.view_component.generate.locale = true
54
+ #
55
+ # #### #distinct_locale_files
56
+ #
57
+ # Always generate as many translations files as available locales:
58
+ #
59
+ # config.view_component.generate.distinct_locale_files = true
60
+ #
61
+ # One file will be generated for each configured `I18n.available_locales`,
62
+ # falling back to `[:en]` when no `available_locales` is defined.
63
+ #
64
+ # #### #preview
65
+ #
66
+ # Always generate a preview alongside the component:
67
+ #
68
+ # config.view_component.generate.preview = true
69
+
70
+ # @!attribute preview_controller
71
+ # @return [String]
72
+ # The controller used for previewing components.
73
+ # Defaults to `ViewComponentsController`.
74
+
75
+ # @!attribute preview_route
76
+ # @return [String]
77
+ # The entry route for component previews.
78
+ # Defaults to `"/rails/view_components"`.
79
+
80
+ # @!attribute show_previews_source
81
+ # @return [Boolean]
82
+ # Whether to display source code previews in component previews.
83
+ # Defaults to `false`.
84
+
85
+ # @!attribute instrumentation_enabled
86
+ # @return [Boolean]
87
+ # Whether ActiveSupport notifications are enabled.
88
+ # Defaults to `false`.
89
+
90
+ # @!attribute render_monkey_patch_enabled
91
+ # @return [Boolean] Whether the #render method should be monkey patched.
92
+ # If this is disabled, use `#render_component` or
93
+ # `#render_component_to_string` instead.
94
+ # Defaults to `true`.
95
+
96
+ # @!attribute view_component_path
97
+ # @return [String]
98
+ # The path in which components, their templates, and their sidecars should
99
+ # be stored.
100
+ # Defaults to `"app/components"`.
101
+
102
+ # @!attribute component_parent_class
103
+ # @return [String]
104
+ # The parent class from which generated components will inherit.
105
+ # Defaults to `nil`. If this is falsy, generators will use
106
+ # `"ApplicationComponent"` if defined, `"ViewComponent::Base"` otherwise.
107
+
108
+ # @!attribute show_previews
109
+ # @return [Boolean]
110
+ # Whether component previews are enabled.
111
+ # Defaults to `true` in development and test environments.
112
+
113
+ # @!attribute preview_paths
114
+ # @return [Array<String>]
115
+ # The locations in which component previews will be looked up.
116
+ # Defaults to `['test/component/previews']` relative to your Rails root.
117
+
118
+ # @!attribute preview_path
119
+ # @deprecated Use #preview_paths instead. Will be removed in v3.0.0.
120
+
121
+ # @!attribute test_controller
122
+ # @return [String]
123
+ # The controller used for testing components.
124
+ # Can also be configured on a per-test basis using `#with_controller_class`.
125
+ # Defaults to `ApplicationController`.
126
+
127
+ # @!attribute default_preview_layout
128
+ # @return [String]
129
+ # A custom default layout used for the previews index page and individual
130
+ # previews.
131
+ # Defaults to `nil`. If this is falsy, `"component_preview"` is used.
132
+
133
+ def default_preview_paths
134
+ return [] unless defined?(Rails.root) && Dir.exist?("#{Rails.root}/test/components/previews")
135
+
136
+ ["#{Rails.root}/test/components/previews"]
137
+ end
138
+ end
139
+
140
+ def initialize
141
+ @config = self.class.defaults
142
+ end
143
+
144
+ def preview_path
145
+ preview_paths
146
+ end
147
+
148
+ def preview_path=(new_value)
149
+ ViewComponent::Deprecation.warn("`preview_path` will be removed in v3.0.0. Use `preview_paths` instead.")
150
+ self.preview_paths = Array.wrap(new_value)
151
+ end
152
+
153
+ delegate_missing_to :config
154
+
155
+ private
156
+
157
+ attr_reader :config
158
+ end
159
+ end
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rails"
4
+ require "view_component/base"
4
5
 
5
6
  module ViewComponent
6
7
  class Engine < Rails::Engine # :nodoc:
7
- config.view_component = ActiveSupport::OrderedOptions.new
8
- config.view_component.preview_paths ||= []
8
+ config.view_component = ViewComponent::Config.default
9
9
 
10
10
  rake_tasks do
11
11
  load "view_component/rails/tasks/view_component.rake"
@@ -14,25 +14,20 @@ module ViewComponent
14
14
  initializer "view_component.set_configs" do |app|
15
15
  options = app.config.view_component
16
16
 
17
+ %i[generate preview_controller preview_route show_previews_source].each do |config_option|
18
+ options[config_option] ||= ViewComponent::Base.public_send(config_option)
19
+ end
20
+ options.instrumentation_enabled = false if options.instrumentation_enabled.nil?
17
21
  options.render_monkey_patch_enabled = true if options.render_monkey_patch_enabled.nil?
18
22
  options.show_previews = Rails.env.development? || Rails.env.test? if options.show_previews.nil?
19
- options.show_previews_source ||= ViewComponent::Base.show_previews_source
20
23
  options.instrumentation_enabled = false if options.instrumentation_enabled.nil?
21
- options.preview_route ||= ViewComponent::Base.preview_route
22
- options.preview_controller ||= ViewComponent::Base.preview_controller
23
24
 
24
25
  if options.show_previews
26
+ # This is still necessary because when `config.view_component` is declared, `Rails.root` is unspecified.
25
27
  options.preview_paths << "#{Rails.root}/test/components/previews" if defined?(Rails.root) && Dir.exist?(
26
28
  "#{Rails.root}/test/components/previews"
27
29
  )
28
30
 
29
- if options.preview_path.present?
30
- ViewComponent::Deprecation.warn(
31
- "`preview_path` will be removed in v3.0.0. Use `preview_paths` instead."
32
- )
33
- options.preview_paths << options.preview_path
34
- end
35
-
36
31
  if options.show_previews_source
37
32
  require "method_source"
38
33
 
@@ -41,10 +36,6 @@ module ViewComponent
41
36
  end
42
37
  end
43
38
  end
44
-
45
- ActiveSupport.on_load(:view_component) do
46
- options.each { |k, v| send("#{k}=", v) if respond_to?("#{k}=") }
47
- end
48
39
  end
49
40
 
50
41
  initializer "view_component.enable_instrumentation" do |app|
@@ -72,12 +63,6 @@ module ViewComponent
72
63
  end
73
64
  end
74
65
 
75
- initializer "view_component.compile_config_methods" do
76
- ActiveSupport.on_load(:view_component) do
77
- config.compile_methods! if config.respond_to?(:compile_methods!)
78
- end
79
- end
80
-
81
66
  initializer "view_component.monkey_patch_render" do |app|
82
67
  next if Rails.version.to_f >= 6.1 || !app.config.view_component.render_monkey_patch_enabled
83
68
 
@@ -94,7 +79,7 @@ module ViewComponent
94
79
  end
95
80
  end
96
81
 
97
- initializer "view_component.include_render_component" do |app|
82
+ initializer "view_component.include_render_component" do |_app|
98
83
  next if Rails.version.to_f >= 6.1
99
84
 
100
85
  ActiveSupport.on_load(:action_view) do
@@ -116,7 +101,7 @@ module ViewComponent
116
101
  end
117
102
  end
118
103
 
119
- initializer "compiler mode" do |app|
104
+ initializer "compiler mode" do |_app|
120
105
  ViewComponent::Compiler.mode = if Rails.env.development? || Rails.env.test?
121
106
  ViewComponent::Compiler::DEVELOPMENT_MODE
122
107
  else
@@ -160,7 +145,7 @@ unless defined?(ViewComponent::Base)
160
145
 
161
146
  ViewComponent::Deprecation.warn(
162
147
  "This manually engine loading is deprecated and will be removed in v3.0.0. " \
163
- "Remove `require \"view_component/engine\"`."
148
+ 'Remove `require "view_component/engine"`.'
164
149
  )
165
150
 
166
151
  require "view_component"
@@ -61,10 +61,12 @@ module ViewComponent
61
61
  end
62
62
 
63
63
  define_method(setter_name) do |*args, &block|
64
- ViewComponent::Deprecation.warn(
65
- "polymorphic slot setters like `#{setter_name}` are deprecated and will be removed in " \
66
- "ViewComponent v3.0.0.\n\nUse `with_#{setter_name}` instead."
67
- )
64
+ if _warn_on_deprecated_slot_setter
65
+ ViewComponent::Deprecation.warn(
66
+ "polymorphic slot setters like `#{setter_name}` are deprecated and will be removed in " \
67
+ "ViewComponent v3.0.0.\n\nUse `with_#{setter_name}` instead."
68
+ )
69
+ end
68
70
 
69
71
  set_polymorphic_slot(slot_name, poly_type, *args, &block)
70
72
  end
@@ -22,9 +22,7 @@ module ViewComponent
22
22
  def render_preview(name)
23
23
  begin
24
24
  preview_klass = if respond_to?(:described_class)
25
- if described_class.nil?
26
- raise "`render_preview` expected a described_class, but it is nil."
27
- end
25
+ raise "`render_preview` expected a described_class, but it is nil." if described_class.nil?
28
26
 
29
27
  "#{described_class}Preview"
30
28
  else
@@ -32,12 +30,10 @@ module ViewComponent
32
30
  end
33
31
  preview_klass = preview_klass.constantize
34
32
  rescue NameError
35
- raise NameError.new(
36
- "`render_preview` expected to find #{preview_klass}, but it does not exist."
37
- )
33
+ raise NameError, "`render_preview` expected to find #{preview_klass}, but it does not exist."
38
34
  end
39
35
 
40
- previews_controller = build_controller(ViewComponent::Base.preview_controller.constantize)
36
+ previews_controller = build_controller(Rails.application.config.view_component.preview_controller.constantize)
41
37
  previews_controller.request.params[:path] = "#{preview_klass.preview_name}/#{name}"
42
38
  previews_controller.response = ActionDispatch::Response.new
43
39
  result = previews_controller.previews
@@ -17,9 +17,19 @@ module ViewComponent
17
17
  # Hash of registered Slots
18
18
  class_attribute :registered_slots
19
19
  self.registered_slots = {}
20
+
21
+ class_attribute :_warn_on_deprecated_slot_setter
22
+ self._warn_on_deprecated_slot_setter = false
20
23
  end
21
24
 
22
25
  class_methods do
26
+ ##
27
+ # Enables deprecations coming to the Slots API in ViewComponent v3
28
+ #
29
+ def warn_on_deprecated_slot_setter
30
+ self._warn_on_deprecated_slot_setter = true
31
+ end
32
+
23
33
  ##
24
34
  # Registers a sub-component
25
35
  #
@@ -70,6 +80,7 @@ module ViewComponent
70
80
  # <% end %>
71
81
  def renders_one(slot_name, callable = nil)
72
82
  validate_singular_slot_name(slot_name)
83
+ validate_plural_slot_name(ActiveSupport::Inflector.pluralize(slot_name).to_sym)
73
84
 
74
85
  define_method :"with_#{slot_name}" do |*args, &block|
75
86
  set_slot(slot_name, nil, *args, &block)
@@ -80,7 +91,13 @@ module ViewComponent
80
91
  if args.empty? && block.nil?
81
92
  get_slot(slot_name)
82
93
  else
83
- # Deprecated: Will remove in 3.0
94
+ if _warn_on_deprecated_slot_setter
95
+ ViewComponent::Deprecation.warn(
96
+ "Setting a slot with `##{slot_name}` is deprecated and will be removed in ViewComponent v3.0.0. " \
97
+ "Use `#with_#{slot_name}` to set the slot instead."
98
+ )
99
+ end
100
+
84
101
  set_slot(slot_name, nil, *args, &block)
85
102
  end
86
103
  end
@@ -131,16 +148,22 @@ module ViewComponent
131
148
  # <% end %>
132
149
  # <% end %>
133
150
  def renders_many(slot_name, callable = nil)
134
- validate_plural_slot_name(slot_name)
135
-
136
151
  singular_name = ActiveSupport::Inflector.singularize(slot_name)
152
+ validate_plural_slot_name(slot_name)
153
+ validate_singular_slot_name(ActiveSupport::Inflector.singularize(slot_name).to_sym)
137
154
 
138
155
  # Define setter for singular names
139
156
  # for example `renders_many :items` allows fetching all tabs with
140
157
  # `component.tabs` and setting a tab with `component.tab`
141
- #
142
- # Deprecated: Will remove in 3.0
158
+
143
159
  define_method singular_name do |*args, &block|
160
+ if _warn_on_deprecated_slot_setter
161
+ ViewComponent::Deprecation.warn(
162
+ "Setting a slot with `##{singular_name}` is deprecated and will be removed in ViewComponent v3.0.0. " \
163
+ "Use `#with_#{singular_name}` to set the slot instead."
164
+ )
165
+ end
166
+
144
167
  set_slot(slot_name, nil, *args, &block)
145
168
  end
146
169
  ruby2_keywords(singular_name.to_sym) if respond_to?(:ruby2_keywords, true)
@@ -162,7 +185,13 @@ module ViewComponent
162
185
  if collection_args.nil? && block.nil?
163
186
  get_slot(slot_name)
164
187
  else
165
- # Deprecated: Will remove in 3.0
188
+ if _warn_on_deprecated_slot_setter
189
+ ViewComponent::Deprecation.warn(
190
+ "Setting a slot with `##{slot_name}` is deprecated and will be removed in ViewComponent v3.0.0. " \
191
+ "Use `#with_#{slot_name}` to set the slot instead."
192
+ )
193
+ end
194
+
166
195
  collection_args.map do |args|
167
196
  set_slot(slot_name, nil, **args, &block)
168
197
  end
@@ -3,7 +3,7 @@
3
3
  module ViewComponent
4
4
  module VERSION
5
5
  MAJOR = 2
6
- MINOR = 63
6
+ MINOR = 66
7
7
  PATCH = 0
8
8
 
9
9
  STRING = [MAJOR, MINOR, PATCH].join(".")
@@ -10,6 +10,7 @@ module ViewComponent
10
10
  autoload :Compiler
11
11
  autoload :CompileCache
12
12
  autoload :ComponentError
13
+ autoload :Config
13
14
  autoload :Deprecation
14
15
  autoload :Instrumentation
15
16
  autoload :Preview
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.63.0
4
+ version: 2.66.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: 2022-08-01 00:00:00.000000000 Z
11
+ date: 2022-08-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -44,6 +44,20 @@ dependencies:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
46
  version: '1.0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: concurrent-ruby
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '1.0'
47
61
  - !ruby/object:Gem::Dependency
48
62
  name: appraisal
49
63
  requirement: !ruby/object:Gem::Requirement
@@ -142,6 +156,20 @@ dependencies:
142
156
  - - "~>"
143
157
  - !ruby/object:Gem::Version
144
158
  version: '2'
159
+ - !ruby/object:Gem::Dependency
160
+ name: m
161
+ requirement: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - "~>"
164
+ - !ruby/object:Gem::Version
165
+ version: '1'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - "~>"
171
+ - !ruby/object:Gem::Version
172
+ version: '1'
145
173
  - !ruby/object:Gem::Dependency
146
174
  name: minitest
147
175
  requirement: !ruby/object:Gem::Requirement
@@ -329,6 +357,7 @@ files:
329
357
  - lib/view_component/compile_cache.rb
330
358
  - lib/view_component/compiler.rb
331
359
  - lib/view_component/component_error.rb
360
+ - lib/view_component/config.rb
332
361
  - lib/view_component/content_areas.rb
333
362
  - lib/view_component/deprecation.rb
334
363
  - lib/view_component/docs_builder_component.html.erb
@@ -338,7 +367,6 @@ files:
338
367
  - lib/view_component/polymorphic_slots.rb
339
368
  - lib/view_component/preview.rb
340
369
  - lib/view_component/preview_template_error.rb
341
- - lib/view_component/previewable.rb
342
370
  - lib/view_component/rails/tasks/view_component.rake
343
371
  - lib/view_component/render_component_helper.rb
344
372
  - lib/view_component/render_component_to_string_helper.rb
@@ -1,62 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "active_support/concern"
4
-
5
- module ViewComponent
6
- module Previewable
7
- extend ActiveSupport::Concern
8
-
9
- included do
10
- # Enable or disable component previews:
11
- #
12
- # config.view_component.show_previews = true
13
- #
14
- # Defaults to `true` in development.
15
- #
16
- mattr_accessor :show_previews, instance_writer: false
17
-
18
- # Enable or disable source code previews in component previews:
19
- #
20
- # config.view_component.show_previews_source = true
21
- #
22
- # Defaults to `false`.
23
- #
24
- mattr_accessor :show_previews_source, instance_writer: false, default: false
25
-
26
- # Set a custom default layout used for preview index and individual previews:
27
- #
28
- # config.view_component.default_preview_layout = "component_preview"
29
- #
30
- mattr_accessor :default_preview_layout, instance_writer: false
31
-
32
- # Set the location of component previews:
33
- #
34
- # config.view_component.preview_paths << "#{Rails.root}/lib/component_previews"
35
- #
36
- mattr_accessor :preview_paths, instance_writer: false
37
-
38
- # @deprecated Use `preview_paths` instead. Will be removed in v3.0.0.
39
- mattr_accessor :preview_path, instance_writer: false
40
-
41
- # Set the entry route for component previews:
42
- #
43
- # config.view_component.preview_route = "/previews"
44
- #
45
- # Defaults to `/rails/view_components` when `show_previews` is enabled.
46
- #
47
- mattr_accessor :preview_route, instance_writer: false do
48
- "/rails/view_components"
49
- end
50
-
51
- # Set the controller used for previewing components:
52
- #
53
- # config.view_component.preview_controller = "MyPreviewController"
54
- #
55
- # Defaults to `ViewComponentsController`.
56
- #
57
- mattr_accessor :preview_controller, instance_writer: false do
58
- "ViewComponentsController"
59
- end
60
- end
61
- end
62
- end