Dynamically generate configuration docs using YARD (#968)

Dynamically generate configuration docs using YARD
This commit is contained in:
Joel Hawksley 2021-06-17 12:36:31 -06:00 коммит произвёл GitHub
Родитель e443c1f80a
Коммит 8682057f98
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
10 изменённых файлов: 230 добавлений и 40 удалений

1
.yardopts Normal file
Просмотреть файл

@ -0,0 +1 @@
--plugin activesupport-concern

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

@ -6,6 +6,10 @@
*Joel Hawksley*
* Add documentation for configuration options.
*Joel Hawksley*
## 2.34.0
* Add the ability to enable ActiveSupport notifications (`!render.view_component` event) with `config.view_component.instrumentation_enabled`.

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

@ -219,6 +219,8 @@ GEM
xpath (3.2.0)
nokogiri (~> 1.8)
yard (0.9.26)
yard-activesupport-concern (0.0.1)
yard (>= 0.8)
zeitwerk (2.4.2)
PLATFORMS
@ -241,6 +243,7 @@ DEPENDENCIES
slim (~> 4.0)
view_component!
yard (~> 0.9.25)
yard-activesupport-concern
BUNDLED WITH
2.2.20

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

@ -3,6 +3,7 @@
require "bundler/gem_tasks"
require "rake/testtask"
require "yard"
require "yard/mattr_accessor_handler"
Rake::TestTask.new(:test) do |t|
t.libs << "test"
@ -50,13 +51,18 @@ namespace :docs do
registry = YARD::RegistryStore.new
registry.load!(".yardoc")
instance_methods = registry.get("ViewComponent::Base").meths.select { |method| method.scope != :class }
instance_methods_to_document =
instance_methods.select do |method|
meths =
registry.
get("ViewComponent::Base").
meths.
select do |method|
!method.tag(:private) &&
method.path.include?("ViewComponent::Base") &&
method.visibility == :public
end
end.sort_by { |method| method[:name] }
instance_methods_to_document = meths.select { |method| method.scope != :class }
class_methods_to_document = meths.select { |method| method.scope == :class }
File.open("docs/api.md", "w") do |f|
f.puts("---")
@ -67,6 +73,31 @@ namespace :docs do
f.puts("<!-- Warning: AUTO-GENERATED file, do not edit. Add code comments to your Ruby instead <3 -->")
f.puts
f.puts("# API")
f.puts
f.puts("## Class methods")
class_methods_to_document.each do |method|
suffix =
if method.tag(:deprecated)
" (Deprecated)"
end
types = if method.tag(:return)&.types
" → [#{method.tag(:return).types.join(',')}]"
end
f.puts
f.puts("### #{method.sep}#{method.signature.gsub('def ', '')}#{types}#{suffix}")
f.puts
f.puts(method.docstring)
if method.tag(:deprecated)
f.puts
f.puts("_#{method.tag(:deprecated).text}_")
end
end
f.puts
f.puts("## Instance methods")
@ -90,6 +121,35 @@ namespace :docs do
f.puts("_#{method.tag(:deprecated).text}_")
end
end
f.puts
f.puts("## Configuration")
registry.
get("ViewComponent::Base").
meths.
select { |method| method[:mattr_accessor] }.
sort_by { |method| method[:name] }.
each do |method|
suffix =
if method.tag(:deprecated)
" (Deprecated)"
end
f.puts
f.puts("### #{method.sep}#{method.name}#{suffix}")
if method.docstring.length > 0
f.puts
f.puts(method.docstring)
end
if method.tag(:deprecated)
f.puts
f.puts("_#{method.tag(:deprecated).text}_")
end
end
end
end
end

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

@ -7,6 +7,20 @@ title: API
# API
## Class methods
### .with_collection(collection, **args)
Render a component for each element in a collection ([documentation](/guide/collections)):
render(ProductsComponent.with_collection(@products, foo: :bar))
### .with_collection_parameter(parameter)
Set the parameter name used when rendering elements of a collection ([documentation](/guide/collections)):
with_collection_parameter :item
## Instance methods
### #_output_postamble → [String]
@ -21,11 +35,7 @@ Called before rendering the component. Override to perform operations that depen
Called after rendering the component.
_Use `before_render` instead. Will be removed in v3.0.0._
### #render? → [Boolean]
Override to determine whether the ViewComponent should render.
_Use `#before_render` instead. Will be removed in v3.0.0._
### #controller → [ActionController::Base]
@ -35,10 +45,71 @@ The current controller. Use sparingly as doing so introduces coupling that inhib
A proxy through which to access helpers. Use sparingly as doing so introduces coupling that inhibits encapsulation & reuse, often making testing difficult.
### #with_variant(variant) → [self]
### #render? → [Boolean]
Use the provided variant instead of the one determined by the current request.
Override to determine whether the ViewComponent should render.
### #request → [ActionDispatch::Request]
The current request. Use sparingly as doing so introduces coupling that inhibits encapsulation & reuse, often making testing difficult.
### #with_variant(variant) → [self]
Use the provided variant instead of the one determined by the current request.
## Configuration
### #default_preview_layout
Set a custom default layout used for preview index and individual previews:
config.view_component.default_preview_layout = "component_preview"
### #preview_controller
Set the controller used for previewing components:
config.view_component.preview_controller = "MyPreviewController"
Defaults to `ViewComponentsController`.
### #preview_path (Deprecated)
_Use `preview_paths` instead. Will be removed in v3.0.0._
### #preview_paths
Set the location of component previews:
config.view_component.preview_paths << "#{Rails.root}/lib/component_previews"
### #preview_route
Set the entry route for component previews:
config.view_component.preview_route = "/previews"
Defaults to `/rails/view_components` when `show_previews` is enabled.
### #render_monkey_patch_enabled
Set if render monkey patches should be included or not in Rails <6.1:
config.view_component.render_monkey_patch_enabled = false
### #show_previews
Enable or disable component previews:
config.view_component.show_previews = true
Defaults to `true` in development.
### #test_controller
Set the controller used for testing components:
config.view_component.test_controller = "MyTestController"
Defaults to ApplicationController. Can also be configured on a per-test
basis using `with_controller_class`.

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

@ -119,7 +119,7 @@ module ViewComponent
# Called after rendering the component.
#
# @deprecated Use `before_render` instead. Will be removed in v3.0.0.
# @deprecated Use `#before_render` instead. Will be removed in v3.0.0.
# @return [void]
def before_render_check
# noop
@ -225,17 +225,24 @@ module ViewComponent
@__vc_content_evaluated
end
# The controller used for testing components.
# Defaults to ApplicationController, but can be configured
# on a per-test basis using `with_controller_class`.
# This should be set early in the initialization process and should be a string.
# Set the controller used for testing components:
#
# config.view_component.test_controller = "MyTestController"
#
# Defaults to ApplicationController. Can also be configured on a per-test
# basis using `with_controller_class`.
#
mattr_accessor :test_controller
@@test_controller = "ApplicationController"
# Configure if render monkey patches should be included or not in Rails <6.1.
# Set if render monkey patches should be included or not in Rails <6.1:
#
# config.view_component.render_monkey_patch_enabled = false
#
mattr_accessor :render_monkey_patch_enabled, instance_writer: false, default: true
class << self
# @private
attr_accessor :source_location, :virtual_path
# EXPERIMENTAL: This API is experimental and may be removed at any time.
@ -245,6 +252,7 @@ module ViewComponent
# strings starting without the "dot", example: `["erb", "haml"]`.
#
# For example, one might collect sidecar CSS files that need to be compiled.
# @private TODO: add documentation
def _sidecar_files(extensions)
return [] unless source_location
@ -279,16 +287,24 @@ module ViewComponent
(sidecar_files - [source_location] + sidecar_directory_files + nested_component_files).uniq
end
# Render a component collection.
# Render a component for each element in a collection ([documentation](/guide/collections)):
#
# render(ProductsComponent.with_collection(@products, foo: :bar))
#
# @param collection [Enumerable] A list of items to pass the ViewComponent one at a time.
# @param args [Arguments] Arguments to pass to the ViewComponent every time.
def with_collection(collection, **args)
Collection.new(self, collection, **args)
end
# Provide identifier for ActionView template annotations
#
# @private
def short_identifier
@short_identifier ||= defined?(Rails.root) ? source_location.sub("#{Rails.root}/", "") : source_location
end
# @private
def inherited(child)
# Compile so child will inherit compiled `call_*` template methods that
# `compile` defines
@ -311,6 +327,7 @@ module ViewComponent
super
end
# @private
def compiled?
compiler.compiled?
end
@ -319,30 +336,39 @@ module ViewComponent
#
# Do as much work as possible in this step, as doing so reduces the amount
# of work done each time a component is rendered.
# @private
def compile(raise_errors: false)
compiler.compile(raise_errors: raise_errors)
end
# @private
def compiler
@__vc_compiler ||= Compiler.new(self)
end
# we'll eventually want to update this to support other types
# @private
def type
"text/html"
end
# @private
def format
:html
end
# @private
def identifier
source_location
end
# Support overriding collection parameter name
def with_collection_parameter(param)
@provided_collection_parameter = param
# Set the parameter name used when rendering elements of a collection ([documentation](/guide/collections)):
#
# with_collection_parameter :item
#
# @param parameter [Symbol] The parameter name used when rendering elements of a collection.
def with_collection_parameter(parameter)
@provided_collection_parameter = parameter
end
# Ensure the component initializer accepts the
@ -350,6 +376,7 @@ module ViewComponent
# validate that the default parameter name
# is accepted, as support for collection
# rendering is optional.
# @private TODO: add documentation
def validate_collection_parameter!(validate_default: false)
parameter = validate_default ? collection_parameter : provided_collection_parameter
@ -374,6 +401,7 @@ module ViewComponent
# Ensure the component initializer does not define
# invalid parameters that could override the framework's
# methods.
# @private TODO: add documentation
def validate_initialization_parameters!
return unless initialize_parameter_names.include?(RESERVED_PARAMETER)
@ -384,6 +412,7 @@ module ViewComponent
)
end
# @private
def collection_parameter
if provided_collection_parameter
provided_collection_parameter
@ -392,18 +421,22 @@ module ViewComponent
end
end
# @private
def collection_counter_parameter
"#{collection_parameter}_counter".to_sym
end
# @private
def counter_argument_present?
initialize_parameter_names.include?(collection_counter_parameter)
end
# @private
def collection_iteration_parameter
"#{collection_parameter}_iteration".to_sym
end
# @private
def iteration_argument_present?
initialize_parameter_names.include?(collection_iteration_parameter)
end

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

