diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 483898def..c4b990178 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -89,5 +89,6 @@ jobs: bundle update bundle exec standardrb -r "rubocop-md" bundle exec erb_lint --lint-all + bundle exec yard-lint lib/view_component/ env: RAILS_VERSION: '~> 8' diff --git a/.yard-lint.yml b/.yard-lint.yml new file mode 100644 index 000000000..dd1d36a53 --- /dev/null +++ b/.yard-lint.yml @@ -0,0 +1,17 @@ +AllValidators: + Exclude: + - '\.git' + - 'vendor/**/*' + - 'spec/**/*' + - 'test/**/*' + - 'lib/docs/**/*' + - 'lib/generators/**/*' + - 'lib/yard/**/*' + + FailOnSeverity: warning + +Documentation/UndocumentedObjects: + Enabled: false + +Documentation/UndocumentedBooleanMethods: + Enabled: false diff --git a/Gemfile b/Gemfile index 19150a26f..fafd04c6f 100644 --- a/Gemfile +++ b/Gemfile @@ -47,4 +47,5 @@ group :development, :test do gem "warning" gem "yard-activesupport-concern", "< 1" gem "yard", "< 1" + gem "yard-lint", "< 1" end diff --git a/Gemfile.lock b/Gemfile.lock index b51952e5e..85ed4d8f1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -184,7 +184,6 @@ GEM matrix (0.4.3) method_source (1.1.0) mini_mime (1.1.5) - mini_portile2 (2.8.9) minitest (6.0.2) drb (~> 2.0) prism (~> 1.5) @@ -199,9 +198,6 @@ GEM net-smtp (0.5.1) net-protocol nio4r (2.7.5) - nokogiri (1.19.1) - mini_portile2 (~> 2.8.2) - racc (~> 1.4) nokogiri (1.19.1-aarch64-linux-gnu) racc (~> 1.4) nokogiri (1.19.1-aarch64-linux-musl) @@ -413,6 +409,9 @@ GEM yard (0.9.38) yard-activesupport-concern (0.0.1) yard (>= 0.8) + yard-lint (0.2.1) + yard (~> 0.9) + zeitwerk (~> 2.6) zeitwerk (2.7.4) PLATFORMS @@ -464,6 +463,7 @@ DEPENDENCIES warning yard (< 1) yard-activesupport-concern (< 1) + yard-lint (< 1) CHECKSUMS action_text-trix (2.1.16) sha256=f645a2c21821b8449fd1d6770708f4031c91a2eedf9ef476e9be93c64e703a8a @@ -529,7 +529,6 @@ CHECKSUMS matrix (0.4.3) sha256=a0d5ab7ddcc1973ff690ab361b67f359acbb16958d1dc072b8b956a286564c5b method_source (1.1.0) sha256=181301c9c45b731b4769bc81e8860e72f9161ad7d66dd99103c9ab84f560f5c5 mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef - mini_portile2 (2.8.9) sha256=0cd7c7f824e010c072e33f68bc02d85a00aeb6fce05bb4819c03dfd3c140c289 minitest (6.0.2) sha256=db6e57956f6ecc6134683b4c87467d6dd792323c7f0eea7b93f66bd284adbc3d minitest-mock (5.27.0) sha256=7040ed7185417a966920987eaa6eaf1be4ea1fc5b25bb03ff4703f98564a55b0 net-imap (0.6.2) sha256=08caacad486853c61676cca0c0c47df93db02abc4a8239a8b67eb0981428acc6 @@ -537,7 +536,6 @@ CHECKSUMS net-protocol (0.2.2) sha256=aa73e0cba6a125369de9837b8d8ef82a61849360eba0521900e2c3713aa162a8 net-smtp (0.5.1) sha256=ed96a0af63c524fceb4b29b0d352195c30d82dd916a42f03c62a3a70e5b70736 nio4r (2.7.5) sha256=6c90168e48fb5f8e768419c93abb94ba2b892a1d0602cb06eef16d8b7df1dca1 - nokogiri (1.19.1) sha256=598b327f36df0b172abd57b68b18979a6e14219353bca87180c31a51a00d5ad3 nokogiri (1.19.1-aarch64-linux-gnu) sha256=cfdb0eafd9a554a88f12ebcc688d2b9005f9fce42b00b970e3dc199587b27f32 nokogiri (1.19.1-aarch64-linux-musl) sha256=1e2150ab43c3b373aba76cd1190af7b9e92103564063e48c474f7600923620b5 nokogiri (1.19.1-arm-linux-gnu) sha256=0a39ed59abe3bf279fab9dd4c6db6fe8af01af0608f6e1f08b8ffa4e5d407fa3 @@ -627,6 +625,7 @@ CHECKSUMS xpath (3.2.0) sha256=6dfda79d91bb3b949b947ecc5919f042ef2f399b904013eb3ef6d20dd3a4082e yard (0.9.38) sha256=721fb82afb10532aa49860655f6cc2eaa7130889df291b052e1e6b268283010f yard-activesupport-concern (0.0.1) sha256=be790cb0efc23e2e87677063598ac8b743586154657bbd9655a7f03ce78390ef + yard-lint (0.2.1) sha256=7d2d374ff6ead506ca00a417bf1e49e45a562368f366fa8f6326ffb76b8a0d20 zeitwerk (2.7.4) sha256=2bef90f356bdafe9a6c2bd32bcd804f83a4f9b8bc27f3600fff051eb3edcec8b RUBY VERSION diff --git a/lib/view_component/base.rb b/lib/view_component/base.rb index 5fee45ea1..3d68cea22 100644 --- a/lib/view_component/base.rb +++ b/lib/view_component/base.rb @@ -17,6 +17,7 @@ module ActionView class OutputBuffer + # @param buf [String, nil] optional buffer to use def with_buffer(buf = nil) new_buffer = buf || +"" old_buffer, @raw_buffer = @raw_buffer, new_buffer @@ -101,6 +102,8 @@ def initialize # # Returns HTML that has been escaped by the respective template handler. # + # @param view_context [ActionView::Base] ActionView context from calling view + # @param block [Proc] optional block to be captured within the view context # @return [String] def render_in(view_context, &block) self.class.__vc_compile(raise_errors: true) @@ -251,6 +254,9 @@ def render? # to maintain backwards compatibility. # # @private + # @param options [Object] render options or component + # @param args [Hash] additional arguments + # @param block [Proc] optional block def render(options = {}, args = {}, &block) if options.respond_to?(:set_original_view_context) options.set_original_view_context(self.__vc_original_view_context) @@ -306,6 +312,8 @@ def helpers if defined?(Rails.env) && (::Rails.env.development? || ::Rails.env.test?) # @private + # @param method_name [Symbol] the missing method name + # @param args [Array] positional arguments def method_missing(method_name, *args) # rubocop:disable Style/MissingRespondToMissing super rescue => e # rubocop:disable Style/RescueStandardError @@ -382,6 +390,7 @@ def content? # @private # Temporarily sets the virtual path to the captured value, then restores it. # This ensures translations and other path-dependent code execute with the correct scope. + # @param captured_path [String] the virtual path to set def with_captured_virtual_path(captured_path) old_virtual_path = @view_context.instance_variable_get(:@virtual_path) @view_context.instance_variable_set(:@virtual_path, captured_path) @@ -564,11 +573,14 @@ def with_collection(collection, spacer_component: nil, **args) end # @private + # @param raise_errors [Boolean] whether to raise on compile errors + # @param force [Boolean] whether to force recompilation def __vc_compile(raise_errors: false, force: false) __vc_compiler.compile(raise_errors: raise_errors, force: force) end # @private + # @param child [Class] the inheriting class def inherited(child) # Compile so child will inherit compiled `call_*` template methods that # `compile` defines @@ -674,6 +686,7 @@ def strip_trailing_whitespace? # is accepted, as support for collection # rendering is optional. # @private + # @param validate_default [Boolean] whether to validate the default parameter name def __vc_validate_collection_parameter!(validate_default: false) parameter = validate_default ? __vc_collection_parameter : __vc_provided_collection_parameter diff --git a/lib/view_component/collection.rb b/lib/view_component/collection.rb index 7d2fb32f4..878b14cda 100644 --- a/lib/view_component/collection.rb +++ b/lib/view_component/collection.rb @@ -10,12 +10,15 @@ class Collection delegate :size, to: :@collection + # @param view_context [ActionView::Base] the view context + # @param block [Proc] optional block def render_in(view_context, &block) components.map do |component| component.render_in(view_context, &block) end.join(rendered_spacer(view_context)).html_safe end + # @param block [Proc] the iteration block def each(&block) components.each(&block) end diff --git a/lib/view_component/compile_cache.rb b/lib/view_component/compile_cache.rb index 390a84fcb..dfac35642 100644 --- a/lib/view_component/compile_cache.rb +++ b/lib/view_component/compile_cache.rb @@ -10,14 +10,17 @@ module CompileCache module_function + # @param klass [Class] the component class to register def register(klass) cache << klass end + # @param klass [Class] the component class to check def compiled?(klass) cache.include? klass end + # @param klass [Class] the component class to invalidate def invalidate_class!(klass) cache.delete(klass) end diff --git a/lib/view_component/compiler.rb b/lib/view_component/compiler.rb index 69b555046..bbb893e0a 100644 --- a/lib/view_component/compiler.rb +++ b/lib/view_component/compiler.rb @@ -10,6 +10,7 @@ class Compiler # * false(a non-blocking mode, default in Rails production mode) class_attribute :__vc_development_mode, default: false + # @param component [Class] the component class to compile def initialize(component) @component = component @lock = Mutex.new @@ -19,6 +20,8 @@ def compiled? CompileCache.compiled?(@component) end + # @param raise_errors [Boolean] whether to raise on template errors + # @param force [Boolean] whether to force recompilation def compile(raise_errors: false, force: false) return if compiled? && !force return if @component == ViewComponent::Base @@ -57,9 +60,8 @@ def compile(raise_errors: false, force: false) end end + # @param requested_details [ActionView::TemplateDetails::Requested] i.e. locales, formats, variants # @return all matching compiled templates, in priority order based on the requested details from LookupContext - # - # @param [ActionView::TemplateDetails::Requested] requested_details i.e. locales, formats, variants def find_templates_for(requested_details) filtered_templates = @templates.select do |template| template.details.matches?(requested_details) diff --git a/lib/view_component/errors.rb b/lib/view_component/errors.rb index b6aa38e4d..d6e13ccd0 100644 --- a/lib/view_component/errors.rb +++ b/lib/view_component/errors.rb @@ -11,12 +11,15 @@ class DuplicateSlotContentError < StandardError "which means that ViewComponent doesn't know which content to use.\n\n" \ "To fix this issue, use either `with_content` or a block." + # @param klass_name [String] the name of the component class def initialize(klass_name) super(MESSAGE.gsub("COMPONENT", klass_name.to_s)) end end class TemplateError < StandardError + # @param errors [Array] the list of template errors + # @param templates [Array, nil] the associated templates def initialize(errors, templates = nil) message = errors.join("\n") @@ -33,6 +36,7 @@ class MissingPreviewTemplateError < StandardError "A preview template for example EXAMPLE doesn't exist.\n\n" \ "To fix this issue, create a template for the example." + # @param example [String] the name of the preview example def initialize(example) super(MESSAGE.gsub("EXAMPLE", example)) end @@ -43,6 +47,8 @@ class MissingTemplateError < StandardError "No templates for COMPONENT match the request DETAIL.\n\n" \ "To fix this issue, provide a suitable template." + # @param component [String] the component identifier + # @param request_detail [Object] the request detail constraints def initialize(component, request_detail) detail = { locale: request_detail.locale, @@ -60,6 +66,7 @@ class DuplicateContentError < StandardError "which means that ViewComponent doesn't know which content to use.\n\n" \ "To fix this issue, use either `with_content` or a block." + # @param klass_name [String] the name of the component class def initialize(klass_name) super(MESSAGE.gsub("COMPONENT", klass_name.to_s)) end @@ -72,6 +79,8 @@ class MissingCollectionArgumentError < StandardError "To fix this issue, update the initializer to accept `PARAMETER`.\n\n" \ "See [the collections docs](https://viewcomponent.org/guide/collections.html) for more information on rendering collections." + # @param klass_name [String] the name of the component class + # @param parameter [Symbol] the missing collection parameter def initialize(klass_name, parameter) super(MESSAGE.gsub("COMPONENT", klass_name.to_s).gsub("PARAMETER", parameter.to_s)) end @@ -82,6 +91,8 @@ class ReservedParameterError < StandardError "COMPONENT initializer can't accept the parameter `PARAMETER`, as it will override a " \ "public ViewComponent method. To fix this issue, rename the parameter." + # @param klass_name [String] the name of the component class + # @param parameter [Symbol] the reserved parameter name def initialize(klass_name, parameter) super(MESSAGE.gsub("COMPONENT", klass_name.to_s).gsub("PARAMETER", parameter.to_s)) end @@ -99,6 +110,7 @@ class ContentSlotNameError < StandardError "Content passed to a ViewComponent as a block is captured and assigned to the `content` accessor without having to create an explicit slot.\n\n" \ "To fix this issue, either use the `content` accessor directly or choose a different slot name." + # @param klass_name [String] the name of the component class def initialize(klass_name) super(MESSAGE.gsub("COMPONENT", klass_name.to_s)) end @@ -120,6 +132,8 @@ class SlotPredicateNameError < InvalidSlotNameError "methods ending in `?`.\n\n" \ "To fix this issue, choose a different name." + # @param klass_name [String] the name of the component class + # @param slot_name [Symbol] the invalid slot name def initialize(klass_name, slot_name) super(MESSAGE.gsub("COMPONENT", klass_name.to_s).gsub("SLOT_NAME", slot_name.to_s)) end @@ -130,6 +144,8 @@ class RedefinedSlotError < StandardError "COMPONENT declares the SLOT_NAME slot multiple times.\n\n" \ "To fix this issue, choose a different slot name." + # @param klass_name [String] the name of the component class + # @param slot_name [Symbol] the redefined slot name def initialize(klass_name, slot_name) super(MESSAGE.gsub("COMPONENT", klass_name.to_s).gsub("SLOT_NAME", slot_name.to_s)) end @@ -140,6 +156,8 @@ class ReservedSingularSlotNameError < InvalidSlotNameError "COMPONENT declares a slot named SLOT_NAME, which is a reserved word in the ViewComponent framework.\n\n" \ "To fix this issue, choose a different name." + # @param klass_name [String] the name of the component class + # @param slot_name [Symbol] the reserved slot name def initialize(klass_name, slot_name) super(MESSAGE.gsub("COMPONENT", klass_name.to_s).gsub("SLOT_NAME", slot_name.to_s)) end @@ -150,6 +168,8 @@ class ReservedPluralSlotNameError < InvalidSlotNameError "COMPONENT declares a slot named SLOT_NAME, which is a reserved word in the ViewComponent framework.\n\n" \ "To fix this issue, choose a different name." + # @param klass_name [String] the name of the component class + # @param slot_name [Symbol] the reserved slot name def initialize(klass_name, slot_name) super(MESSAGE.gsub("COMPONENT", klass_name.to_s).gsub("SLOT_NAME", slot_name.to_s)) end @@ -160,6 +180,8 @@ class UncountableSlotNameError < InvalidSlotNameError "COMPONENT declares a slot named SLOT_NAME, which is an uncountable word\n\n" \ "To fix this issue, choose a different name." + # @param klass_name [String] the name of the component class + # @param slot_name [Symbol] the uncountable slot name def initialize(klass_name, slot_name) super(MESSAGE.gsub("COMPONENT", klass_name.to_s).gsub("SLOT_NAME", slot_name.to_s)) end @@ -168,6 +190,7 @@ def initialize(klass_name, slot_name) class ContentAlreadySetForPolymorphicSlotError < StandardError MESSAGE = "Content for slot SLOT_NAME has already been provided." + # @param slot_name [Symbol] the polymorphic slot name def initialize(slot_name) super(MESSAGE.gsub("SLOT_NAME", slot_name.to_s)) end @@ -215,6 +238,8 @@ class AlreadyDefinedPolymorphicSlotSetterError < StandardError "A method called 'SETTER_METHOD_NAME' already exists and would be overwritten by the 'SETTER_NAME' polymorphic " \ "slot setter.\n\nPlease choose a different setter name." + # @param setter_method_name [Symbol] the existing method name + # @param setter_name [Symbol] the polymorphic slot setter name def initialize(setter_method_name, setter_name) super(MESSAGE.gsub("SETTER_METHOD_NAME", setter_method_name.to_s).gsub("SETTER_NAME", setter_name.to_s)) end diff --git a/lib/view_component/inline_template.rb b/lib/view_component/inline_template.rb index 95bb2acac..6fa4dbb5f 100644 --- a/lib/view_component/inline_template.rb +++ b/lib/view_component/inline_template.rb @@ -7,6 +7,8 @@ module InlineTemplate Template = Struct.new(:source, :language, :path, :lineno) class_methods do + # @param method [Symbol] the method name + # @param args [Array] the method arguments def method_missing(method, *args) return super if !method.end_with?("_template") @@ -34,6 +36,8 @@ def method_missing(method, *args) @__vc_inline_template_defined = true end + # @param method [Symbol] the method name + # @param include_all [Boolean] whether to include private methods def respond_to_missing?(method, include_all = false) method.end_with?("_template") || super end @@ -46,6 +50,7 @@ def __vc_inline_template_language @__vc_inline_template_language if defined?(@__vc_inline_template_language) end + # @param subclass [Class] the inheriting class def inherited(subclass) super subclass.instance_variable_set(:@__vc_inline_template_language, __vc_inline_template_language) diff --git a/lib/view_component/instrumentation.rb b/lib/view_component/instrumentation.rb index 2b50d84f2..fdb6536a5 100644 --- a/lib/view_component/instrumentation.rb +++ b/lib/view_component/instrumentation.rb @@ -4,10 +4,13 @@ module ViewComponent # :nodoc: module Instrumentation + # @param mod [Module] the module being included into def self.included(mod) mod.prepend(self) unless self <= ViewComponent::Instrumentation end + # @param view_context [ActionView::Base] the view context + # @param block [Proc] optional block def render_in(view_context, &block) return super if !Rails.application.config.view_component.instrumentation_enabled.present? diff --git a/lib/view_component/preview.rb b/lib/view_component/preview.rb index 64a1c0885..115700aab 100644 --- a/lib/view_component/preview.rb +++ b/lib/view_component/preview.rb @@ -13,6 +13,9 @@ class Preview include ActionView::Helpers::AssetTagHelper extend ActiveSupport::DescendantsTracker + # @param component [ViewComponent::Base] the component instance to render + # @param args [Hash] additional arguments + # @param block [Proc] optional block def render(component, **args, &block) { args: args, @@ -23,6 +26,8 @@ def render(component, **args, &block) } end + # @param template [String, nil] the template path + # @param locals [Hash] local variables to pass to the template def render_with_template(template: nil, locals: {}) { template: template, @@ -39,6 +44,8 @@ def all end # Returns the arguments for rendering of the component in its layout + # @param example [String] the preview example method name + # @param params [Hash] request parameters def render_args(example, params: {}) example_params_names = instance_method(example).parameters.map(&:last) provided_params = params.slice(*example_params_names).to_h.symbolize_keys @@ -55,11 +62,13 @@ def examples end # Returns +true+ if the preview exists. + # @param preview [String] the preview name def exists?(preview) all.any? { |p| p.preview_name == preview } end # Find a component preview by its underscored class name. + # @param preview [String] the preview name def find(preview) all.find { |p| p.preview_name == preview } end @@ -71,12 +80,14 @@ def preview_name # rubocop:disable Style/TrivialAccessors # Setter for layout name. + # @param layout_name [String] the layout name def layout(layout_name) @layout = layout_name end # rubocop:enable Style/TrivialAccessors # Returns the relative path (from preview_path) to the preview example template if the template exists + # @param example [String] the preview example method name def preview_example_template_path(example) preview_path = Array(preview_paths).detect do |path| diff --git a/lib/view_component/slot.rb b/lib/view_component/slot.rb index e70a421e2..7e87a0c9c 100644 --- a/lib/view_component/slot.rb +++ b/lib/view_component/slot.rb @@ -8,6 +8,7 @@ class Slot attr_writer :__vc_component_instance, :__vc_content_block, :__vc_content, :__vc_content_block_virtual_path + # @param parent [ViewComponent::Base] the parent component def initialize(parent) @parent = parent end @@ -21,6 +22,7 @@ def content? @__vc_component_instance.content? end + # @param args [Object] the content to set def with_content(args) if __vc_component_instance? @__vc_component_instance.with_content(args) @@ -97,6 +99,10 @@ def to_s # end # end # + # @param symbol [Symbol] the method name + # @param args [Array] positional arguments + # @param kwargs [Hash] keyword arguments + # @param block [Proc] optional block def method_missing(symbol, *args, **kwargs, &block) @__vc_component_instance.public_send(symbol, *args, **kwargs, &block) end @@ -105,6 +111,8 @@ def html_safe? to_s.html_safe? end + # @param symbol [Symbol] the method name + # @param include_all [Boolean] whether to include private methods def respond_to_missing?(symbol, include_all = false) __vc_component_instance? && @__vc_component_instance.respond_to?(symbol, include_all) end diff --git a/lib/view_component/slotable.rb b/lib/view_component/slotable.rb index f1625080d..098140a8c 100644 --- a/lib/view_component/slotable.rb +++ b/lib/view_component/slotable.rb @@ -72,6 +72,8 @@ module Slotable # on the component instance. # # <%= render_inline(MyComponent.new.with_header_content("Foo")) %> + # @param slot_name [Symbol] the name of the slot + # @param callable [Object, nil] the renderable for the slot def renders_one(slot_name, callable = nil) __vc_validate_singular_slot_name(slot_name) @@ -140,8 +142,8 @@ def renders_one(slot_name, callable = nil) # <% component.with_item(name: "Bar") do %> #

