Centralize ViewComponent::Base config (#1291)

* Update changelog

* Clean up mirrored config options from ViewComponent::Base

* Refactor generate test helper

* Test that app config and ViewComponent::Base config are equivalent

* Update documentation on defaults

* Update lib/view_component/base.rb

Co-authored-by: Hans Lemuet <Spone@users.noreply.github.com>

* Create dedicated config object

* Move default config to constant

* Test to ensure preview_path alias is maintained

* Use OrderedOptions for generate config option to maintain behavior

* Update some defaults

* Remove duplicate config definitions

These are specified in ViewComponent::Config now.

* Clarify necessity of specifying preview paths

* Delegate the majority of config options back to application config

* Move remainder of Previewable accessors to app config

* Extract out default preview paths

* Make all config accessors accessible on ViewComponent::Base by default

* Remove the Previewable module

ALl it did was make preview-related config accessible on ViewComponent::Base

* Add default_preview_layout to global config

* Run `bundle update`

* Make ViewComponent::Base the source of truth for config

* Add backwards compatibility for config_accessor defaults

* Restore documentation for config options

* Improve test coverage

* Directly require ViewComponent::Base to fix Primer CI

* Directly require ViewComponent::Config to fix Primer CI

* Move config require to ViewComponent::Base

* Require ViewComponent::Deprecation in Config

* Add missing defaults

* Reset default_preview_layout to `nil`

* Remove all changes to docs/api.md

* Add code ownership

* s/with_generate/with_generate_option/

* Explain alias between .default and .new

* Draft test to ensure all config options are documented

The pieces are in place, but I think the final aim is that everything
for which there's a default and/or a reader is documented

* Ensure accessors defined in public methods are considered too

* Coerce to string for backwards compatibility

* Lint

* Use Ruby 3.0.3

* Add more detailed changelog message

Co-authored-by: Joel Hawksley <joel@hawksley.org>

* Manually restore the Previewable changes after rebase

* Lint

* Remove global output buffer option

* Remove previewable module

Config options for preview are now under ViewComponent::Config.

* Make Rails.application.config.view_component the source of truth

ViewComponent::Base should continue to work but will delegate back to that.

* Update missed stub

* Update Gemfile.lock

* Compose around ActiveSupport::OrderedOptions

Co-authored-by: Hans Lemuet <Spone@users.noreply.github.com>
Co-authored-by: Joel Hawksley <joel@hawksley.org>
This commit is contained in:
Simon Fish 2022-08-09 14:37:16 +01:00 коммит произвёл GitHub
Родитель 8ed02f923b
Коммит c271c5ac06
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
22 изменённых файлов: 325 добавлений и 198 удалений

3
.github/CODEOWNERS поставляемый
Просмотреть файл

@ -26,7 +26,6 @@
/app/**/* @juanmanuelramallo
/docs/guide/previews.md @juanmanuelramallo
/lib/view_component/preview.rb @juanmanuelramallo
/lib/view_component/previewable.rb @juanmanuelramallo
/lib/view_component/preview_template_error.rb @juanmanuelramallo
/test/sandbox/test/components/previews/preview_source_from_layout_component_preview.rb @juanmanuelramallo
/test/view_component/default_preview_layout_integration_test.rb @juanmanuelramallo
@ -46,3 +45,5 @@
/script/replicate-bug @boardfish
/lib/view_component/docs_builder_component.rb @boardfish
/lib/view_component/docs_builder_component.html.erb @boardfish
/lib/view_component/config.rb @boardfish
/test/view_component/config_test.rb @boardfish

Просмотреть файл

@ -70,9 +70,7 @@ namespace :docs do
instance_methods_to_document = meths.select { |method| method.scope != :class }
class_methods_to_document = meths.select { |method| method.scope == :class }
configuration_methods_to_document = registry.get("ViewComponent::Base").meths.select { |method|
method[:mattr_accessor]
}
configuration_methods_to_document = registry.get("ViewComponent::Config").meths.select(&:reader?)
test_helper_methods_to_document = registry
.get("ViewComponent::TestHelpers")
.meths

Просмотреть файл

@ -10,9 +10,7 @@ module ViewComponent
around_action :set_locale, only: :previews
before_action :require_local!, unless: :show_previews?
if respond_to?(:content_security_policy)
content_security_policy(false)
end
content_security_policy(false) if respond_to?(:content_security_policy)
end
def index
@ -45,18 +43,18 @@ module ViewComponent
# :doc:
def default_preview_layout
ViewComponent::Base.default_preview_layout
Rails.application.config.view_component.default_preview_layout
end
# :doc:
def show_previews?
ViewComponent::Base.show_previews
Rails.application.config.view_component.show_previews
end
# :doc:
def find_preview
candidates = []
params[:path].to_s.scan(%r{/|$}) { candidates << $` }
params[:path].to_s.scan(%r{/|$}) { candidates << Regexp.last_match.pre_match }
preview = candidates.detect { |candidate| ViewComponent::Preview.exists?(candidate) }
if preview
@ -66,10 +64,8 @@ module ViewComponent
end
end
def set_locale
I18n.with_locale(params[:locale] || I18n.default_locale) do
yield
end
def set_locale(&block)
I18n.with_locale(params[:locale] || I18n.default_locale, &block)
end
# Returns either {} or {layout: value} depending on configuration

Просмотреть файл

@ -1,7 +1,7 @@
# frozen_string_literal: true
module PreviewHelper
AVAILABLE_PRISM_LANGUAGES = ["ruby", "erb", "haml"]
AVAILABLE_PRISM_LANGUAGES = %w[ruby erb haml]
FALLBACK_LANGUAGE = "ruby"
def preview_source
@ -22,7 +22,7 @@ module PreviewHelper
# Fetch template source via finding it through preview paths
# to accomodate source view when exclusively using templates
# for previews for Rails < 6.1.
all_template_paths = ViewComponent::Base.preview_paths.map do |preview_path|
all_template_paths = Rails.application.config.view_component.preview_paths.map do |preview_path|
Dir.glob("#{preview_path}/**/*")
end.flatten

Просмотреть файл

@ -1,5 +1,5 @@
<% if @render_args[:component] %>
<% if ViewComponent::Base.render_monkey_patch_enabled || Rails.version.to_f >= 6.1 %>
<% if Rails.application.config.view_component.render_monkey_patch_enabled || Rails.version.to_f >= 6.1 %>
<%= render(@render_args[:component], @render_args[:args], &@render_args[:block]) %>
<% else %>
<%= render_component(@render_args[:component], &@render_args[:block]) %>
@ -8,6 +8,6 @@
<%= render template: @render_args[:template], locals: @render_args[:locals] || {} %>
<% end %>
<% if ViewComponent::Base.show_previews_source %>
<% if Rails.application.config.view_component.show_previews_source %>
<%= preview_source %>
<% end %>

Просмотреть файл

@ -9,6 +9,10 @@ title: Changelog
## main
* Add missing `generate.sidecar`, `generate.stimulus_controller`, `generate.locale`, `generate.distinct_locale_files`, `generate.preview` config options to `config.view_component`.
*Simon Fish*
## 2.65.0
* Raise `ArgumentError` when conflicting Slots are defined.

Просмотреть файл

@ -3,9 +3,7 @@
module ViewComponent
module AbstractGenerator
def copy_view_file
unless options["inline"]
template "component.html.#{engine_name}", destination
end
template "component.html.#{engine_name}", destination unless options["inline"]
end
private
@ -31,7 +29,7 @@ module ViewComponent
end
def component_path
ViewComponent::Base.view_component_path
Rails.application.config.view_component.view_component_path
end
def stimulus_controller
@ -44,7 +42,7 @@ module ViewComponent
end
def sidecar?
options["sidecar"] || ViewComponent::Base.generate.sidecar
options["sidecar"] || Rails.application.config.view_component.generate.sidecar
end
end
end

Просмотреть файл

@ -13,11 +13,12 @@ module Rails
check_class_collision suffix: "Component"
class_option :inline, type: :boolean, default: false
class_option :locale, type: :boolean, default: ViewComponent::Base.generate.locale
class_option :locale, type: :boolean, default: Rails.application.config.view_component.generate.locale
class_option :parent, type: :string, desc: "The parent class for the generated component"
class_option :preview, type: :boolean, default: ViewComponent::Base.generate.preview
class_option :preview, type: :boolean, default: Rails.application.config.view_component.generate.preview
class_option :sidecar, type: :boolean, default: false
class_option :stimulus, type: :boolean, default: ViewComponent::Base.generate.stimulus_controller
class_option :stimulus, type: :boolean,
default: Rails.application.config.view_component.generate.stimulus_controller
def create_component_file
template "component.rb", File.join(component_path, class_path, "#{file_name}_component.rb")
@ -40,7 +41,7 @@ module Rails
def parent_class
return options[:parent] if options[:parent]
ViewComponent::Base.component_parent_class || default_parent_class
Rails.application.config.view_component.component_parent_class || default_parent_class
end
def initialize_signature

Просмотреть файл

@ -12,7 +12,7 @@ module Locale
class_option :sidecar, type: :boolean, default: false
def create_locale_file
if ViewComponent::Base.generate.distinct_locale_files
if Rails.application.config.view_component.generate.distinct_locale_files
I18n.available_locales.each do |locale|
create_file destination(locale), translations_hash([locale]).to_yaml
end

Просмотреть файл

@ -10,6 +10,7 @@ module ViewComponent
autoload :Compiler
autoload :CompileCache
autoload :ComponentError
autoload :Config
autoload :Deprecation
autoload :Instrumentation
autoload :Preview

Просмотреть файл

@ -4,9 +4,9 @@ require "action_view"
require "active_support/configurable"
require "view_component/collection"
require "view_component/compile_cache"
require "view_component/config"
require "view_component/content_areas"
require "view_component/polymorphic_slots"
require "view_component/previewable"
require "view_component/slotable"
require "view_component/slotable_v2"
require "view_component/translatable"
@ -14,10 +14,16 @@ require "view_component/with_content_helper"
module ViewComponent
class Base < ActionView::Base
include ActiveSupport::Configurable
class << self
delegate(*ViewComponent::Config.defaults.keys, to: :config)
def config
Rails.application.config.view_component
end
end
include ViewComponent::ContentAreas
include ViewComponent::PolymorphicSlots
include ViewComponent::Previewable
include ViewComponent::SlotableV2
include ViewComponent::Translatable
include ViewComponent::WithContentHelper
@ -113,11 +119,9 @@ module ViewComponent
@current_template = self
if block && defined?(@__vc_content_set_by_with_content)
raise ArgumentError.new(
"It looks like a block was provided after calling `with_content` on #{self.class.name}, " \
raise ArgumentError, "It looks like a block was provided after calling `with_content` on #{self.class.name}, " \
"which means that ViewComponent doesn't know which content to use.\n\n" \
"To fix this issue, use either `with_content` or a block."
)
end
@__vc_content_evaluated = false
@ -262,9 +266,7 @@ module ViewComponent
# @private
def format
# Ruby 2.6 throws a warning without checking `defined?`, 2.7 doesn't
if defined?(@__vc_variant)
@__vc_variant
end
@__vc_variant if defined?(@__vc_variant)
end
# Use the provided variant instead of the one determined by the current request.
@ -313,11 +315,9 @@ module ViewComponent
# config.view_component.test_controller = "MyTestController"
# ```
#
# Defaults to ApplicationController. Can also be configured on a per-test
# basis using `with_controller_class`.
# Defaults to `nil`. If this is falsy, `"ApplicationController"` is used. Can also be
# configured on a per-test basis using `with_controller_class`.
#
mattr_accessor :test_controller
@@test_controller = "ApplicationController"
# Set if render monkey patches should be included or not in Rails <6.1:
#
@ -325,7 +325,6 @@ module ViewComponent
# config.view_component.render_monkey_patch_enabled = false
# ```
#
mattr_accessor :render_monkey_patch_enabled, instance_writer: false, default: true
# Path for component files
#
@ -333,9 +332,8 @@ module ViewComponent
# config.view_component.view_component_path = "app/my_components"
# ```
#
# Defaults to `app/components`.
# Defaults to `nil`. If this is falsy, `app/components` is used.
#
mattr_accessor :view_component_path, instance_writer: false, default: "app/components"
# Parent class for generated components
#
@ -346,7 +344,6 @@ module ViewComponent
# Defaults to nil. If this is falsy, generators will use
# "ApplicationComponent" if defined, "ViewComponent::Base" otherwise.
#
mattr_accessor :component_parent_class, instance_writer: false
# Configuration for generators.
#
@ -397,7 +394,6 @@ module ViewComponent
# ```
#
# Defaults to `false`.
mattr_accessor :generate, instance_writer: false, default: ActiveSupport::OrderedOptions.new(false)
class << self
# @private
@ -489,8 +485,8 @@ module ViewComponent
# If Rails application is loaded, add application url_helpers to the component context
# we need to check this to use this gem as a dependency
if defined?(Rails) && Rails.application
child.include Rails.application.routes.url_helpers unless child < Rails.application.routes.url_helpers
if defined?(Rails) && Rails.application && !(child < Rails.application.routes.url_helpers)
child.include Rails.application.routes.url_helpers
end
# Derive the source location of the component Ruby file from the call stack.
@ -500,7 +496,7 @@ module ViewComponent
# Removes the first part of the path and the extension.
child.virtual_path = child.source_location.gsub(
%r{(.*#{Regexp.quote(ViewComponent::Base.view_component_path)})|(\.rb)}, ""
/(.*#{Regexp.quote(Rails.application.config.view_component.view_component_path)})|(\.rb)/, ""
)
# Set collection parameter to the extended component
@ -591,20 +587,16 @@ module ViewComponent
# parameters will be empty and ViewComponent will not be able to render
# the component.
if initialize_parameters.empty?
raise ArgumentError.new(
"The #{self} initializer is empty or invalid." \
raise ArgumentError, "The #{self} initializer is empty or invalid." \
"It must accept the parameter `#{parameter}` to render it as a collection.\n\n" \
"To fix this issue, update the initializer to accept `#{parameter}`.\n\n" \
"See https://viewcomponent.org/guide/collections.html for more information on rendering collections."
)
end
raise ArgumentError.new(
"The initializer for #{self} doesn't accept the parameter `#{parameter}`, " \
raise ArgumentError, "The initializer for #{self} doesn't accept the parameter `#{parameter}`, " \
"which is required in order to render it as a collection.\n\n" \
"To fix this issue, update the initializer to accept `#{parameter}`.\n\n" \
"See https://viewcomponent.org/guide/collections.html for more information on rendering collections."
)
end
# Ensure the component initializer doesn't define
@ -614,10 +606,8 @@ module ViewComponent
def validate_initialization_parameters!
return unless initialize_parameter_names.include?(RESERVED_PARAMETER)
raise ViewComponent::ComponentError.new(
"#{self} initializer can't accept the parameter `#{RESERVED_PARAMETER}`, as it will override a " \
raise ViewComponent::ComponentError, "#{self} initializer can't accept the parameter `#{RESERVED_PARAMETER}`, as it will override a " \
"public ViewComponent method. To fix this issue, rename the parameter."
)
end
# @private

Просмотреть файл

@ -0,0 +1,159 @@
# frozen_string_literal: true
require "view_component/deprecation"
module ViewComponent
class Config
class << self
# `new` without any arguments initializes the default configuration, but
# it's important to differentiate in case that's no longer the case in
# future.
alias_method :default, :new
def defaults
ActiveSupport::OrderedOptions.new.merge!({
generate: ActiveSupport::OrderedOptions.new(false),
preview_controller: "ViewComponentsController",
preview_route: "/rails/view_components",
show_previews_source: false,
instrumentation_enabled: false,
render_monkey_patch_enabled: true,
view_component_path: "app/components",
component_parent_class: nil,
show_previews: Rails.env.development? || Rails.env.test?,
preview_paths: default_preview_paths,
test_controller: "ApplicationController",
default_preview_layout: nil
})
end
# @!attribute generate
# @return [ActiveSupport::OrderedOptions]
# The subset of configuration options relating to generators.
#
# All options under this namespace default to `false` unless otherwise
# stated.
#
# #### #sidecar
#
# Always generate a component with a sidecar directory:
#
# config.view_component.generate.sidecar = true
#
# #### #stimulus_controller
#
# Always generate a Stimulus controller alongside the component:
#
# config.view_component.generate.stimulus_controller = true
#
# #### #locale
#
# Always generate translations file alongside the component:
#
# config.view_component.generate.locale = true
#
# #### #distinct_locale_files
#
# Always generate as many translations files as available locales:
#
# config.view_component.generate.distinct_locale_files = true
#
# One file will be generated for each configured `I18n.available_locales`,
# falling back to `[:en]` when no `available_locales` is defined.
#
# #### #preview
#
# Always generate a preview alongside the component:
#
# config.view_component.generate.preview = true
# @!attribute preview_controller
# @return [String]
# The controller used for previewing components.
# Defaults to `ViewComponentsController`.
# @!attribute preview_route
# @return [String]
# The entry route for component previews.
# Defaults to `"/rails/view_components"`.
# @!attribute show_previews_source
# @return [Boolean]
# Whether to display source code previews in component previews.
# Defaults to `false`.
# @!attribute instrumentation_enabled
# @return [Boolean]
# Whether ActiveSupport notifications are enabled.
# Defaults to `false`.
# @!attribute render_monkey_patch_enabled
# @return [Boolean] Whether the #render method should be monkey patched.
# If this is disabled, use `#render_component` or
# `#render_component_to_string` instead.
# Defaults to `true`.
# @!attribute view_component_path
# @return [String]
# The path in which components, their templates, and their sidecars should
# be stored.
# Defaults to `"app/components"`.
# @!attribute component_parent_class
# @return [String]
# The parent class from which generated components will inherit.
# Defaults to `nil`. If this is falsy, generators will use
# `"ApplicationComponent"` if defined, `"ViewComponent::Base"` otherwise.
# @!attribute show_previews
# @return [Boolean]
# Whether component previews are enabled.
# Defaults to `true` in development and test environments.
# @!attribute preview_paths
# @return [Array<String>]
# The locations in which component previews will be looked up.
# Defaults to `['test/component/previews']` relative to your Rails root.
# @!attribute preview_path
# @deprecated Use #preview_paths instead. Will be removed in v3.0.0.
# @!attribute test_controller
# @return [String]
# The controller used for testing components.
# Can also be configured on a per-test basis using `#with_controller_class`.
# Defaults to `ApplicationController`.
# @!attribute default_preview_layout
# @return [String]
# A custom default layout used for the previews index page and individual
# previews.
# Defaults to `nil`. If this is falsy, `"component_preview"` is used.
def default_preview_paths
return [] unless defined?(Rails.root) && Dir.exist?("#{Rails.root}/test/components/previews")
["#{Rails.root}/test/components/previews"]
end
end
def initialize
@config = self.class.defaults
end
def preview_path
preview_paths
end
def preview_path=(new_value)
ViewComponent::Deprecation.warn("`preview_path` will be removed in v3.0.0. Use `preview_paths` instead.")
self.preview_paths = Array.wrap(new_value)
end
delegate_missing_to :config
private
attr_reader :config
end
end

Просмотреть файл

@ -1,11 +1,11 @@
# frozen_string_literal: true
require "rails"
require "view_component/base"
module ViewComponent
class Engine < Rails::Engine # :nodoc:
config.view_component = ActiveSupport::OrderedOptions.new
config.view_component.preview_paths ||= []
config.view_component = ViewComponent::Config.default
rake_tasks do
load "view_component/rails/tasks/view_component.rake"
@ -14,25 +14,20 @@ module ViewComponent
initializer "view_component.set_configs" do |app|
options = app.config.view_component
%i[generate preview_controller preview_route show_previews_source].each do |config_option|
options[config_option] ||= ViewComponent::Base.public_send(config_option)
end
options.instrumentation_enabled = false if options.instrumentation_enabled.nil?
options.render_monkey_patch_enabled = true if options.render_monkey_patch_enabled.nil?
options.show_previews = Rails.env.development? || Rails.env.test? if options.show_previews.nil?
options.show_previews_source ||= ViewComponent::Base.show_previews_source
options.instrumentation_enabled = false if options.instrumentation_enabled.nil?
options.preview_route ||= ViewComponent::Base.preview_route
options.preview_controller ||= ViewComponent::Base.preview_controller
if options.show_previews
# This is still necessary because when `config.view_component` is declared, `Rails.root` is unspecified.
options.preview_paths << "#{Rails.root}/test/components/previews" if defined?(Rails.root) && Dir.exist?(
"#{Rails.root}/test/components/previews"
)
if options.preview_path.present?
ViewComponent::Deprecation.warn(
"`preview_path` will be removed in v3.0.0. Use `preview_paths` instead."
)
options.preview_paths << options.preview_path
end
if options.show_previews_source
require "method_source"
@ -41,10 +36,6 @@ module ViewComponent
end
end
end
ActiveSupport.on_load(:view_component) do
options.each { |k, v| send("#{k}=", v) if respond_to?("#{k}=") }
end
end
initializer "view_component.enable_instrumentation" do |app|
@ -72,12 +63,6 @@ module ViewComponent
end
end
initializer "view_component.compile_config_methods" do
ActiveSupport.on_load(:view_component) do
config.compile_methods! if config.respond_to?(:compile_methods!)
end
end
initializer "view_component.monkey_patch_render" do |app|
next if Rails.version.to_f >= 6.1 || !app.config.view_component.render_monkey_patch_enabled
@ -94,7 +79,7 @@ module ViewComponent
end
end
initializer "view_component.include_render_component" do |app|
initializer "view_component.include_render_component" do |_app|
next if Rails.version.to_f >= 6.1
ActiveSupport.on_load(:action_view) do
@ -116,7 +101,7 @@ module ViewComponent
end
end
initializer "compiler mode" do |app|
initializer "compiler mode" do |_app|
ViewComponent::Compiler.mode = if Rails.env.development? || Rails.env.test?
ViewComponent::Compiler::DEVELOPMENT_MODE
else
@ -160,7 +145,7 @@ unless defined?(ViewComponent::Base)
ViewComponent::Deprecation.warn(
"This manually engine loading is deprecated and will be removed in v3.0.0. " \
"Remove `require \"view_component/engine\"`."
'Remove `require "view_component/engine"`.'
)
require "view_component"

Просмотреть файл

@ -1,62 +0,0 @@
# frozen_string_literal: true
require "active_support/concern"
module ViewComponent
module Previewable
extend ActiveSupport::Concern
included do
# Enable or disable component previews:
#
# config.view_component.show_previews = true
#
# Defaults to `true` in development.
#
mattr_accessor :show_previews, instance_writer: false
# Enable or disable source code previews in component previews:
#
# config.view_component.show_previews_source = true
#
# Defaults to `false`.
#
mattr_accessor :show_previews_source, instance_writer: false, default: false
# Set a custom default layout used for preview index and individual previews:
#
# config.view_component.default_preview_layout = "component_preview"
#
mattr_accessor :default_preview_layout, instance_writer: false
# Set the location of component previews:
#
# config.view_component.preview_paths << "#{Rails.root}/lib/component_previews"
#
mattr_accessor :preview_paths, instance_writer: false
# @deprecated Use `preview_paths` instead. Will be removed in v3.0.0.
mattr_accessor :preview_path, instance_writer: false
# Set the entry route for component previews:
#
# config.view_component.preview_route = "/previews"
#
# Defaults to `/rails/view_components` when `show_previews` is enabled.
#
mattr_accessor :preview_route, instance_writer: false do
"/rails/view_components"
end
# Set the controller used for previewing components:
#
# config.view_component.preview_controller = "MyPreviewController"
#
# Defaults to `ViewComponentsController`.
#
mattr_accessor :preview_controller, instance_writer: false do
"ViewComponentsController"
end
end
end
end

Просмотреть файл

@ -22,9 +22,7 @@ module ViewComponent
def render_preview(name)
begin
preview_klass = if respond_to?(:described_class)
if described_class.nil?
raise "`render_preview` expected a described_class, but it is nil."
end
raise "`render_preview` expected a described_class, but it is nil." if described_class.nil?
"#{described_class}Preview"
else
@ -32,12 +30,10 @@ module ViewComponent
end
preview_klass = preview_klass.constantize
rescue NameError
raise NameError.new(
"`render_preview` expected to find #{preview_klass}, but it does not exist."
)
raise NameError, "`render_preview` expected to find #{preview_klass}, but it does not exist."
end
previews_controller = build_controller(ViewComponent::Base.preview_controller.constantize)
previews_controller = build_controller(Rails.application.config.view_component.preview_controller.constantize)
previews_controller.request.params[:path] = "#{preview_klass.preview_name}/#{name}"
previews_controller.response = ActionDispatch::Response.new
result = previews_controller.previews

Просмотреть файл

@ -2,7 +2,7 @@
class ContainerComponent < ViewComponent::Base
def call
if ViewComponent::Base.render_monkey_patch_enabled || Rails.version.to_f >= 6.1
if Rails.application.config.view_component.render_monkey_patch_enabled || Rails.version.to_f >= 6.1
render HelpersProxyComponent.new
else
render_component HelpersProxyComponent.new

Просмотреть файл

@ -1,6 +1,6 @@
# frozen_string_literal: true
require File.expand_path("../boot", __FILE__)
require File.expand_path("boot", __dir__)
require "active_model/railtie"
require "action_controller/railtie"

Просмотреть файл

@ -0,0 +1,53 @@
# frozen_string_literal: true
require "test_helper"
module ViewComponent
class ConfigTest < TestCase
def setup
@config = ViewComponent::Config.new
end
def test_defaults_are_correct
assert_equal @config.generate, {}
assert_equal @config.preview_controller, "ViewComponentsController"
assert_equal @config.preview_route, "/rails/view_components"
assert_equal @config.show_previews_source, false
assert_equal @config.instrumentation_enabled, false
assert_equal @config.render_monkey_patch_enabled, true
assert_equal @config.show_previews, true
assert_equal @config.preview_paths, ["#{Rails.root}/test/components/previews"]
end
def test_preview_path_alias
@config.preview_path << "some/new/path"
assert_equal @config.preview_paths, @config.preview_path
end
def test_preview_path_setter_alias
old_value = @config.preview_path
@config.preview_path = "some/new/path"
assert_equal @config.preview_path, ["some/new/path"]
@config.preview_path = old_value
end
def test_all_methods_are_documented
require "yard"
require "rake"
YARD::Rake::YardocTask.new
Rake::Task["yard"].execute
configuration_methods_to_document = YARD::RegistryStore.new.tap do |store|
store.load!(".yardoc")
end.get("ViewComponent::Config").meths.select(&:reader?).reject { |meth| meth.name == :config }
default_options = ViewComponent::Config.defaults.keys
accessors = ViewComponent::Config.instance_methods(false).reject do |method_name|
method_name.to_s.end_with?("=") || method_name == :method_missing
end
options_defined_on_instance = Set[*default_options, *accessors]
assert options_defined_on_instance.subset?(Set[*configuration_methods_to_document.map(&:name)]),
"Not all configuration options are documented: #{configuration_methods_to_document.map(&:name) - options_defined_on_instance.to_a}"
assert configuration_methods_to_document.map(&:docstring).all?(&:present?),
"Configuration options are missing docstrings."
end
end
end

Просмотреть файл

@ -130,9 +130,7 @@ class LocaleGeneratorTest < Rails::Generators::TestCase
private
def with_generate_distinct_locale_files
ViewComponent::Base.generate.distinct_locale_files = true
yield
ViewComponent::Base.generate.distinct_locale_files = false
def with_generate_distinct_locale_files(&block)
with_generate_option(:distinct_locale_files, true, &block)
end
end

Просмотреть файл

@ -666,4 +666,19 @@ class IntegrationTest < ActionDispatch::IntegrationTest
ActionController::Base.perform_caching = false
Rails.cache.clear
end
def test_config_options_shared_between_base_and_engine
Rails.application.config.view_component.yield_self do |config|
{
generate: config.generate.dup.tap { |c| c.sidecar = true },
preview_controller: "SomeOtherController",
preview_route: "/some/other/route",
show_previews_source: true
}.each do |option, value|
with_config_option(option, value) do
assert_equal(config.public_send(option), ViewComponent::Base.public_send(option))
end
end
end
end
end

Просмотреть файл

@ -94,7 +94,7 @@ class PreviewHelperTest < ActiveSupport::TestCase
mock = Minitest::Mock.new
mock.expect :map, [expected_template_path]
ViewComponent::Base.stub(:preview_paths, mock) do
Rails.application.config.view_component.stub(:preview_paths, mock) do
File.stub(:read, expected_source, [expected_template_path]) do
template_data = PreviewHelper.find_template_data(
lookup_context: lookup_context,
@ -120,7 +120,7 @@ class PreviewHelperTest < ActiveSupport::TestCase
mock = Minitest::Mock.new
mock.expect :map, []
ViewComponent::Base.stub :preview_paths, mock do
Rails.application.config.view_component.stub :preview_paths, mock do
exception = assert_raises RuntimeError do
PreviewHelper.find_template_data(
lookup_context: lookup_context,
@ -144,7 +144,7 @@ class PreviewHelperTest < ActiveSupport::TestCase
mock = Minitest::Mock.new
mock.expect :map, [template_identifier + ".html.haml", template_identifier + ".html.erb"]
ViewComponent::Base.stub :preview_paths, mock do
Rails.application.config.view_component.stub :preview_paths, mock do
exception = assert_raises RuntimeError do
PreviewHelper.find_template_data(
lookup_context: lookup_context,

Просмотреть файл

@ -35,19 +35,24 @@ ENV["RAILS_ENV"] = "test"
require "view_component/deprecation"
ViewComponent::Deprecation.behavior = :silence
require File.expand_path("../sandbox/config/environment.rb", __FILE__)
require File.expand_path("sandbox/config/environment.rb", __dir__)
require "rails/test_help"
def with_config_option(option_name, new_value)
old_value = Rails.application.config.view_component.public_send(option_name)
Rails.application.config.view_component.public_send("#{option_name}=", new_value)
yield
ensure
Rails.application.config.view_component.public_send("#{option_name}=", old_value)
end
# Sets custom preview paths in tests.
#
# @param new_value [Array<String>] List of preview paths
# @yield Test code to run
# @return [void]
def with_preview_paths(new_value)
old_value = Rails.application.config.view_component.preview_paths
Rails.application.config.view_component.preview_paths = new_value
yield
Rails.application.config.view_component.preview_paths = old_value
def with_preview_paths(new_value, &block)
with_config_option(:preview_paths, new_value, &block)
end
def with_preview_route(new_value)
@ -55,6 +60,7 @@ def with_preview_route(new_value)
Rails.application.config.view_component.preview_route = new_value
app.reloader.reload!
yield
ensure
Rails.application.config.view_component.preview_route = old_value
app.reloader.reload!
end
@ -64,24 +70,17 @@ def with_preview_controller(new_value)
Rails.application.config.view_component.preview_controller = new_value
app.reloader.reload!
yield
ensure
Rails.application.config.view_component.preview_controller = old_value
app.reloader.reload!
end
def with_custom_component_path(new_value)
old_value = ViewComponent::Base.view_component_path
ViewComponent::Base.view_component_path = new_value
yield
ensure
ViewComponent::Base.view_component_path = old_value
def with_custom_component_path(new_value, &block)
with_config_option(:view_component_path, new_value, &block)
end
def with_custom_component_parent_class(new_value)
old_value = ViewComponent::Base.component_parent_class
ViewComponent::Base.component_parent_class = new_value
yield
ensure
ViewComponent::Base.component_parent_class = old_value
def with_custom_component_parent_class(new_value, &block)
with_config_option(:component_parent_class, new_value, &block)
end
def with_application_component_class
@ -91,12 +90,16 @@ ensure
Object.send(:remove_const, :ApplicationComponent)
end
def with_generate_sidecar(enabled)
old_value = ViewComponent::Base.generate.sidecar
ViewComponent::Base.generate.sidecar = enabled
def with_generate_option(config_option, value)
old_value = Rails.application.config.view_component.generate[config_option]
Rails.application.config.view_component.generate[config_option] = value
yield
ensure
ViewComponent::Base.generate.sidecar = old_value
Rails.application.config.view_component.generate[config_option] = old_value
end
def with_generate_sidecar(enabled, &block)
with_generate_option(:sidecar, enabled, &block)
end
def with_new_cache
@ -111,15 +114,13 @@ ensure
ViewComponent::CompileCache.cache = old_cache
end
def without_template_annotations
def without_template_annotations(&block)
if ActionView::Base.respond_to?(:annotate_rendered_view_with_filenames)
old_value = ActionView::Base.annotate_rendered_view_with_filenames
ActionView::Base.annotate_rendered_view_with_filenames = false
app.reloader.reload! if defined?(app)
with_new_cache do
yield
end
with_new_cache(&block)
ActionView::Base.annotate_rendered_view_with_filenames = old_value
app.reloader.reload! if defined?(app)
@ -139,19 +140,12 @@ def modify_file(file, content)
end
end
def with_default_preview_layout(layout)
old_value = ViewComponent::Base.default_preview_layout
ViewComponent::Base.default_preview_layout = layout
yield
ViewComponent::Base.default_preview_layout = old_value
def with_default_preview_layout(layout, &block)
with_config_option(:default_preview_layout, layout, &block)
end
def with_render_monkey_patch_config(enabled)
old_default = ViewComponent::Base.render_monkey_patch_enabled
ViewComponent::Base.render_monkey_patch_enabled = enabled
yield
ensure
ViewComponent::Base.render_monkey_patch_enabled = old_default
def with_render_monkey_patch_config(enabled, &block)
with_config_option(:render_monkey_patch_enabled, enabled, &block)
end
def with_compiler_mode(mode)