@ -26,7 +26,7 @@ module ViewComponent
if subclass_instance_methods.include?(:before_render_check)
ActiveSupport::Deprecation.warn(
"`before_render_check` will be removed in v3.0.0. Use `before_render` instead."
"`before_render_check` will be removed in v3.0.0. Use `#before_render` instead."
)
end

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

@ -2,51 +2,49 @@
require "active_support/concern"
module ViewComponent # :nodoc:
module ViewComponent
module Previewable
extend ActiveSupport::Concern
included do
# Set a custom default preview layout through app configuration:
# Enable or disable component previews:
#
# config.view_component.show_previews = true
#
# Defaults to `true` in development.
#
mattr_accessor :show_previews, instance_writer: false
# Set a custom default layout used for preview index and individual previews:
#
# config.view_component.default_preview_layout = "component_preview"
#
# This affects preview index pages as well as individual component previews
#
mattr_accessor :default_preview_layout, instance_writer: false
# Set the location of component previews through app configuration:
# Set the location of component previews:
#
# config.view_component.preview_paths << "#{Rails.root}/lib/component_previews"
#
mattr_accessor :preview_paths, instance_writer: false
# TODO: deprecated, remove in v3.0.0
# @deprecated Use `preview_paths` instead. Will be removed in v3.0.0.
mattr_accessor :preview_path, instance_writer: false
# Enable or disable component previews through app configuration:
#
# config.view_component.show_previews = true
#
# Defaults to +true+ for development environment
#
mattr_accessor :show_previews, instance_writer: false
# Set the entry route for component previews through app configuration:
# Set the entry route for component previews:
#
# config.view_component.preview_route = "/previews"
#
# Defaults to +/rails/view_components+ when `show_previews' is enabled
# 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 to be used for previewing components through app configuration:
# Set the controller used for previewing components:
#
# config.view_component.preview_controller = "MyPreviewController"
#
# Defaults to the provided +ViewComponentsController+
# Defaults to `ViewComponentsController`.
#
mattr_accessor :preview_controller, instance_writer: false do
"ViewComponentsController"

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

@ -0,0 +1,19 @@
# frozen_string_literal: true
module YARD
# YARD Handler to parse `mattr_accessor` calls.
class MattrAccessorHandler < YARD::Handlers::Ruby::Base
handles method_call(:mattr_accessor)
namespace_only
process do
name = statement.parameters.first.jump(:tstring_content, :ident).source
object = YARD::CodeObjects::MethodObject.new(namespace, name)
register(object)
parse_block(statement.last, owner: object)
object.dynamic = true
object[:mattr_accessor] = true
end
end
end

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

@ -43,4 +43,5 @@ Gem::Specification.new do |spec|
spec.add_development_dependency "simplecov-console", "~> 0.7.2"
spec.add_development_dependency "pry", "~> 0.13"
spec.add_development_dependency "yard", "~> 0.9.25"
spec.add_development_dependency "yard-activesupport-concern"
end