two

# <% end %> - # <% end %> - def renders_many(slot_name, callable = nil) + # <% end %> # @param slot_name [Symbol] the name of the slot + # @param callable [Object, nil] the renderable for the slot def renders_many(slot_name, callable = nil) __vc_validate_plural_slot_name(slot_name) if callable.is_a?(Hash) && callable.key?(:types) @@ -184,6 +186,7 @@ def renders_many(slot_name, callable = nil) end end + # @param slot_name [Symbol] the name of the slot def slot_type(slot_name) registered_slot = registered_slots[slot_name] if registered_slot @@ -195,6 +198,7 @@ def slot_type(slot_name) end end + # @param child [Class] the inheriting class def inherited(child) # Clone slot configuration into child class # see #test_slots_pollution @@ -352,6 +356,7 @@ def __vc_raise_if_slot_name_uncountable(slot_name) end end + # @param slot_name [Symbol] the name of the slot def __vc_get_slot(slot_name) @__vc_set_slots ||= {} content unless defined?(@__vc_content_evaluated) && @__vc_content_evaluated # ensure content is loaded so slots will be defined @@ -377,6 +382,11 @@ def __vc_get_slot(slot_name) end end + # @param slot_name [Symbol] the name of the slot + # @param slot_definition [Hash, nil] the slot configuration + # @param args [Array] positional arguments + # @param kwargs [Hash] keyword arguments + # @param block [Proc] optional content block def __vc_set_slot(slot_name, slot_definition = nil, *args, **kwargs, &block) slot_definition ||= self.class.registered_slots[slot_name] slot = Slot.new(self) @@ -441,6 +451,11 @@ def __vc_set_slot(slot_name, slot_definition = nil, *args, **kwargs, &block) slot end + # @param slot_name [Symbol] the name of the slot + # @param poly_type [Symbol, nil] the polymorphic type + # @param args [Array] positional arguments + # @param kwargs [Hash] keyword arguments + # @param block [Proc] optional content block def __vc_set_polymorphic_slot(slot_name, poly_type = nil, *args, **kwargs, &block) slot_definition = self.class.registered_slots[slot_name] diff --git a/lib/view_component/system_test_helpers.rb b/lib/view_component/system_test_helpers.rb index c5677e63b..ffd005f59 100644 --- a/lib/view_component/system_test_helpers.rb +++ b/lib/view_component/system_test_helpers.rb @@ -4,10 +4,10 @@ module ViewComponent module SystemTestHelpers include TestHelpers - # Returns a block that can be used to visit the path of the inline rendered component. # @param fragment [Nokogiri::Fragment] The fragment returned from `render_inline`. # @param layout [String] The (optional) layout to use. - # @return [Proc] A block that can be used to visit the path of the inline rendered component. + # @param block [Proc] A block that receives the component path to visit. + # @return [void] def with_rendered_component_path(fragment, layout: false, &block) file = Tempfile.new( ["rendered_#{fragment.class.name}", ".html"], diff --git a/lib/view_component/template.rb b/lib/view_component/template.rb index 23ca0c5ee..1a29e5336 100644 --- a/lib/view_component/template.rb +++ b/lib/view_component/template.rb @@ -12,6 +12,10 @@ class Template delegate :virtual_path, to: :@component delegate :format, :variant, to: :@details + # @param component [Class] the component class + # @param details [ActionView::TemplateDetails] template format details + # @param lineno [Integer, nil] the line number offset + # @param path [String, nil] the template file path def initialize(component:, details:, lineno: nil, path: nil) @component = component @details = details @@ -20,6 +24,9 @@ def initialize(component:, details:, lineno: nil, path: nil) end class File < Template + # @param component [Class] the component class + # @param details [ActionView::TemplateDetails] template format details + # @param path [String] the template file path def initialize(component:, details:, path:) @strip_annotation_line = false @@ -73,6 +80,8 @@ def compiled_source class Inline < Template attr_reader :source + # @param component [Class] the component class + # @param inline_template [ViewComponent::InlineTemplate::Template] the inline template def initialize(component:, inline_template:) details = ActionView::TemplateDetails.new(nil, inline_template.language.to_sym, nil, nil) @@ -103,6 +112,9 @@ def type end class InlineCall < Template + # @param component [Class] the component class + # @param method_name [Symbol] the call method name + # @param defined_on_self [Boolean] whether the method is defined on the component itself def initialize(component:, method_name:, defined_on_self:) variant = method_name.to_s.include?("call_") ? method_name.to_s.sub("call_", "").to_sym : nil details = ActionView::TemplateDetails.new(nil, nil, nil, variant) diff --git a/lib/view_component/test_helpers.rb b/lib/view_component/test_helpers.rb index f6763e91e..b238b1f3c 100644 --- a/lib/view_component/test_helpers.rb +++ b/lib/view_component/test_helpers.rb @@ -35,6 +35,8 @@ def assert_component_rendered # ``` # # @param component [ViewComponent::Base, ViewComponent::Collection] The instance of the component to be rendered. + # @param args [Hash] additional arguments passed to the component. + # @param block [Proc] optional block passed to the component. # @return [Nokogiri::HTML5] def render_inline(component, **args, &block) @page = nil diff --git a/lib/view_component/translatable.rb b/lib/view_component/translatable.rb index 815a1325e..6d441da03 100644 --- a/lib/view_component/translatable.rb +++ b/lib/view_component/translatable.rb @@ -43,6 +43,8 @@ def __vc_build_i18n_backend end end + # @param key [String, nil] the translation key + # @param scope [Array, String, nil] the translation scope def __vc_i18n_key(key, scope = nil) scope = scope.join(".") if scope.is_a? Array key = key&.to_s unless key.is_a?(String) @@ -51,6 +53,8 @@ def __vc_i18n_key(key, scope = nil) key end + # @param key [String, Array, nil] the translation key + # @param options [Hash] translation options def translate(key = nil, **options) return key.map { |k| translate(k, **options) } if key.is_a?(Array) @@ -68,6 +72,8 @@ def translate(key = nil, **options) class I18nBackend < ::I18n::Backend::Simple EMPTY_HASH = {}.freeze + # @param scope [String] the i18n scope + # @param load_paths [Array] paths to translation files def initialize(scope:, load_paths:) @__vc_i18n_scope = scope.split(".").map(&:to_sym) @__vc_load_paths = load_paths @@ -78,6 +84,7 @@ def load_translations super(@__vc_load_paths) end + # @param data [Hash] the translation data to scope def scope_data(data) @__vc_i18n_scope.reverse_each do |part| data = {part => data} @@ -85,11 +92,16 @@ def scope_data(data) data end + # @param locale [Symbol] the locale + # @param data [Hash] the translation data + # @param options [Hash] additional options def store_translations(locale, data, options = EMPTY_HASH) super(locale, scope_data(data), options) end end + # @param key [String, Array, nil] the translation key + # @param options [Hash] translation options def translate(key = nil, **options) raise ViewComponent::TranslateCalledBeforeRenderError if view_context.nil? diff --git a/lib/view_component/with_content_helper.rb b/lib/view_component/with_content_helper.rb index 34fe6427b..e55315e84 100644 --- a/lib/view_component/with_content_helper.rb +++ b/lib/view_component/with_content_helper.rb @@ -2,6 +2,7 @@ module ViewComponent module WithContentHelper + # @param value [String] the content to set def with_content(value) raise NilWithContentError if value.nil?