view_component-3.12.1/lib/rails/generators/component/ 0000755 0000041 0000041 00000000000 14624365352 022727 5 ustar www-data www-data view_component-3.12.1/lib/rails/generators/component/component_generator.rb 0000644 0000041 0000041 00000003751 14624365352 027332 0 ustar www-data www-data # frozen_string_literal: true
require "rails/generators/abstract_generator"
module Rails
module Generators
class ComponentGenerator < Rails::Generators::NamedBase
include ViewComponent::AbstractGenerator
source_root File.expand_path("templates", __dir__)
argument :attributes, type: :array, default: [], banner: "attribute"
check_class_collision suffix: "Component"
class_option :inline, type: :boolean, default: false
class_option :locale, type: :boolean, default: ViewComponent::Base.config.generate.locale
class_option :parent, type: :string, desc: "The parent class for the generated component"
class_option :preview, type: :boolean, default: ViewComponent::Base.config.generate.preview
class_option :sidecar, type: :boolean, default: false
class_option :stimulus, type: :boolean,
default: ViewComponent::Base.config.generate.stimulus_controller
def create_component_file
template "component.rb", File.join(component_path, class_path, "#{file_name}_component.rb")
end
hook_for :test_framework
hook_for :preview, type: :boolean
hook_for :stimulus, type: :boolean
hook_for :locale, type: :boolean
hook_for :template_engine do |instance, template_engine|
instance.invoke template_engine, [instance.name]
end
private
def parent_class
return options[:parent] if options[:parent]
ViewComponent::Base.config.component_parent_class || default_parent_class
end
def initialize_signature
return if attributes.blank?
attributes.map { |attr| "#{attr.name}:" }.join(", ")
end
def initialize_body
attributes.map { |attr| "@#{attr.name} = #{attr.name}" }.join("\n ")
end
def initialize_call_method_for_inline?
options["inline"]
end
def default_parent_class
defined?(ApplicationComponent) ? ApplicationComponent : ViewComponent::Base
end
end
end
end
view_component-3.12.1/lib/rails/generators/component/templates/ 0000755 0000041 0000041 00000000000 14624365352 024725 5 ustar www-data www-data view_component-3.12.1/lib/rails/generators/component/templates/component.rb.tt 0000644 0000041 0000041 00000000627 14624365352 027707 0 ustar www-data www-data # frozen_string_literal: true
class <%= class_name %>Component < <%= parent_class %>
<%- if initialize_signature -%>
def initialize(<%= initialize_signature %>)
<%= initialize_body %>
end
<%- end -%>
<%- if initialize_call_method_for_inline? -%>
def call
content_tag :h1, "Hello world!"<%= ", data: { controller: \"#{stimulus_controller}\" }" if options["stimulus"] %>
end
<%- end -%>
end
view_component-3.12.1/lib/rails/generators/component/USAGE 0000644 0000041 0000041 00000000733 14624365352 023521 0 ustar www-data www-data Description:
============
Creates a new component and test.
Pass the component name, either CamelCased or under_scored, and an optional list of attributes as arguments.
Example:
========
bin/rails generate component Profile name age
creates a Profile component and test:
Component: app/components/profile_component.rb
Template: app/components/profile_component.html.erb
Test: test/components/profile_component_test.rb
view_component-3.12.1/lib/rails/generators/haml/ 0000755 0000041 0000041 00000000000 14624365352 021646 5 ustar www-data www-data view_component-3.12.1/lib/rails/generators/haml/component_generator.rb 0000644 0000041 0000041 00000000717 14624365352 026250 0 ustar www-data www-data # frozen_string_literal: true
require "rails/generators/erb/component_generator"
module Haml
module Generators
class ComponentGenerator < Erb::Generators::ComponentGenerator
include ViewComponent::AbstractGenerator
source_root File.expand_path("templates", __dir__)
class_option :sidecar, type: :boolean, default: false
def engine_name
"haml"
end
def copy_view_file
super
end
end
end
end
view_component-3.12.1/lib/rails/generators/haml/templates/ 0000755 0000041 0000041 00000000000 14624365352 023644 5 ustar www-data www-data view_component-3.12.1/lib/rails/generators/haml/templates/component.html.haml.tt 0000644 0000041 0000041 00000000051 14624365352 030076 0 ustar www-data www-data %div Add <%= class_name %> template here
view_component-3.12.1/lib/rails/generators/abstract_generator.rb 0000644 0000041 0000041 00000002104 14624365352 025120 0 ustar www-data www-data # frozen_string_literal: true
module ViewComponent
module AbstractGenerator
def copy_view_file
template "component.html.#{engine_name}", destination unless options["inline"]
end
private
def destination
File.join(destination_directory, "#{destination_file_name}.html.#{engine_name}")
end
def destination_directory
if sidecar?
File.join(component_path, class_path, destination_file_name)
else
File.join(component_path, class_path)
end
end
def destination_file_name
"#{file_name}_component"
end
def file_name
@_file_name ||= super.sub(/_component\z/i, "")
end
def component_path
ViewComponent::Base.config.view_component_path
end
def stimulus_controller
if options["stimulus"]
File.join(destination_directory, destination_file_name)
.sub("#{component_path}/", "")
.tr("_", "-")
.gsub("/", "--")
end
end
def sidecar?
options["sidecar"] || ViewComponent::Base.config.generate.sidecar
end
end
end
view_component-3.12.1/lib/rails/generators/locale/ 0000755 0000041 0000041 00000000000 14624365352 022164 5 ustar www-data www-data view_component-3.12.1/lib/rails/generators/locale/component_generator.rb 0000644 0000041 0000041 00000002653 14624365352 026567 0 ustar www-data www-data # frozen_string_literal: true
require "rails/generators/abstract_generator"
module Locale
module Generators
class ComponentGenerator < ::Rails::Generators::NamedBase
include ViewComponent::AbstractGenerator
source_root File.expand_path("templates", __dir__)
argument :attributes, type: :array, default: [], banner: "attribute"
class_option :sidecar, type: :boolean, default: false
def create_locale_file
if ViewComponent::Base.config.generate.distinct_locale_files
I18n.available_locales.each do |locale|
create_file destination(locale), translations_hash([locale]).to_yaml
end
else
create_file destination, translations_hash(I18n.available_locales).to_yaml
end
end
private
def translations_hash(locales = [:en])
locales.map { |locale| [locale.to_s, translation_keys] }.to_h
end
def translation_keys
keys = attributes.map(&:name)
keys = %w[hello] if keys.empty?
keys.map { |name| [name, name.capitalize] }.to_h
end
def destination(locale = nil)
extension = ".#{locale}" if locale
if sidecar?
File.join(component_path, class_path, "#{file_name}_component", "#{file_name}_component#{extension}.yml")
else
File.join(component_path, class_path, "#{file_name}_component#{extension}.yml")
end
end
end
end
end
view_component-3.12.1/lib/rails/generators/slim/ 0000755 0000041 0000041 00000000000 14624365352 021671 5 ustar www-data www-data view_component-3.12.1/lib/rails/generators/slim/component_generator.rb 0000644 0000041 0000041 00000000717 14624365352 026273 0 ustar www-data www-data # frozen_string_literal: true
require "rails/generators/erb/component_generator"
module Slim
module Generators
class ComponentGenerator < Erb::Generators::ComponentGenerator
include ViewComponent::AbstractGenerator
source_root File.expand_path("templates", __dir__)
class_option :sidecar, type: :boolean, default: false
def engine_name
"slim"
end
def copy_view_file
super
end
end
end
end
view_component-3.12.1/lib/rails/generators/slim/templates/ 0000755 0000041 0000041 00000000000 14624365352 023667 5 ustar www-data www-data view_component-3.12.1/lib/rails/generators/slim/templates/component.html.slim.tt 0000644 0000041 0000041 00000000050 14624365352 030143 0 ustar www-data www-data div Add <%= class_name %> template here
view_component-3.12.1/lib/rails/generators/preview/ 0000755 0000041 0000041 00000000000 14624365352 022406 5 ustar www-data www-data view_component-3.12.1/lib/rails/generators/preview/component_generator.rb 0000644 0000041 0000041 00000002405 14624365352 027004 0 ustar www-data www-data # frozen_string_literal: true
module Preview
module Generators
class ComponentGenerator < ::Rails::Generators::NamedBase
source_root File.expand_path("templates", __dir__)
class_option :preview_path, type: :string, desc: "Path for previews, required when multiple preview paths are configured", default: ViewComponent::Base.config.generate.preview_path
argument :attributes, type: :array, default: [], banner: "attribute"
check_class_collision suffix: "ComponentPreview"
def create_preview_file
preview_paths = ViewComponent::Base.config.preview_paths
optional_path = options[:preview_path]
return if preview_paths.count > 1 && optional_path.blank?
path_prefix = if optional_path.present?
optional_path
else
preview_paths.one? ? preview_paths.first : "test/components/previews"
end
template "component_preview.rb", File.join(path_prefix, class_path, "#{file_name}_component_preview.rb")
end
private
def file_name
@_file_name ||= super.sub(/_component\z/i, "")
end
def render_signature
return if attributes.blank?
attributes.map { |attr| %(#{attr.name}: "#{attr.name}") }.join(", ")
end
end
end
end
view_component-3.12.1/lib/rails/generators/preview/templates/ 0000755 0000041 0000041 00000000000 14624365352 024404 5 ustar www-data www-data view_component-3.12.1/lib/rails/generators/preview/templates/component_preview.rb.tt 0000644 0000041 0000041 00000000325 14624365352 031122 0 ustar www-data www-data # frozen_string_literal: true
class <%= class_name %>ComponentPreview < ViewComponent::Preview
def default
render(<%= class_name %>Component.new<%= "(#{render_signature})" if render_signature %>)
end
end
view_component-3.12.1/lib/rails/generators/erb/ 0000755 0000041 0000041 00000000000 14624365352 021475 5 ustar www-data www-data view_component-3.12.1/lib/rails/generators/erb/component_generator.rb 0000644 0000041 0000041 00000001327 14624365352 026075 0 ustar www-data www-data # frozen_string_literal: true
require "rails/generators/erb"
require "rails/generators/abstract_generator"
module Erb
module Generators
class ComponentGenerator < Base
include ViewComponent::AbstractGenerator
source_root File.expand_path("templates", __dir__)
class_option :sidecar, type: :boolean, default: false
class_option :inline, type: :boolean, default: false
class_option :stimulus, type: :boolean, default: false
def engine_name
"erb"
end
def copy_view_file
super
end
private
def data_attributes
if options["stimulus"]
" data-controller=\"#{stimulus_controller}\""
end
end
end
end
end
view_component-3.12.1/lib/rails/generators/erb/templates/ 0000755 0000041 0000041 00000000000 14624365352 023473 5 ustar www-data www-data view_component-3.12.1/lib/rails/generators/erb/templates/component.html.erb.tt 0000644 0000041 0000041 00000000105 14624365352 027554 0 ustar www-data www-data
>Add <%= class_name %> template here
view_component-3.12.1/lib/rails/generators/stimulus/ 0000755 0000041 0000041 00000000000 14624365352 022612 5 ustar www-data www-data view_component-3.12.1/lib/rails/generators/stimulus/component_generator.rb 0000644 0000041 0000041 00000002105 14624365352 027205 0 ustar www-data www-data # frozen_string_literal: true
require "rails/generators/abstract_generator"
module Stimulus
module Generators
class ComponentGenerator < ::Rails::Generators::NamedBase
include ViewComponent::AbstractGenerator
source_root File.expand_path("templates", __dir__)
class_option :sidecar, type: :boolean, default: false
def create_stimulus_controller
template "component_controller.js", destination
end
def stimulus_module
return "stimulus" if legacy_stimulus?
"@hotwired/stimulus"
end
private
def destination
if sidecar?
File.join(component_path, class_path, "#{file_name}_component", "#{file_name}_component_controller.js")
else
File.join(component_path, class_path, "#{file_name}_component_controller.js")
end
end
def legacy_stimulus?
package_json_pathname = Rails.root.join("package.json")
package_json_pathname.exist? && JSON.parse(package_json_pathname.read).dig("dependencies", "stimulus").present?
end
end
end
end
view_component-3.12.1/lib/rails/generators/stimulus/templates/ 0000755 0000041 0000041 00000000000 14624365352 024610 5 ustar www-data www-data view_component-3.12.1/lib/rails/generators/stimulus/templates/component_controller.js.tt 0000644 0000041 0000041 00000000247 14624365352 032044 0 ustar www-data www-data import { Controller } from "<%= stimulus_module %>";
export default class extends Controller {
connect() {
console.log("Hello, Stimulus!", this.element);
}
}
view_component-3.12.1/lib/rails/generators/test_unit/ 0000755 0000041 0000041 00000000000 14624365352 022743 5 ustar www-data www-data view_component-3.12.1/lib/rails/generators/test_unit/component_generator.rb 0000644 0000041 0000041 00000000774 14624365352 027350 0 ustar www-data www-data # frozen_string_literal: true
module TestUnit
module Generators
class ComponentGenerator < ::Rails::Generators::NamedBase
source_root File.expand_path("templates", __dir__)
check_class_collision suffix: "ComponentTest"
def create_test_file
template "component_test.rb", File.join("test/components", class_path, "#{file_name}_component_test.rb")
end
private
def file_name
@_file_name ||= super.sub(/_component\z/i, "")
end
end
end
end
view_component-3.12.1/lib/rails/generators/test_unit/templates/ 0000755 0000041 0000041 00000000000 14624365352 024741 5 ustar www-data www-data view_component-3.12.1/lib/rails/generators/test_unit/templates/component_test.rb.tt 0000644 0000041 0000041 00000000536 14624365352 030761 0 ustar www-data www-data # frozen_string_literal: true
require "test_helper"
class <%= class_name %>ComponentTest < ViewComponent::TestCase
def test_component_renders_something_useful
# assert_equal(
# %(Hello, components!),
# render_inline(<%= class_name %>Component.new(message: "Hello, components!")).css("span").to_html
# )
end
end
view_component-3.12.1/lib/rails/generators/rspec/ 0000755 0000041 0000041 00000000000 14624365352 022041 5 ustar www-data www-data view_component-3.12.1/lib/rails/generators/rspec/component_generator.rb 0000644 0000041 0000041 00000001641 14624365352 026440 0 ustar www-data www-data # frozen_string_literal: true
require "rails/generators/abstract_generator"
module Rspec
module Generators
class ComponentGenerator < ::Rails::Generators::NamedBase
include ViewComponent::AbstractGenerator
source_root File.expand_path("templates", __dir__)
def create_test_file
template "component_spec.rb", File.join(spec_component_path, class_path, "#{file_name}_component_spec.rb")
end
private
def spec_component_path
return "spec/components" unless ViewComponent::Base.config.generate.use_component_path_for_rspec_tests
configured_component_path = component_path
if configured_component_path.start_with?("app#{File::SEPARATOR}")
_app, *rest_of_path = Pathname.new(configured_component_path).each_filename.to_a
File.join("spec", *rest_of_path)
else
"spec/components"
end
end
end
end
end
view_component-3.12.1/lib/rails/generators/rspec/templates/ 0000755 0000041 0000041 00000000000 14624365352 024037 5 ustar www-data www-data view_component-3.12.1/lib/rails/generators/rspec/templates/component_spec.rb.tt 0000644 0000041 0000041 00000000614 14624365352 030027 0 ustar www-data www-data # frozen_string_literal: true
require "rails_helper"
RSpec.describe <%= class_name %>Component, type: :component do
pending "add some examples to (or delete) #{__FILE__}"
# it "renders something useful" do
# expect(
# render_inline(described_class.new(attr: "value")) { "Hello, components!" }.css("p").to_html
# ).to include(
# "Hello, components!"
# )
# end
end
view_component-3.12.1/lib/view_component/ 0000755 0000041 0000041 00000000000 14624365352 020476 5 ustar www-data www-data view_component-3.12.1/lib/view_component/render_component_to_string_helper.rb 0000644 0000041 0000041 00000000315 14624365352 030012 0 ustar www-data www-data # frozen_string_literal: true
module ViewComponent
module RenderComponentToStringHelper # :nodoc:
def render_component_to_string(component)
component.render_in(view_context)
end
end
end
view_component-3.12.1/lib/view_component/slot.rb 0000644 0000041 0000041 00000007345 14624365352 022015 0 ustar www-data www-data # frozen_string_literal: true
require "view_component/with_content_helper"
module ViewComponent
class Slot
include ViewComponent::WithContentHelper
attr_writer :__vc_component_instance, :__vc_content_block, :__vc_content
def initialize(parent)
@parent = parent
end
def content?
return true if defined?(@__vc_content) && @__vc_content.present?
return true if defined?(@__vc_content_set_by_with_content) && @__vc_content_set_by_with_content.present?
return true if defined?(@__vc_content_block) && @__vc_content_block.present?
return false if !__vc_component_instance?
@__vc_component_instance.content?
end
def with_content(args)
if __vc_component_instance?
@__vc_component_instance.with_content(args)
else
super
end
end
# Used to render the slot content in the template
#
# There's currently 3 different values that may be set, that we can render.
#
# If the slot renderable is a component, the string class name of a
# component, or a function that returns a component, we render that
# component instance, returning the string.
#
# If the slot renderable is a function and returns a string, it's
# set as `@__vc_content` and is returned directly.
#
# If there is no slot renderable, we evaluate the block passed to
# the slot and return it.
def to_s
return @content if defined?(@content)
view_context = @parent.send(:view_context)
if defined?(@__vc_content_block) && defined?(@__vc_content_set_by_with_content)
raise DuplicateSlotContentError.new(self.class.name)
end
@content =
if __vc_component_instance?
@__vc_component_instance.__vc_original_view_context = @parent.__vc_original_view_context
if defined?(@__vc_content_block)
# render_in is faster than `parent.render`
@__vc_component_instance.render_in(view_context) do |*args|
return @__vc_content_block.call(*args) if @__vc_content_block&.source_location.nil?
block_context = @__vc_content_block.binding.receiver
if block_context.class < ActionView::Base
block_context.capture(*args, &@__vc_content_block)
else
@__vc_content_block.call(*args)
end
end
else
@__vc_component_instance.render_in(view_context)
end
elsif defined?(@__vc_content)
@__vc_content
elsif defined?(@__vc_content_block)
view_context.capture(&@__vc_content_block)
elsif defined?(@__vc_content_set_by_with_content)
@__vc_content_set_by_with_content
end
@content = @content.to_s
end
# Allow access to public component methods via the wrapper
#
# for example
#
# calling `header.name` (where `header` is a slot) will call `name`
# on the `HeaderComponent` instance.
#
# Where the component may look like:
#
# class MyComponent < ViewComponent::Base
# has_one :header, HeaderComponent
#
# class HeaderComponent < ViewComponent::Base
# def name
# @name
# end
# end
# end
#
def method_missing(symbol, *args, &block)
@__vc_component_instance.public_send(symbol, *args, &block)
end
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
def html_safe?
# :nocov:
to_s.html_safe?
# :nocov:
end
def respond_to_missing?(symbol, include_all = false)
__vc_component_instance? && @__vc_component_instance.respond_to?(symbol, include_all)
end
private
def __vc_component_instance?
defined?(@__vc_component_instance)
end
end
end
view_component-3.12.1/lib/view_component/deprecation.rb 0000644 0000041 0000041 00000000323 14624365352 023316 0 ustar www-data www-data # frozen_string_literal: true
require "active_support/deprecation"
module ViewComponent
DEPRECATION_HORIZON = "4.0.0"
Deprecation = ActiveSupport::Deprecation.new(DEPRECATION_HORIZON, "ViewComponent")
end
view_component-3.12.1/lib/view_component/system_test_helpers.rb 0000644 0000041 0000041 00000001651 14624365352 025133 0 ustar www-data www-data # frozen_string_literal: true
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.
def with_rendered_component_path(fragment, layout: false, &block)
file = Tempfile.new(
["rendered_#{fragment.class.name}", ".html"],
ViewComponentsSystemTestController.temp_dir
)
begin
file.write(vc_test_controller.render_to_string(html: fragment.to_html.html_safe, layout: layout))
file.rewind
block.call("/_system_test_entrypoint?file=#{file.path.split("/").last}")
ensure
file.unlink
end
end
end
end
view_component-3.12.1/lib/view_component/render_monkey_patch.rb 0000644 0000041 0000041 00000000417 14624365352 025045 0 ustar www-data www-data # frozen_string_literal: true
module ViewComponent
module RenderMonkeyPatch # :nodoc:
def render(options = {}, args = {}, &block)
if options.respond_to?(:render_in)
options.render_in(self, &block)
else
super
end
end
end
end
view_component-3.12.1/lib/view_component/component_error.rb 0000644 0000041 0000041 00000000145 14624365352 024236 0 ustar www-data www-data # frozen_string_literal: true
module ViewComponent
class ComponentError < StandardError
end
end
view_component-3.12.1/lib/view_component/config.rb 0000644 0000041 0000041 00000016167 14624365352 022303 0 ustar www-data www-data # 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: default_generate_options,
preview_controller: "ViewComponentsController",
preview_route: "/rails/view_components",
show_previews_source: false,
instrumentation_enabled: false,
use_deprecated_instrumentation_name: true,
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,
capture_compatibility_patch_enabled: false
})
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
#
# #### #preview_path
#
# Path to generate preview:
#
# config.view_component.generate.preview_path = "test/components/previews"
#
# Required when there is more than one path defined in preview_paths.
# Defaults to `""`. If this is blank, the generator will use
# `ViewComponent.config.preview_paths` if defined,
# `"test/components/previews"` otherwise
#
# #### `#use_component_path_for_rspec_tests`
#
# Whether to use the `config.view_component_path` when generating new
# RSpec component tests:
#
# config.view_component.generate.use_component_path_for_rspec_tests = true
#
# When set to `true`, the generator will use the `view_component_path` to
# decide where to generate the new RSpec component test.
# For example, if the `view_component_path` is
# `app/views/components`, then the generator will create a new spec file
# in `spec/views/components/` rather than the default `spec/components/`.
# @!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 use_deprecated_instrumentation_name
# @return [Boolean]
# Whether ActiveSupport Notifications use the private name `"!render.view_component"`
# or are made more publicly available via `"render.view_component"`.
# Will default to `false` in next major version.
# Defaults to `true`.
# @!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]
# The locations in which component previews will be looked up.
# Defaults to `['test/component/previews']` relative to your Rails root.
# @!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.
# @!attribute capture_compatibility_patch_enabled
# @return [Boolean]
# Enables the experimental capture compatibility patch that makes ViewComponent
# compatible with forms, capture, and other built-ins.
# previews.
# Defaults to `false`.
def default_preview_paths
return [] unless defined?(Rails.root) && Dir.exist?("#{Rails.root}/test/components/previews")
["#{Rails.root}/test/components/previews"]
end
def default_generate_options
options = ActiveSupport::OrderedOptions.new(false)
options.preview_path = ""
options
end
end
# @!attribute current
# @return [ViewComponent::Config]
# Returns the current ViewComponent::Config. This is persisted against this
# class so that config options remain accessible before the rest of
# ViewComponent has loaded. Defaults to an instance of ViewComponent::Config
# with all other documented defaults set.
class_attribute :current, default: defaults, instance_predicate: false
def initialize
@config = self.class.defaults
end
delegate_missing_to :config
private
attr_reader :config
end
end
view_component-3.12.1/lib/view_component/docs_builder_component.rb 0000644 0000041 0000041 00000004131 14624365352 025542 0 ustar www-data www-data # frozen_string_literal: true
module ViewComponent
class DocsBuilderComponent < Base
class Section < Struct.new(:heading, :methods, :error_klasses, :show_types, keyword_init: true)
def initialize(heading: nil, methods: [], error_klasses: [], show_types: true)
methods.sort_by! { |method| method[:name] }
error_klasses.sort!
super
end
end
class ErrorKlassDoc < ViewComponent::Base
def initialize(error_klass, _show_types)
@error_klass = error_klass
end
def klass_name
@error_klass.gsub("ViewComponent::", "").gsub("::MESSAGE", "")
end
def error_message
ViewComponent.const_get(@error_klass)
end
def call
<<~DOCS.chomp
`#{klass_name}`
#{error_message}
DOCS
end
end
class MethodDoc < ViewComponent::Base
def initialize(method, show_types = true)
@method = method
@show_types = show_types
end
def deprecated?
@method.tag(:deprecated).present?
end
def suffix
" (Deprecated)" if deprecated?
end
def types
" → [#{@method.tag(:return).types.join(",")}]" if @method.tag(:return)&.types && @show_types
end
def signature_or_name
@method.signature ? @method.signature.gsub("def ", "") : @method.name
end
def separator
@method.sep
end
def docstring
@method.docstring
end
def deprecation_text
@method.tag(:deprecated)&.text
end
def docstring_and_deprecation_text
<<~DOCS.strip
#{docstring}
#{"_#{deprecation_text}_" if deprecated?}
DOCS
end
def call
<<~DOCS.chomp
`#{separator}#{signature_or_name}`#{types}#{suffix}
#{docstring_and_deprecation_text}
DOCS
end
end
# { heading: String, public_only: Boolean, show_types: Boolean}
def initialize(sections: [])
@sections = sections
end
# deprecation
# return
# only public methods
# sig with types or name
end
end
view_component-3.12.1/lib/view_component/engine.rb 0000644 0000041 0000041 00000015106 14624365352 022273 0 ustar www-data www-data # frozen_string_literal: true
require "rails"
require "view_component/config"
require "view_component/deprecation"
module ViewComponent
class Engine < Rails::Engine # :nodoc:
config.view_component = ViewComponent::Config.current
rake_tasks do
load "view_component/rails/tasks/view_component.rake"
end
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?
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.show_previews_source
require "method_source"
app.config.to_prepare do
MethodSource.instance_variable_set(:@lines_for_file, {})
end
end
end
end
initializer "view_component.enable_instrumentation" do |app|
ActiveSupport.on_load(:view_component) do
if app.config.view_component.instrumentation_enabled.present?
# :nocov: Re-executing the below in tests duplicates initializers and causes order-dependent failures.
ViewComponent::Base.prepend(ViewComponent::Instrumentation)
if app.config.view_component.use_deprecated_instrumentation_name
ViewComponent::Deprecation.deprecation_warning(
"!render.view_component",
"Use the new instrumentation key `render.view_component` instead. See https://viewcomponent.org/guide/instrumentation.html"
)
end
# :nocov:
end
end
end
# :nocov:
initializer "view_component.enable_capture_patch" do |app|
ActiveSupport.on_load(:view_component) do
ActionView::Base.include(ViewComponent::CaptureCompatibility) if app.config.view_component.capture_compatibility_patch_enabled
end
end
# :nocov:
initializer "view_component.set_autoload_paths" do |app|
options = app.config.view_component
if options.show_previews && !options.preview_paths.empty?
paths_to_add = options.preview_paths - ActiveSupport::Dependencies.autoload_paths
ActiveSupport::Dependencies.autoload_paths.concat(paths_to_add) if paths_to_add.any?
end
end
initializer "view_component.eager_load_actions" do
ActiveSupport.on_load(:after_initialize) do
ViewComponent::Base.descendants.each(&:compile) if Rails.application.config.eager_load
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
# :nocov:
ViewComponent::Deprecation.deprecation_warning("Monkey patching `render`", "ViewComponent 4.0 will remove the `render` monkey patch")
ActiveSupport.on_load(:action_view) do
require "view_component/render_monkey_patch"
ActionView::Base.prepend ViewComponent::RenderMonkeyPatch
end
ActiveSupport.on_load(:action_controller) do
require "view_component/rendering_monkey_patch"
require "view_component/render_to_string_monkey_patch"
ActionController::Base.prepend ViewComponent::RenderingMonkeyPatch
ActionController::Base.prepend ViewComponent::RenderToStringMonkeyPatch
end
# :nocov:
end
initializer "view_component.include_render_component" do |_app|
next if Rails.version.to_f >= 6.1
# :nocov:
ViewComponent::Deprecation.deprecation_warning("using `render_component`", "ViewComponent 4.0 will remove `render_component`")
ActiveSupport.on_load(:action_view) do
require "view_component/render_component_helper"
ActionView::Base.include ViewComponent::RenderComponentHelper
end
ActiveSupport.on_load(:action_controller) do
require "view_component/rendering_component_helper"
require "view_component/render_component_to_string_helper"
ActionController::Base.include ViewComponent::RenderingComponentHelper
ActionController::Base.include ViewComponent::RenderComponentToStringHelper
end
# :nocov:
end
initializer "static assets" do |app|
if serve_static_preview_assets?(app.config)
app.middleware.use(::ActionDispatch::Static, "#{root}/app/assets/vendor")
end
end
def serve_static_preview_assets?(app_config)
app_config.view_component.show_previews && app_config.public_file_server.enabled
end
initializer "compiler mode" do |_app|
ViewComponent::Compiler.mode = if Rails.env.development? || Rails.env.test?
ViewComponent::Compiler::DEVELOPMENT_MODE
else
ViewComponent::Compiler::PRODUCTION_MODE
end
end
config.after_initialize do |app|
options = app.config.view_component
if options.show_previews
app.routes.prepend do
preview_controller = options.preview_controller.sub(/Controller$/, "").underscore
get(
options.preview_route,
to: "#{preview_controller}#index",
as: :preview_view_components,
internal: true
)
get(
"#{options.preview_route}/*path",
to: "#{preview_controller}#previews",
as: :preview_view_component,
internal: true
)
end
end
if Rails.env.test?
app.routes.prepend do
get("_system_test_entrypoint", to: "view_components_system_test#system_test_entrypoint")
end
end
# :nocov:
if RUBY_VERSION < "3.0.0"
ViewComponent::Deprecation.deprecation_warning("Support for Ruby versions < 3.0.0", "ViewComponent 4.0 will remove support for Ruby versions < 3.0.0 ")
end
if Rails.version.to_f < 6.1
ViewComponent::Deprecation.deprecation_warning("Support for Rails versions < 6.1", "ViewComponent 4.0 will remove support for Rails versions < 6.1 ")
end
# :nocov:
app.executor.to_run :before do
CompileCache.invalidate! unless ActionView::Base.cache_template_loading
end
end
end
end
view_component-3.12.1/lib/view_component/render_component_helper.rb 0000644 0000041 0000041 00000000447 14624365352 025730 0 ustar www-data www-data # frozen_string_literal: true
module ViewComponent
module RenderComponentHelper # :nodoc:
def render_component(component, &block)
component.set_original_view_context(__vc_original_view_context) if is_a?(ViewComponent::Base)
component.render_in(self, &block)
end
end
end
view_component-3.12.1/lib/view_component/system_test_case.rb 0000644 0000041 0000041 00000000374 14624365352 024405 0 ustar www-data www-data # frozen_string_literal: true
require "active_support/test_case"
module ViewComponent
class SystemTestCase < ActionDispatch::SystemTestCase
include ViewComponent::SystemTestHelpers
def page
Capybara.current_session
end
end
end
view_component-3.12.1/lib/view_component/use_helpers.rb 0000644 0000041 0000041 00000001045 14624365352 023341 0 ustar www-data www-data # frozen_string_literal: true
module ViewComponent::UseHelpers
extend ActiveSupport::Concern
class_methods do
def use_helpers(*args)
args.each do |helper_method|
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
def #{helper_method}(*args, &block)
raise HelpersCalledBeforeRenderError if view_context.nil?
__vc_original_view_context.#{helper_method}(*args, &block)
end
RUBY
ruby2_keywords(helper_method) if respond_to?(:ruby2_keywords, true)
end
end
end
end
view_component-3.12.1/lib/view_component/docs_builder_component.html.erb 0000644 0000041 0000041 00000001043 14624365352 026651 0 ustar www-data www-data ---
layout: default
title: API reference
nav_order: 3
---
# API
<% @sections.each do |section| %>
## <%= section.heading %>
<% section.methods.each do |method| %>
### <%== render ViewComponent::DocsBuilderComponent::MethodDoc.new(method, section.show_types) %>
<% end %>
<% section.error_klasses.each do |error_klass| %>
### <%== render ViewComponent::DocsBuilderComponent::ErrorKlassDoc.new(error_klass, section.show_types) %>
<% end %>
<% end %>
view_component-3.12.1/lib/view_component/rendering_monkey_patch.rb 0000644 0000041 0000041 00000000437 14624365352 025545 0 ustar www-data www-data # frozen_string_literal: true
module ViewComponent
module RenderingMonkeyPatch # :nodoc:
def render(options = {}, args = {})
if options.respond_to?(:render_in)
self.response_body = options.render_in(view_context)
else
super
end
end
end
end
view_component-3.12.1/lib/view_component/base.rb 0000644 0000041 0000041 00000056174 14624365352 021752 0 ustar www-data www-data # frozen_string_literal: true
require "action_view"
require "active_support/configurable"
require "view_component/collection"
require "view_component/compile_cache"
require "view_component/compiler"
require "view_component/config"
require "view_component/errors"
require "view_component/inline_template"
require "view_component/preview"
require "view_component/slotable"
require "view_component/translatable"
require "view_component/with_content_helper"
require "view_component/use_helpers"
module ViewComponent
class Base < ActionView::Base
class << self
delegate(*ViewComponent::Config.defaults.keys, to: :config)
# Returns the current config.
#
# @return [ActiveSupport::OrderedOptions]
def config
ViewComponent::Config.current
end
end
include ViewComponent::InlineTemplate
include ViewComponent::UseHelpers
include ViewComponent::Slotable
include ViewComponent::Translatable
include ViewComponent::WithContentHelper
RESERVED_PARAMETER = :content
# For CSRF authenticity tokens in forms
delegate :form_authenticity_token, :protect_against_forgery?, :config, to: :helpers
# For Content Security Policy nonces
delegate :content_security_policy_nonce, to: :helpers
# Config option that strips trailing whitespace in templates before compiling them.
class_attribute :__vc_strip_trailing_whitespace, instance_accessor: false, instance_predicate: false
self.__vc_strip_trailing_whitespace = false # class_attribute:default doesn't work until Rails 5.2
attr_accessor :__vc_original_view_context
# Components render in their own view context. Helpers and other functionality
# require a reference to the original Rails view context, an instance of
# `ActionView::Base`. Use this method to set a reference to the original
# view context. Objects that implement this method will render in the component's
# view context, while objects that don't will render in the original view context
# so helpers, etc work as expected.
#
# @param view_context [ActionView::Base] The original view context.
# @return [void]
def set_original_view_context(view_context)
self.__vc_original_view_context = view_context
end
# Entrypoint for rendering components.
#
# - `view_context`: ActionView context from calling view
# - `block`: optional block to be captured within the view context
#
# Returns HTML that has been escaped by the respective template handler.
#
# @return [String]
def render_in(view_context, &block)
self.class.compile(raise_errors: true)
@view_context = view_context
self.__vc_original_view_context ||= view_context
@output_buffer = ActionView::OutputBuffer.new
@lookup_context ||= view_context.lookup_context
# required for path helpers in older Rails versions
@view_renderer ||= view_context.view_renderer
# For content_for
@view_flow ||= view_context.view_flow
# For i18n
@virtual_path ||= virtual_path
# For template variants (+phone, +desktop, etc.)
@__vc_variant ||= @lookup_context.variants.first
# For caching, such as #cache_if
@current_template = nil unless defined?(@current_template)
old_current_template = @current_template
@current_template = self
if block && defined?(@__vc_content_set_by_with_content)
raise DuplicateContentError.new(self.class.name)
end
@__vc_content_evaluated = false
@__vc_render_in_block = block
before_render
if render?
# Avoid allocating new string when output_preamble and output_postamble are blank
rendered_template = safe_render_template_for(@__vc_variant).to_s
if output_preamble.blank? && output_postamble.blank?
rendered_template
else
safe_output_preamble + rendered_template + safe_output_postamble
end
else
""
end
ensure
@current_template = old_current_template
end
# Subclass components that call `super` inside their template code will cause a
# double render if they emit the result.
#
# ```erb
# <%= super %> # double-renders
# <% super %> # doesn't double-render
# ```
#
# `super` also doesn't consider the current variant. `render_parent` renders the
# parent template considering the current variant and emits the result without
# double-rendering.
def render_parent
render_parent_to_string
nil
end
# Renders the parent component to a string and returns it. This method is meant
# to be used inside custom #call methods when a string result is desired, eg.
#
# ```ruby
# def call
# "
#{render_parent_to_string}
"
# end
# ```
#
# When rendering the parent inside an .erb template, use `#render_parent` instead.
def render_parent_to_string
@__vc_parent_render_level ||= 0 # ensure a good starting value
begin
target_render = self.class.instance_variable_get(:@__vc_ancestor_calls)[@__vc_parent_render_level]
@__vc_parent_render_level += 1
target_render.bind_call(self, @__vc_variant)
ensure
@__vc_parent_render_level -= 1
end
end
# Optional content to be returned before the rendered template.
#
# @return [String]
def output_preamble
@@default_output_preamble ||= "".html_safe
end
# Optional content to be returned after the rendered template.
#
# @return [String]
def output_postamble
@@default_output_postamble ||= "".html_safe
end
# Called before rendering the component. Override to perform operations that
# depend on having access to the view context, such as helpers.
#
# @return [void]
def before_render
# noop
end
# Override to determine whether the ViewComponent should render.
#
# @return [Boolean]
def render?
true
end
# @private
def initialize(*)
end
# Re-use original view_context if we're not rendering a component.
#
# This prevents an exception when rendering a partial inside of a component that has also been rendered outside
# of the component. This is due to the partials compiled template method existing in the parent `view_context`,
# and not the component's `view_context`.
#
# @private
def render(options = {}, args = {}, &block)
if options.respond_to?(:set_original_view_context)
options.set_original_view_context(self.__vc_original_view_context)
super
else
__vc_original_view_context.render(options, args, &block)
end
end
# The current controller. Use sparingly as doing so introduces coupling
# that inhibits encapsulation & reuse, often making testing difficult.
#
# @return [ActionController::Base]
def controller
raise ControllerCalledBeforeRenderError if view_context.nil?
@__vc_controller ||= view_context.controller
end
# A proxy through which to access helpers. Use sparingly as doing so introduces
# coupling that inhibits encapsulation & reuse, often making testing difficult.
#
# @return [ActionView::Base]
def helpers
raise HelpersCalledBeforeRenderError if view_context.nil?
# Attempt to re-use the original view_context passed to the first
# component rendered in the rendering pipeline. This prevents the
# instantiation of a new view_context via `controller.view_context` which
# always returns a new instance of the view context class.
#
# This allows ivars to remain persisted when using the same helper via
# `helpers` across multiple components and partials.
@__vc_helpers ||= __vc_original_view_context || controller.view_context
end
if ::Rails.env.development? || ::Rails.env.test?
# @private
def method_missing(method_name, *args) # rubocop:disable Style/MissingRespondToMissing
super
rescue => e # rubocop:disable Style/RescueStandardError
e.set_backtrace e.backtrace.tap(&:shift)
raise e, <<~MESSAGE.chomp if view_context && e.is_a?(NameError) && helpers.respond_to?(method_name)
#{e.message}
You may be trying to call a method provided as a view helper. Did you mean `helpers.#{method_name}'?
MESSAGE
raise
end
end
# Exposes .virtual_path as an instance method
#
# @private
def virtual_path
self.class.virtual_path
end
# For caching, such as #cache_if
# @private
def view_cache_dependencies
[]
end
# For caching, such as #cache_if
#
# @private
def format
@__vc_variant if defined?(@__vc_variant)
end
# The current request. Use sparingly as doing so introduces coupling that
# inhibits encapsulation & reuse, often making testing difficult.
#
# @return [ActionDispatch::Request]
def request
@request ||= controller.request if controller.respond_to?(:request)
end
# The content passed to the component instance as a block.
#
# @return [String]
def content
@__vc_content_evaluated = true
return @__vc_content if defined?(@__vc_content)
@__vc_content =
if __vc_render_in_block_provided?
view_context.capture(self, &@__vc_render_in_block)
elsif __vc_content_set_by_with_content_defined?
@__vc_content_set_by_with_content
end
end
# Whether `content` has been passed to the component.
#
# @return [Boolean]
def content?
__vc_render_in_block_provided? || __vc_content_set_by_with_content_defined?
end
private
attr_reader :view_context
def __vc_render_in_block_provided?
defined?(@view_context) && @view_context && @__vc_render_in_block
end
def __vc_content_set_by_with_content_defined?
defined?(@__vc_content_set_by_with_content)
end
def content_evaluated?
defined?(@__vc_content_evaluated) && @__vc_content_evaluated
end
def maybe_escape_html(text)
return text if request && !request.format.html?
return text if text.blank?
if text.html_safe?
text
else
yield
html_escape(text)
end
end
def safe_render_template_for(variant)
if compiler.renders_template_for_variant?(variant)
render_template_for(variant)
else
maybe_escape_html(render_template_for(variant)) do
Kernel.warn("WARNING: The #{self.class} component rendered HTML-unsafe output. The output will be automatically escaped, but you may want to investigate.")
end
end
end
def safe_output_preamble
maybe_escape_html(output_preamble) do
Kernel.warn("WARNING: The #{self.class} component was provided an HTML-unsafe preamble. The preamble will be automatically escaped, but you may want to investigate.")
end
end
def safe_output_postamble
maybe_escape_html(output_postamble) do
Kernel.warn("WARNING: The #{self.class} component was provided an HTML-unsafe postamble. The postamble will be automatically escaped, but you may want to investigate.")
end
end
def compiler
@compiler ||= self.class.compiler
end
# Set the controller used for testing components:
#
# ```ruby
# config.view_component.test_controller = "MyTestController"
# ```
#
# Defaults to `nil`. If this is falsy, `"ApplicationController"` is used. Can also be
# configured on a per-test basis using `with_controller_class`.
#
# Set if render monkey patches should be included or not in Rails <6.1:
#
# ```ruby
# config.view_component.render_monkey_patch_enabled = false
# ```
#
# Path for component files
#
# ```ruby
# config.view_component.view_component_path = "app/my_components"
# ```
#
# Defaults to `nil`. If this is falsy, `app/components` is used.
#
# Parent class for generated components
#
# ```ruby
# config.view_component.component_parent_class = "MyBaseComponent"
# ```
#
# Defaults to nil. If this is falsy, generators will use
# "ApplicationComponent" if defined, "ViewComponent::Base" otherwise.
#
# Configuration for generators.
#
# All options under this namespace default to `false` unless otherwise
# stated.
#
# #### #sidecar
#
# Always generate a component with a sidecar directory:
#
# ```ruby
# config.view_component.generate.sidecar = true
# ```
#
# #### #stimulus_controller
#
# Always generate a Stimulus controller alongside the component:
#
# ```ruby
# config.view_component.generate.stimulus_controller = true
# ```
#
# #### #locale
#
# Always generate translations file alongside the component:
#
# ```ruby
# config.view_component.generate.locale = true
# ```
#
# #### #distinct_locale_files
#
# Always generate as many translations files as available locales:
#
# ```ruby
# 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 preview alongside the component:
#
# ```ruby
# config.view_component.generate.preview = true
# ```
#
# Defaults to `false`.
class << self
# @private
attr_accessor :source_location, :virtual_path
# Find sidecar files for the given extensions.
#
# The provided array of extensions is expected to contain
# strings starting without the dot, example: `["erb", "haml"]`.
#
# For example, one might collect sidecar CSS files that need to be compiled.
# @param extensions [Array] Extensions of which to return matching sidecar files.
def sidecar_files(extensions)
return [] unless source_location
extensions = extensions.join(",")
# view files in a directory named like the component
directory = File.dirname(source_location)
filename = File.basename(source_location, ".rb")
component_name = name.demodulize.underscore
# Add support for nested components defined in the same file.
#
# for example
#
# class MyComponent < ViewComponent::Base
# class MyOtherComponent < ViewComponent::Base
# end
# end
#
# Without this, `MyOtherComponent` will not look for `my_component/my_other_component.html.erb`
nested_component_files =
if name.include?("::") && component_name != filename
Dir["#{directory}/#{filename}/#{component_name}.*{#{extensions}}"]
else
[]
end
# view files in the same directory as the component
sidecar_files = Dir["#{directory}/#{component_name}.*{#{extensions}}"]
sidecar_directory_files = Dir["#{directory}/#{component_name}/#{filename}.*{#{extensions}}"]
(sidecar_files - [source_location] + sidecar_directory_files + nested_component_files).uniq
end
# Render a component for each element in a collection ([documentation](/guide/collections)):
#
# ```ruby
# 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
compile
# Give the child its own personal #render_template_for to protect against the case when
# eager loading is disabled and the parent component is rendered before the child. In
# such a scenario, the parent will override ViewComponent::Base#render_template_for,
# meaning it will not be called for any children and thus not compile their templates.
if !child.instance_methods(false).include?(:render_template_for) && !child.compiled?
child.class_eval <<~RUBY, __FILE__, __LINE__ + 1
def render_template_for(variant = nil)
# Force compilation here so the compiler always redefines render_template_for.
# This is mostly a safeguard to prevent infinite recursion.
self.class.compile(raise_errors: true, force: true)
# .compile replaces this method; call the new one
render_template_for(variant)
end
RUBY
end
# 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 < 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.
# We need to ignore `inherited` frames here as they indicate that `inherited`
# has been re-defined by the consuming application, likely in ApplicationComponent.
# We use `base_label` method here instead of `label` to avoid cases where the method
# owner is included in a prefix like `ApplicationComponent.inherited`.
child.source_location = caller_locations(1, 10).reject { |l| l.base_label == "inherited" }[0].path
# If Rails application is loaded, removes the first part of the path and the extension.
if defined?(Rails) && Rails.application
child.virtual_path = child.source_location.gsub(
/(.*#{Regexp.quote(ViewComponent::Base.config.view_component_path)})|(\.rb)/, ""
)
end
# Set collection parameter to the extended component
child.with_collection_parameter provided_collection_parameter
if instance_methods(false).include?(:render_template_for)
vc_ancestor_calls = defined?(@__vc_ancestor_calls) ? @__vc_ancestor_calls.dup : []
vc_ancestor_calls.unshift(instance_method(:render_template_for))
child.instance_variable_set(:@__vc_ancestor_calls, vc_ancestor_calls)
end
super
end
# @private
def compiled?
compiler.compiled?
end
# @private
def ensure_compiled
compile unless compiled?
end
# Compile templates to instance methods, assuming they haven't been compiled already.
#
# 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, force: false)
compiler.compile(raise_errors: raise_errors, force: force)
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
# Set the parameter name used when rendering elements of a collection ([documentation](/guide/collections)):
#
# ```ruby
# 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
@initialize_parameters = nil
end
# Strips trailing whitespace from templates before compiling them.
#
# ```ruby
# class MyComponent < ViewComponent::Base
# strip_trailing_whitespace
# end
# ```
#
# @param value [Boolean] Whether to strip newlines.
def strip_trailing_whitespace(value = true)
self.__vc_strip_trailing_whitespace = value
end
# Whether trailing whitespace will be stripped before compilation.
#
# @return [Boolean]
def strip_trailing_whitespace?
__vc_strip_trailing_whitespace
end
# Ensure the component initializer accepts the
# collection parameter. By default, we don't
# 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
return unless parameter
return if initialize_parameter_names.include?(parameter) || splatted_keyword_argument_present?
# If Ruby can't parse the component class, then the initialize
# parameters will be empty and ViewComponent will not be able to render
# the component.
if initialize_parameters.empty?
raise EmptyOrInvalidInitializerError.new(name, parameter)
end
raise MissingCollectionArgumentError.new(name, parameter)
end
# Ensure the component initializer doesn't 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)
raise ReservedParameterError.new(name, RESERVED_PARAMETER)
end
# @private
def collection_parameter
provided_collection_parameter || name && name.demodulize.underscore.chomp("_component").to_sym
end
# @private
def collection_counter_parameter
:"#{collection_parameter}_counter"
end
# @private
def counter_argument_present?
initialize_parameter_names.include?(collection_counter_parameter)
end
# @private
def collection_iteration_parameter
:"#{collection_parameter}_iteration"
end
# @private
def iteration_argument_present?
initialize_parameter_names.include?(collection_iteration_parameter)
end
private
def splatted_keyword_argument_present?
initialize_parameters.flatten.include?(:keyrest) &&
!initialize_parameters.include?([:keyrest, :**]) # Un-named splatted keyword args don't count!
end
def initialize_parameter_names
return attribute_names.map(&:to_sym) if respond_to?(:attribute_names)
return attribute_types.keys.map(&:to_sym) if Rails::VERSION::MAJOR <= 5 && respond_to?(:attribute_types)
initialize_parameters.map(&:last)
end
def initialize_parameters
@initialize_parameters ||= instance_method(:initialize).parameters
end
def provided_collection_parameter
@provided_collection_parameter ||= nil
end
end
ActiveSupport.run_load_hooks(:view_component, self)
end
end
view_component-3.12.1/lib/view_component/compile_cache.rb 0000644 0000041 0000041 00000001030 14624365352 023570 0 ustar www-data www-data # frozen_string_literal: true
module ViewComponent
# Keeps track of which templates have already been compiled
# This isn't part of the public API
module CompileCache
mattr_accessor :cache, instance_reader: false, instance_accessor: false do
Set.new
end
module_function
def register(klass)
cache << klass
end
def compiled?(klass)
cache.include? klass
end
def invalidate_class!(klass)
cache.delete(klass)
end
def invalidate!
cache.clear
end
end
end
view_component-3.12.1/lib/view_component/preview.rb 0000644 0000041 0000041 00000006755 14624365352 022521 0 ustar www-data www-data # frozen_string_literal: true
require "active_support/descendants_tracker"
module ViewComponent # :nodoc:
class Preview
if defined?(Rails.application.routes.url_helpers)
# Workaround from https://stackoverflow.com/questions/20853526/make-yard-ignore-certain-class-extensions to appease YARD
send(:include, Rails.application.routes.url_helpers)
end
include ActionView::Helpers::TagHelper
include ActionView::Helpers::AssetTagHelper
extend ActiveSupport::DescendantsTracker
def render(component, **args, &block)
{
args: args,
block: block,
component: component,
locals: {},
template: "view_components/preview"
}
end
def render_with_template(template: nil, locals: {})
{
template: template,
locals: locals
}
end
alias_method :render_component, :render
class << self
# Returns all component preview classes.
def all
load_previews
descendants
end
# Returns the arguments for rendering of the component in its layout
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
result = provided_params.empty? ? new.public_send(example) : new.public_send(example, **provided_params)
result ||= {}
result[:template] = preview_example_template_path(example) if result[:template].nil?
@layout = nil unless defined?(@layout)
result.merge(layout: @layout)
end
# Returns all of the available examples for the component preview.
def examples
public_instance_methods(false).map(&:to_s).sort
end
# Returns +true+ if the preview exists.
def exists?(preview)
all.any? { |p| p.preview_name == preview }
end
# Find a component preview by its underscored class name.
def find(preview)
all.find { |p| p.preview_name == preview }
end
# Returns the underscored name of the component preview without the suffix.
def preview_name
name.chomp("Preview").underscore
end
# rubocop:disable Style/TrivialAccessors
# Setter for 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
def preview_example_template_path(example)
preview_path =
Array(preview_paths).detect do |path|
Dir["#{path}/#{preview_name}_preview/#{example}.html.*"].first
end
raise MissingPreviewTemplateError.new(example) if preview_path.nil?
path = Dir["#{preview_path}/#{preview_name}_preview/#{example}.html.*"].first
Pathname.new(path)
.relative_path_from(Pathname.new(preview_path))
.to_s
.sub(/\..*$/, "")
end
# Returns the method body for the example from the preview file.
def preview_source(example)
source = instance_method(example.to_sym).source.split("\n")
source[1...(source.size - 1)].join("\n")
end
def load_previews
Array(preview_paths).each do |preview_path|
Dir["#{preview_path}/**/*_preview.rb"].sort.each { |file| require_dependency file }
end
end
private
def preview_paths
Base.preview_paths
end
end
end
end
view_component-3.12.1/lib/view_component/test_case.rb 0000644 0000041 0000041 00000000264 14624365352 022777 0 ustar www-data www-data # frozen_string_literal: true
require "active_support/test_case"
module ViewComponent
class TestCase < ActiveSupport::TestCase
include ViewComponent::TestHelpers
end
end
view_component-3.12.1/lib/view_component/rendering_component_helper.rb 0000644 0000041 0000041 00000000323 14624365352 026417 0 ustar www-data www-data # frozen_string_literal: true
module ViewComponent
module RenderingComponentHelper # :nodoc:
def render_component(component)
self.response_body = component.render_in(view_context)
end
end
end
view_component-3.12.1/lib/view_component/translatable.rb 0000644 0000041 0000041 00000010410 14624365352 023473 0 ustar www-data www-data # frozen_string_literal: true
require "erb"
require "set"
require "i18n"
require "active_support/concern"
module ViewComponent
module Translatable
extend ActiveSupport::Concern
HTML_SAFE_TRANSLATION_KEY = /(?:_|\b)html\z/
TRANSLATION_EXTENSIONS = %w[yml yaml].freeze
included do
class_attribute :i18n_backend, instance_writer: false, instance_predicate: false
end
class_methods do
def i18n_scope
@i18n_scope ||= virtual_path.sub(%r{^/}, "").gsub(%r{/_?}, ".")
end
def build_i18n_backend
return if compiled?
# We need to load the translations files from the ancestors so a component
# can inherit translations from its parent and is able to overwrite them.
translation_files = ancestors.reverse_each.with_object([]) do |ancestor, files|
if ancestor.is_a?(Class) && ancestor < ViewComponent::Base
files.concat(ancestor.sidecar_files(TRANSLATION_EXTENSIONS))
end
end
# In development it will become nil if the translations file is removed
self.i18n_backend = if translation_files.any?
I18nBackend.new(
i18n_scope: i18n_scope,
load_paths: translation_files
)
end
end
def i18n_key(key, scope = nil)
scope = scope.join(".") if scope.is_a? Array
key = key&.to_s unless key.is_a?(String)
key = "#{scope}.#{key}" if scope
key = "#{i18n_scope}#{key}" if key.start_with?(".")
key
end
def translate(key = nil, **options)
return key.map { |k| translate(k, **options) } if key.is_a?(Array)
ensure_compiled
locale = options.delete(:locale) || ::I18n.locale
key = i18n_key(key, options.delete(:scope))
i18n_backend.translate(locale, key, options)
end
alias_method :t, :translate
end
class I18nBackend < ::I18n::Backend::Simple
EMPTY_HASH = {}.freeze
def initialize(i18n_scope:, load_paths:)
@i18n_scope = i18n_scope.split(".").map(&:to_sym)
@load_paths = load_paths
end
# Ensure the Simple backend won't load paths from ::I18n.load_path
def load_translations
super(@load_paths)
end
def scope_data(data)
@i18n_scope.reverse_each do |part|
data = {part => data}
end
data
end
def store_translations(locale, data, options = EMPTY_HASH)
super(locale, scope_data(data), options)
end
end
def translate(key = nil, **options)
raise ViewComponent::TranslateCalledBeforeRenderError if view_context.nil?
return super unless i18n_backend
return key.map { |k| translate(k, **options) } if key.is_a?(Array)
locale = options.delete(:locale) || ::I18n.locale
key = self.class.i18n_key(key, options.delete(:scope))
as_html = HTML_SAFE_TRANSLATION_KEY.match?(key)
html_escape_translation_options!(options) if as_html
if key.start_with?(i18n_scope + ".")
translated =
catch(:exception) do
i18n_backend.translate(locale, key, options)
end
# Fallback to the global translations
if translated.is_a? ::I18n::MissingTranslation
return super(key, locale: locale, **options)
end
translated = html_safe_translation(translated) if as_html
translated
else
super(key, locale: locale, **options)
end
end
alias_method :t, :translate
# Exposes .i18n_scope as an instance method
def i18n_scope
self.class.i18n_scope
end
private
def html_safe_translation(translation)
if translation.respond_to?(:map)
translation.map { |element| html_safe_translation(element) }
else
# It's assumed here that objects loaded by the i18n backend will respond to `#html_safe?`.
# It's reasonable that if we're in Rails, `active_support/core_ext/string/output_safety.rb`
# will provide this to `Object`.
translation.html_safe
end
end
def html_escape_translation_options!(options)
options.except(*::I18n::RESERVED_KEYS).each do |name, value|
next if name == :count && value.is_a?(Numeric)
options[name] = ERB::Util.html_escape(value.to_s)
end
end
end
end
view_component-3.12.1/lib/view_component/render_to_string_monkey_patch.rb 0000644 0000041 0000041 00000000431 14624365352 027131 0 ustar www-data www-data # frozen_string_literal: true
module ViewComponent
module RenderToStringMonkeyPatch # :nodoc:
def render_to_string(options = {}, args = {})
if options.respond_to?(:render_in)
options.render_in(view_context)
else
super
end
end
end
end
view_component-3.12.1/lib/view_component/compiler.rb 0000644 0000041 0000041 00000023527 14624365352 022646 0 ustar www-data www-data # frozen_string_literal: true
require "concurrent-ruby"
module ViewComponent
class Compiler
# Compiler mode. Can be either:
# * development (a blocking mode which ensures thread safety when redefining the `call` method for components,
# default in Rails development and test mode)
# * production (a non-blocking mode, default in Rails production mode)
DEVELOPMENT_MODE = :development
PRODUCTION_MODE = :production
class_attribute :mode, default: PRODUCTION_MODE
def initialize(component_class)
@component_class = component_class
@redefinition_lock = Mutex.new
@variants_rendering_templates = Set.new
end
def compiled?
CompileCache.compiled?(component_class)
end
def development?
self.class.mode == DEVELOPMENT_MODE
end
def compile(raise_errors: false, force: false)
return if compiled? && !force
return if component_class == ViewComponent::Base
component_class.superclass.compile(raise_errors: raise_errors) if should_compile_superclass?
if template_errors.present?
raise TemplateError.new(template_errors) if raise_errors
return false
end
if raise_errors
component_class.validate_initialization_parameters!
component_class.validate_collection_parameter!
end
if has_inline_template?
template = component_class.inline_template
redefinition_lock.synchronize do
component_class.silence_redefinition_of_method("call")
# rubocop:disable Style/EvalWithLocation
component_class.class_eval <<-RUBY, template.path, template.lineno
def call
#{compiled_inline_template(template)}
end
RUBY
# rubocop:enable Style/EvalWithLocation
component_class.define_method(:"_call_#{safe_class_name}", component_class.instance_method(:call))
component_class.silence_redefinition_of_method("render_template_for")
component_class.class_eval <<-RUBY, __FILE__, __LINE__ + 1
def render_template_for(variant = nil)
_call_#{safe_class_name}
end
RUBY
end
else
templates.each do |template|
method_name = call_method_name(template[:variant])
@variants_rendering_templates << template[:variant]
redefinition_lock.synchronize do
component_class.silence_redefinition_of_method(method_name)
# rubocop:disable Style/EvalWithLocation
component_class.class_eval <<-RUBY, template[:path], 0
def #{method_name}
#{compiled_template(template[:path])}
end
RUBY
# rubocop:enable Style/EvalWithLocation
end
end
define_render_template_for
end
component_class.build_i18n_backend
CompileCache.register(component_class)
end
def renders_template_for_variant?(variant)
@variants_rendering_templates.include?(variant)
end
private
attr_reader :component_class, :redefinition_lock
def define_render_template_for
variant_elsifs = variants.compact.uniq.map do |variant|
safe_name = "_call_variant_#{normalized_variant_name(variant)}_#{safe_class_name}"
component_class.define_method(safe_name, component_class.instance_method(call_method_name(variant)))
"elsif variant.to_sym == :'#{variant}'\n #{safe_name}"
end.join("\n")
component_class.define_method(:"_call_#{safe_class_name}", component_class.instance_method(:call))
body = <<-RUBY
if variant.nil?
_call_#{safe_class_name}
#{variant_elsifs}
else
_call_#{safe_class_name}
end
RUBY
redefinition_lock.synchronize do
component_class.silence_redefinition_of_method(:render_template_for)
component_class.class_eval <<-RUBY, __FILE__, __LINE__ + 1
def render_template_for(variant = nil)
#{body}
end
RUBY
end
end
def has_inline_template?
component_class.respond_to?(:inline_template) && component_class.inline_template.present?
end
def template_errors
@__vc_template_errors ||=
begin
errors = []
if (templates + inline_calls).empty? && !has_inline_template?
errors << "Couldn't find a template file or inline render method for #{component_class}."
end
if templates.count { |template| template[:variant].nil? } > 1
errors <<
"More than one template found for #{component_class}. " \
"There can only be one default template file per component."
end
invalid_variants =
templates
.group_by { |template| template[:variant] }
.map { |variant, grouped| variant if grouped.length > 1 }
.compact
.sort
unless invalid_variants.empty?
errors <<
"More than one template found for #{"variant".pluralize(invalid_variants.count)} " \
"#{invalid_variants.map { |v| "'#{v}'" }.to_sentence} in #{component_class}. " \
"There can only be one template file per variant."
end
if templates.find { |template| template[:variant].nil? } && inline_calls_defined_on_self.include?(:call)
errors <<
"Template file and inline render method found for #{component_class}. " \
"There can only be a template file or inline render method per component."
end
duplicate_template_file_and_inline_variant_calls =
templates.pluck(:variant) & variants_from_inline_calls(inline_calls_defined_on_self)
unless duplicate_template_file_and_inline_variant_calls.empty?
count = duplicate_template_file_and_inline_variant_calls.count
errors <<
"Template #{"file".pluralize(count)} and inline render #{"method".pluralize(count)} " \
"found for #{"variant".pluralize(count)} " \
"#{duplicate_template_file_and_inline_variant_calls.map { |v| "'#{v}'" }.to_sentence} " \
"in #{component_class}. " \
"There can only be a template file or inline render method per variant."
end
uniq_variants = variants.compact.uniq
normalized_variants = uniq_variants.map { |variant| normalized_variant_name(variant) }
colliding_variants = uniq_variants.select do |variant|
normalized_variants.count(normalized_variant_name(variant)) > 1
end
unless colliding_variants.empty?
errors <<
"Colliding templates #{colliding_variants.sort.map { |v| "'#{v}'" }.to_sentence} " \
"found in #{component_class}."
end
errors
end
end
def templates
@templates ||=
begin
extensions = ActionView::Template.template_handler_extensions
component_class.sidecar_files(extensions).each_with_object([]) do |path, memo|
pieces = File.basename(path).split(".")
memo << {
path: path,
variant: pieces[1..-2].join(".").split("+").second&.to_sym,
handler: pieces.last
}
end
end
end
def inline_calls
@inline_calls ||=
begin
# Fetch only ViewComponent ancestor classes to limit the scope of
# finding inline calls
view_component_ancestors =
(
component_class.ancestors.take_while { |ancestor| ancestor != ViewComponent::Base } -
component_class.included_modules
)
view_component_ancestors.flat_map { |ancestor| ancestor.instance_methods(false).grep(/^call(_|$)/) }.uniq
end
end
def inline_calls_defined_on_self
@inline_calls_defined_on_self ||= component_class.instance_methods(false).grep(/^call(_|$)/)
end
def variants
@__vc_variants = (
templates.map { |template| template[:variant] } + variants_from_inline_calls(inline_calls)
).compact.uniq
end
def variants_from_inline_calls(calls)
calls.reject { |call| call == :call }.map do |variant_call|
variant_call.to_s.sub("call_", "").to_sym
end
end
def compiled_inline_template(template)
handler = ActionView::Template.handler_for_extension(template.language)
template.rstrip! if component_class.strip_trailing_whitespace?
compile_template(template.source, handler)
end
def compiled_template(file_path)
handler = ActionView::Template.handler_for_extension(File.extname(file_path).delete("."))
template = File.read(file_path)
compile_template(template, handler)
end
def compile_template(template, handler)
template.rstrip! if component_class.strip_trailing_whitespace?
if handler.method(:call).parameters.length > 1
handler.call(component_class, template)
# :nocov:
else
handler.call(
OpenStruct.new(
source: template,
identifier: component_class.identifier,
type: component_class.type
)
)
end
# :nocov:
end
def call_method_name(variant)
if variant.present? && variants.include?(variant)
"call_#{normalized_variant_name(variant)}"
else
"call"
end
end
def normalized_variant_name(variant)
variant.to_s.gsub("-", "__").gsub(".", "___")
end
def safe_class_name
@safe_class_name ||= component_class.name.underscore.gsub("/", "__")
end
def should_compile_superclass?
development? && templates.empty? && !has_inline_template? && !call_defined?
end
def call_defined?
component_class.instance_methods(false).include?(:call) ||
component_class.private_instance_methods(false).include?(:call)
end
end
end
view_component-3.12.1/lib/view_component/with_content_helper.rb 0000644 0000041 0000041 00000000350 14624365352 025065 0 ustar www-data www-data # frozen_string_literal: true
module ViewComponent
module WithContentHelper
def with_content(value)
raise NilWithContentError if value.nil?
@__vc_content_set_by_with_content = value
self
end
end
end
view_component-3.12.1/lib/view_component/test_helpers.rb 0000644 0000041 0000041 00000022416 14624365352 023531 0 ustar www-data www-data # frozen_string_literal: true
module ViewComponent
module TestHelpers
begin
require "capybara/minitest"
include Capybara::Minitest::Assertions
def page
@page ||= Capybara::Node::Simple.new(rendered_content)
end
def refute_component_rendered
assert_no_selector("body")
end
rescue LoadError
# We don't have a test case for running an application without capybara installed.
# It's probably fine to leave this without coverage.
# :nocov:
if ENV["DEBUG"]
warn(
"WARNING in `ViewComponent::TestHelpers`: Add `capybara` " \
"to Gemfile to use Capybara assertions."
)
end
# :nocov:
end
# Returns the result of a render_inline call.
#
# @return [ActionView::OutputBuffer]
attr_reader :rendered_content
# Render a component inline. Internally sets `page` to be a `Capybara::Node::Simple`,
# allowing for Capybara assertions to be used:
#
# ```ruby
# render_inline(MyComponent.new)
# assert_text("Hello, World!")
# ```
#
# @param component [ViewComponent::Base, ViewComponent::Collection] The instance of the component to be rendered.
# @return [Nokogiri::HTML]
def render_inline(component, **args, &block)
@page = nil
@rendered_content =
if Rails.version.to_f >= 6.1
vc_test_controller.view_context.render(component, args, &block)
# :nocov:
else
vc_test_controller.view_context.render_component(component, &block)
end
# :nocov:
Nokogiri::HTML.fragment(@rendered_content)
end
# Render a preview inline. Internally sets `page` to be a `Capybara::Node::Simple`,
# allowing for Capybara assertions to be used:
#
# ```ruby
# render_preview(:default)
# assert_text("Hello, World!")
# ```
#
# Note: `#rendered_preview` expects a preview to be defined with the same class
# name as the calling test, but with `Test` replaced with `Preview`:
#
# MyComponentTest -> MyComponentPreview etc.
#
# In RSpec, `Preview` is appended to `described_class`.
#
# @param name [String] The name of the preview to be rendered.
# @param from [ViewComponent::Preview] The class of the preview to be rendered.
# @param params [Hash] Parameters to be passed to the preview.
# @return [Nokogiri::HTML]
def render_preview(name, from: __vc_test_helpers_preview_class, params: {})
previews_controller = __vc_test_helpers_build_controller(Rails.application.config.view_component.preview_controller.constantize)
# From what I can tell, it's not possible to overwrite all request parameters
# at once, so we set them individually here.
params.each do |k, v|
previews_controller.request.params[k] = v
end
previews_controller.request.params[:path] = "#{from.preview_name}/#{name}"
previews_controller.set_response!(ActionDispatch::Response.new)
result = previews_controller.previews
@rendered_content = result
Nokogiri::HTML.fragment(@rendered_content)
end
# Execute the given block in the view context (using `instance_exec`).
# Internally sets `page` to be a `Capybara::Node::Simple`, allowing for
# Capybara assertions to be used. All arguments are forwarded to the block.
#
# ```ruby
# render_in_view_context(arg1, arg2: nil) do |arg1, arg2:|
# render(MyComponent.new(arg1, arg2))
# end
#
# assert_text("Hello, World!")
# ```
def render_in_view_context(*args, &block)
@page = nil
@rendered_content = vc_test_controller.view_context.instance_exec(*args, &block)
Nokogiri::HTML.fragment(@rendered_content)
end
ruby2_keywords(:render_in_view_context) if respond_to?(:ruby2_keywords, true)
# Set the Action Pack request variant for the given block:
#
# ```ruby
# with_variant(:phone) do
# render_inline(MyComponent.new)
# end
# ```
#
# @param variant [Symbol] The variant to be set for the provided block.
def with_variant(variant)
old_variants = vc_test_controller.view_context.lookup_context.variants
vc_test_controller.view_context.lookup_context.variants = variant
yield
ensure
vc_test_controller.view_context.lookup_context.variants = old_variants
end
# Set the controller to be used while executing the given block,
# allowing access to controller-specific methods:
#
# ```ruby
# with_controller_class(UsersController) do
# render_inline(MyComponent.new)
# end
# ```
#
# @param klass [ActionController::Base] The controller to be used.
def with_controller_class(klass)
old_controller = defined?(@vc_test_controller) && @vc_test_controller
@vc_test_controller = __vc_test_helpers_build_controller(klass)
yield
ensure
@vc_test_controller = old_controller
end
# Set the URL of the current request (such as when using request-dependent path helpers):
#
# ```ruby
# with_request_url("/users/42") do
# render_inline(MyComponent.new)
# end
# ```
#
# To use a specific host, pass the host param:
#
# ```ruby
# with_request_url("/users/42", host: "app.example.com") do
# render_inline(MyComponent.new)
# end
# ```
#
# To specify a request method, pass the method param:
#
# ```ruby
# with_request_url("/users/42", method: "POST") do
# render_inline(MyComponent.new)
# end
# ```
#
# @param full_path [String] The path to set for the current request.
# @param host [String] The host to set for the current request.
# @param method [String] The request method to set for the current request.
def with_request_url(full_path, host: nil, method: nil, format: :html)
old_request_host = vc_test_request.host
old_request_method = vc_test_request.request_method
old_request_path_info = vc_test_request.path_info
old_request_path_parameters = vc_test_request.path_parameters
old_request_query_parameters = vc_test_request.query_parameters
old_request_query_string = vc_test_request.query_string
old_request_format = vc_test_request.format.symbol
old_controller = defined?(@vc_test_controller) && @vc_test_controller
path, query = full_path.split("?", 2)
vc_test_request.instance_variable_set(:@fullpath, full_path)
vc_test_request.instance_variable_set(:@original_fullpath, full_path)
vc_test_request.host = host if host
vc_test_request.request_method = method if method
vc_test_request.path_info = path
vc_test_request.path_parameters = Rails.application.routes.recognize_path_with_request(vc_test_request, path, {})
vc_test_request.set_header("action_dispatch.request.query_parameters",
Rack::Utils.parse_nested_query(query).with_indifferent_access)
vc_test_request.set_header(Rack::QUERY_STRING, query)
vc_test_request.format = format
yield
ensure
vc_test_request.host = old_request_host
vc_test_request.request_method = old_request_method
vc_test_request.path_info = old_request_path_info
vc_test_request.path_parameters = old_request_path_parameters
vc_test_request.set_header("action_dispatch.request.query_parameters", old_request_query_parameters)
vc_test_request.set_header(Rack::QUERY_STRING, old_request_query_string)
vc_test_request.format = old_request_format
@vc_test_controller = old_controller
end
# Access the controller used by `render_inline`:
#
# ```ruby
# test "logged out user sees login link" do
# vc_test_controller.expects(:logged_in?).at_least_once.returns(false)
# render_inline(LoginComponent.new)
# assert_selector("[aria-label='You must be signed in']")
# end
# ```
#
# @return [ActionController::Base]
def vc_test_controller
@vc_test_controller ||= __vc_test_helpers_build_controller(Base.test_controller.constantize)
end
# Access the request used by `render_inline`:
#
# ```ruby
# test "component does not render in Firefox" do
# request.env["HTTP_USER_AGENT"] = "Mozilla/5.0"
# render_inline(NoFirefoxComponent.new)
# refute_component_rendered
# end
# ```
#
# @return [ActionDispatch::TestRequest]
def vc_test_request
require "action_controller/test_case"
@vc_test_request ||=
begin
out = ActionDispatch::TestRequest.create
out.session = ActionController::TestSession.new
out
end
end
# Note: We prefix private methods here to prevent collisions in consumer's tests.
private
def __vc_test_helpers_build_controller(klass)
klass.new.tap { |c| c.request = vc_test_request }.extend(Rails.application.routes.url_helpers)
end
def __vc_test_helpers_preview_class
result = if respond_to?(:described_class)
# :nocov:
raise "`render_preview` expected a described_class, but it is nil." if described_class.nil?
"#{described_class}Preview"
# :nocov:
else
self.class.name.gsub("Test", "Preview")
end
result = result.constantize
rescue NameError
raise NameError, "`render_preview` expected to find #{result}, but it does not exist."
end
# :nocov:
end
end
view_component-3.12.1/lib/view_component/collection.rb 0000644 0000041 0000041 00000003503 14624365352 023157 0 ustar www-data www-data # frozen_string_literal: true
require "action_view/renderer/collection_renderer" if Rails.version.to_f >= 6.1
module ViewComponent
class Collection
include Enumerable
attr_reader :component
delegate :format, to: :component
delegate :size, to: :@collection
attr_accessor :__vc_original_view_context
def set_original_view_context(view_context)
self.__vc_original_view_context = view_context
end
def render_in(view_context, &block)
components.map do |component|
component.set_original_view_context(__vc_original_view_context)
component.render_in(view_context, &block)
end.join.html_safe
end
def components
return @components if defined? @components
iterator = ActionView::PartialIteration.new(@collection.size)
component.validate_collection_parameter!(validate_default: true)
@components = @collection.map do |item|
component.new(**component_options(item, iterator)).tap do |component|
iterator.iterate!
end
end
end
def each(&block)
components.each(&block)
end
private
def initialize(component, object, **options)
@component = component
@collection = collection_variable(object || [])
@options = options
end
def collection_variable(object)
if object.respond_to?(:to_ary)
object.to_ary
else
raise InvalidCollectionArgumentError
end
end
def component_options(item, iterator)
item_options = {component.collection_parameter => item}
item_options[component.collection_counter_parameter] = iterator.index if component.counter_argument_present?
item_options[component.collection_iteration_parameter] = iterator.dup if component.iteration_argument_present?
@options.merge(item_options)
end
end
end
view_component-3.12.1/lib/view_component/version.rb 0000644 0000041 0000041 00000000405 14624365352 022507 0 ustar www-data www-data # frozen_string_literal: true
module ViewComponent
module VERSION
MAJOR = 3
MINOR = 12
PATCH = 1
PRE = nil
STRING = [MAJOR, MINOR, PATCH, PRE].compact.join(".")
end
end
puts ViewComponent::VERSION::STRING if __FILE__ == $PROGRAM_NAME
view_component-3.12.1/lib/view_component/inline_template.rb 0000644 0000041 0000041 00000003022 14624365352 024171 0 ustar www-data www-data # frozen_string_literal: true
module ViewComponent # :nodoc:
module InlineTemplate
extend ActiveSupport::Concern
Template = Struct.new(:source, :language, :path, :lineno)
class_methods do
def method_missing(method, *args)
return super if !method.end_with?("_template")
if defined?(@__vc_inline_template_defined) && @__vc_inline_template_defined
raise MultipleInlineTemplatesError
end
if args.size != 1
raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 1)"
end
ext = method.to_s.gsub("_template", "")
template = args.first
@__vc_inline_template_language = ext
caller = caller_locations(1..1)[0]
@__vc_inline_template = Template.new(
template,
ext,
caller.absolute_path || caller.path,
caller.lineno
)
@__vc_inline_template_defined = true
end
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
def respond_to_missing?(method, include_all = false)
method.end_with?("_template") || super
end
def inline_template
@__vc_inline_template if defined?(@__vc_inline_template)
end
def inline_template_language
@__vc_inline_template_language if defined?(@__vc_inline_template_language)
end
def inherited(subclass)
super
subclass.instance_variable_set(:@__vc_inline_template_language, inline_template_language)
end
end
end
end
view_component-3.12.1/lib/view_component/instrumentation.rb 0000644 0000041 0000041 00000001312 14624365352 024263 0 ustar www-data www-data # frozen_string_literal: true
require "active_support/notifications"
module ViewComponent # :nodoc:
module Instrumentation
def self.included(mod)
mod.prepend(self) unless ancestors.include?(ViewComponent::Instrumentation)
end
def render_in(view_context, &block)
ActiveSupport::Notifications.instrument(
notification_name,
{
name: self.class.name,
identifier: self.class.identifier
}
) do
super(view_context, &block)
end
end
private
def notification_name
return "!render.view_component" if ViewComponent::Base.config.use_deprecated_instrumentation_name
"render.view_component"
end
end
end
view_component-3.12.1/lib/view_component/errors.rb 0000644 0000041 0000041 00000021466 14624365352 022350 0 ustar www-data www-data module ViewComponent
class BaseError < StandardError
def initialize
super(self.class::MESSAGE)
end
end
class DuplicateSlotContentError < StandardError
MESSAGE =
"It looks like a block was provided after calling `with_content` on COMPONENT, " \
"which means that ViewComponent doesn't know which content to use.\n\n" \
"To fix this issue, use either `with_content` or a block."
def initialize(klass_name)
super(MESSAGE.gsub("COMPONENT", klass_name.to_s))
end
end
class TemplateError < StandardError
def initialize(errors)
super(errors.join(", "))
end
end
class MultipleInlineTemplatesError < BaseError
MESSAGE = "Inline templates can only be defined once per-component."
end
class MissingPreviewTemplateError < StandardError
MESSAGE =
"A preview template for example EXAMPLE doesn't exist.\n\n" \
"To fix this issue, create a template for the example."
def initialize(example)
super(MESSAGE.gsub("EXAMPLE", example))
end
end
class DuplicateContentError < StandardError
MESSAGE =
"It looks like a block was provided after calling `with_content` on COMPONENT, " \
"which means that ViewComponent doesn't know which content to use.\n\n" \
"To fix this issue, use either `with_content` or a block."
def initialize(klass_name)
super(MESSAGE.gsub("COMPONENT", klass_name.to_s))
end
end
class EmptyOrInvalidInitializerError < StandardError
MESSAGE =
"The COMPONENT 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 [the collections docs](https://viewcomponent.org/guide/collections.html) for more information on rendering collections."
def initialize(klass_name, parameter)
super(MESSAGE.gsub("COMPONENT", klass_name.to_s).gsub("PARAMETER", parameter.to_s))
end
end
class MissingCollectionArgumentError < StandardError
MESSAGE =
"The initializer for COMPONENT doesn't accept the parameter `PARAMETER`, " \
"which is required to render it as a collection.\n\n" \
"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."
def initialize(klass_name, parameter)
super(MESSAGE.gsub("COMPONENT", klass_name.to_s).gsub("PARAMETER", parameter.to_s))
end
end
class ReservedParameterError < StandardError
MESSAGE =
"COMPONENT initializer can't accept the parameter `PARAMETER`, as it will override a " \
"public ViewComponent method. To fix this issue, rename the parameter."
def initialize(klass_name, parameter)
super(MESSAGE.gsub("COMPONENT", klass_name.to_s).gsub("PARAMETER", parameter.to_s))
end
end
class InvalidCollectionArgumentError < BaseError
MESSAGE =
"The value of the first argument passed to `with_collection` isn't a valid collection. " \
"Make sure it responds to `to_ary`."
end
class ContentSlotNameError < StandardError
MESSAGE =
"COMPONENT declares a slot named content, which is a reserved word in ViewComponent.\n\n" \
"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."
def initialize(klass_name)
super(MESSAGE.gsub("COMPONENT", klass_name.to_s))
end
end
class InvalidSlotDefinitionError < BaseError
MESSAGE =
"Invalid slot definition. Please pass a class, " \
"string, or callable (that is proc, lambda, etc)"
end
class InvalidSlotNameError < StandardError
end
class SlotPredicateNameError < InvalidSlotNameError
MESSAGE =
"COMPONENT declares a slot named SLOT_NAME, which ends with a question mark.\n\n" \
"This isn't allowed because the ViewComponent framework already provides predicate " \
"methods ending in `?`.\n\n" \
"To fix this issue, choose a different name."
def initialize(klass_name, slot_name)
super(MESSAGE.gsub("COMPONENT", klass_name.to_s).gsub("SLOT_NAME", slot_name.to_s))
end
end
class RedefinedSlotError < StandardError
MESSAGE =
"COMPONENT declares the SLOT_NAME slot multiple times.\n\n" \
"To fix this issue, choose a different slot name."
def initialize(klass_name, slot_name)
super(MESSAGE.gsub("COMPONENT", klass_name.to_s).gsub("SLOT_NAME", slot_name.to_s))
end
end
class ReservedSingularSlotNameError < InvalidSlotNameError
MESSAGE =
"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."
def initialize(klass_name, slot_name)
super(MESSAGE.gsub("COMPONENT", klass_name.to_s).gsub("SLOT_NAME", slot_name.to_s))
end
end
class ReservedPluralSlotNameError < InvalidSlotNameError
MESSAGE =
"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."
def initialize(klass_name, slot_name)
super(MESSAGE.gsub("COMPONENT", klass_name.to_s).gsub("SLOT_NAME", slot_name.to_s))
end
end
class UncountableSlotNameError < InvalidSlotNameError
MESSAGE =
"COMPONENT declares a slot named SLOT_NAME, which is an uncountable word\n\n" \
"To fix this issue, choose a different name."
def initialize(klass_name, slot_name)
super(MESSAGE.gsub("COMPONENT", klass_name.to_s).gsub("SLOT_NAME", slot_name.to_s))
end
end
class ContentAlreadySetForPolymorphicSlotError < StandardError
MESSAGE = "Content for slot SLOT_NAME has already been provided."
def initialize(slot_name)
super(MESSAGE.gsub("SLOT_NAME", slot_name.to_s))
end
end
class NilWithContentError < BaseError
MESSAGE =
"No content provided to `#with_content` for #{self}.\n\n" \
"To fix this issue, pass a value."
end
class TranslateCalledBeforeRenderError < BaseError
MESSAGE =
"`#translate` can't be used during initialization as it depends " \
"on the view context that only exists once a ViewComponent is passed to " \
"the Rails render pipeline.\n\n" \
"It's sometimes possible to fix this issue by moving code dependent on " \
"`#translate` to a [`#before_render` method](https://viewcomponent.org/api.html#before_render--void)."
end
class HelpersCalledBeforeRenderError < BaseError
MESSAGE =
"`#helpers` can't be used during initialization as it depends " \
"on the view context that only exists once a ViewComponent is passed to " \
"the Rails render pipeline.\n\n" \
"It's sometimes possible to fix this issue by moving code dependent on " \
"`#helpers` to a [`#before_render` method](https://viewcomponent.org/api.html#before_render--void)."
end
class ControllerCalledBeforeRenderError < BaseError
MESSAGE =
"`#controller` can't be used during initialization, as it depends " \
"on the view context that only exists once a ViewComponent is passed to " \
"the Rails render pipeline.\n\n" \
"It's sometimes possible to fix this issue by moving code dependent on " \
"`#controller` to a [`#before_render` method](https://viewcomponent.org/api.html#before_render--void)."
end
# :nocov:
class NoMatchingTemplatesForPreviewError < StandardError
MESSAGE = "Found 0 matches for templates for TEMPLATE_IDENTIFIER."
def initialize(template_identifier)
super(MESSAGE.gsub("TEMPLATE_IDENTIFIER", template_identifier))
end
end
class MultipleMatchingTemplatesForPreviewError < StandardError
MESSAGE = "Found multiple templates for TEMPLATE_IDENTIFIER."
def initialize(template_identifier)
super(MESSAGE.gsub("TEMPLATE_IDENTIFIER", template_identifier))
end
end
# :nocov:
class SystemTestControllerOnlyAllowedInTestError < BaseError
MESSAGE = "ViewComponent SystemTest controller must only be called in a test environment for security reasons."
end
class SystemTestControllerNefariousPathError < BaseError
MESSAGE = "ViewComponent SystemTest controller attempted to load a file outside of the expected directory."
end
class AlreadyDefinedPolymorphicSlotSetterError < StandardError
MESSAGE =
"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."
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
end
end
view_component-3.12.1/lib/view_component/capture_compatibility.rb 0000644 0000041 0000041 00000003126 14624365352 025421 0 ustar www-data www-data # frozen_string_literal: true
module ViewComponent
# CaptureCompatibility is a module that patches #capture to fix issues
# related to ViewComponent and functionality that relies on `capture`
# like forms, capture itself, turbo frames, etc.
#
# This underlying incompatibility with ViewComponent and capture is
# that several features like forms keep a reference to the primary
# `ActionView::Base` instance which has its own @output_buffer. When
# `#capture` is called on the original `ActionView::Base` instance while
# evaluating a block from a ViewComponent the @output_buffer is overridden
# in the ActionView::Base instance, and *not* the component. This results
# in a double render due to `#capture` implementation details.
#
# To resolve the issue, we override `#capture` so that we can delegate
# the `capture` logic to the ViewComponent that created the block.
module CaptureCompatibility
def self.included(base)
return if base < InstanceMethods
base.class_eval do
alias_method :original_capture, :capture
end
base.prepend(InstanceMethods)
end
module InstanceMethods
def capture(*args, &block)
# Handle blocks that originate from C code and raise, such as `&:method`
return original_capture(*args, &block) if block.source_location.nil?
block_context = block.binding.receiver
if block_context != self && block_context.class < ActionView::Base
block_context.original_capture(*args, &block)
else
original_capture(*args, &block)
end
end
end
end
end
view_component-3.12.1/lib/view_component/rails/ 0000755 0000041 0000041 00000000000 14624365352 021610 5 ustar www-data www-data view_component-3.12.1/lib/view_component/rails/tasks/ 0000755 0000041 0000041 00000000000 14624365352 022735 5 ustar www-data www-data view_component-3.12.1/lib/view_component/rails/tasks/view_component.rake 0000644 0000041 0000041 00000000516 14624365352 026637 0 ustar www-data www-data # frozen_string_literal: true
task stats: "view_component:statsetup"
namespace :view_component do
task :statsetup do
# :nocov:
require "rails/code_statistics"
dir = ViewComponent::Base.view_component_path
::STATS_DIRECTORIES << ["ViewComponents", dir] if File.directory?(Rails.root + dir)
# :nocov:
end
end
view_component-3.12.1/lib/view_component/slotable.rb 0000644 0000041 0000041 00000033360 14624365352 022635 0 ustar www-data www-data # frozen_string_literal: true
require "active_support/concern"
require "active_support/inflector/inflections"
require "view_component/slot"
module ViewComponent
module Slotable
extend ActiveSupport::Concern
RESERVED_NAMES = {
singular: %i[content render].freeze,
plural: %i[contents renders].freeze
}.freeze
# Setup component slot state
included do
# Hash of registered Slots
class_attribute :registered_slots
self.registered_slots = {}
end
class_methods do
##
# Registers a sub-component
#
# = Example
#
# renders_one :header -> (classes:) do
# HeaderComponent.new(classes: classes)
# end
#
# # OR
#
# renders_one :header, HeaderComponent
#
# where `HeaderComponent` is defined as:
#
# class HeaderComponent < ViewComponent::Base
# def initialize(classes:)
# @classes = classes
# end
# end
#
# and has the following template:
#
#
# <%= content %>
#
#
# = Rendering sub-component content
#
# The component's sidecar template can access the sub-component by calling a
# helper method with the same name as the sub-component.
#
#
# <%= header do %>
# My header title
# <% end %>
#
#
# = Setting sub-component content
#
# Consumers of the component can render a sub-component by calling a
# helper method with the same name as the slot prefixed with `with_`.
#
# <%= render_inline(MyComponent.new) do |component| %>
# <% component.with_header(classes: "Foo") do %>
#
Bar
# <% end %>
# <% end %>
#
# Additionally, content can be set by calling `with_SLOT_NAME_content`
# on the component instance.
#
# <%= render_inline(MyComponent.new.with_header_content("Foo")) %>
def renders_one(slot_name, callable = nil)
validate_singular_slot_name(slot_name)
if callable.is_a?(Hash) && callable.key?(:types)
register_polymorphic_slot(slot_name, callable[:types], collection: false)
else
validate_plural_slot_name(ActiveSupport::Inflector.pluralize(slot_name).to_sym)
setter_method_name = :"with_#{slot_name}"
define_method setter_method_name do |*args, &block|
set_slot(slot_name, nil, *args, &block)
end
ruby2_keywords(setter_method_name) if respond_to?(:ruby2_keywords, true)
define_method slot_name do
get_slot(slot_name)
end
define_method :"#{slot_name}?" do
get_slot(slot_name).present?
end
define_method :"with_#{slot_name}_content" do |content|
send(setter_method_name) { content.to_s }
self
end
register_slot(slot_name, collection: false, callable: callable)
end
end
##
# Registers a collection sub-component
#
# = Example
#
# renders_many :items, -> (name:) { ItemComponent.new(name: name }
#
# # OR
#
# renders_many :items, ItemComponent
#
# = Rendering sub-components
#
# The component's sidecar template can access the slot by calling a
# helper method with the same name as the slot.
#
#
# <% items.each do |item| %>
# <%= item %>
# <% end %>
#
#
# = Setting sub-component content
#
# Consumers of the component can set the content of a slot by calling a
# helper method with the same name as the slot prefixed with `with_`. The
# method can be called multiple times to append to the slot.
#
# <%= render_inline(MyComponent.new) do |component| %>
# <% component.with_item(name: "Foo") do %>
#
One
# <% end %>
#
# <% component.with_item(name: "Bar") do %>
#
two
# <% end %>
# <% end %>
def renders_many(slot_name, callable = nil)
validate_plural_slot_name(slot_name)
if callable.is_a?(Hash) && callable.key?(:types)
register_polymorphic_slot(slot_name, callable[:types], collection: true)
else
singular_name = ActiveSupport::Inflector.singularize(slot_name)
validate_singular_slot_name(ActiveSupport::Inflector.singularize(slot_name).to_sym)
setter_method_name = :"with_#{singular_name}"
define_method setter_method_name do |*args, &block|
set_slot(slot_name, nil, *args, &block)
end
ruby2_keywords(setter_method_name) if respond_to?(:ruby2_keywords, true)
define_method :"with_#{singular_name}_content" do |content|
send(setter_method_name) { content.to_s }
self
end
define_method :"with_#{slot_name}" do |collection_args = nil, &block|
collection_args.map do |args|
if args.respond_to?(:to_hash)
set_slot(slot_name, nil, **args, &block)
else
set_slot(slot_name, nil, *args, &block)
end
end
end
define_method slot_name do
get_slot(slot_name)
end
define_method :"#{slot_name}?" do
get_slot(slot_name).present?
end
register_slot(slot_name, collection: true, callable: callable)
end
end
def slot_type(slot_name)
registered_slot = registered_slots[slot_name]
if registered_slot
registered_slot[:collection] ? :collection : :single
else
plural_slot_name = ActiveSupport::Inflector.pluralize(slot_name).to_sym
plural_registered_slot = registered_slots[plural_slot_name]
plural_registered_slot&.fetch(:collection) ? :collection_item : nil
end
end
# Clone slot configuration into child class
# see #test_slots_pollution
def inherited(child)
child.registered_slots = registered_slots.clone
super
end
def register_polymorphic_slot(slot_name, types, collection:)
define_method(slot_name) do
get_slot(slot_name)
end
define_method(:"#{slot_name}?") do
get_slot(slot_name).present?
end
renderable_hash = types.each_with_object({}) do |(poly_type, poly_attributes_or_callable), memo|
if poly_attributes_or_callable.is_a?(Hash)
poly_callable = poly_attributes_or_callable[:renders]
poly_slot_name = poly_attributes_or_callable[:as]
else
poly_callable = poly_attributes_or_callable
poly_slot_name = nil
end
poly_slot_name ||=
if collection
"#{ActiveSupport::Inflector.singularize(slot_name)}_#{poly_type}"
else
"#{slot_name}_#{poly_type}"
end
memo[poly_type] = define_slot(
poly_slot_name, collection: collection, callable: poly_callable
)
setter_method_name = :"with_#{poly_slot_name}"
if instance_methods.include?(setter_method_name)
raise AlreadyDefinedPolymorphicSlotSetterError.new(setter_method_name, poly_slot_name)
end
define_method(setter_method_name) do |*args, &block|
set_polymorphic_slot(slot_name, poly_type, *args, &block)
end
ruby2_keywords(setter_method_name) if respond_to?(:ruby2_keywords, true)
define_method :"with_#{poly_slot_name}_content" do |content|
send(setter_method_name) { content.to_s }
self
end
end
registered_slots[slot_name] = {
collection: collection,
renderable_hash: renderable_hash
}
end
private
def register_slot(slot_name, **kwargs)
registered_slots[slot_name] = define_slot(slot_name, **kwargs)
end
def define_slot(slot_name, collection:, callable:)
# Setup basic slot data
slot = {
collection: collection
}
return slot unless callable
# If callable responds to `render_in`, we set it on the slot as a renderable
if callable.respond_to?(:method_defined?) && callable.method_defined?(:render_in)
slot[:renderable] = callable
elsif callable.is_a?(String)
# If callable is a string, we assume it's referencing an internal class
slot[:renderable_class_name] = callable
elsif callable.respond_to?(:call)
# If slot doesn't respond to `render_in`, we assume it's a proc,
# define a method, and save a reference to it to call when setting
method_name = :"_call_#{slot_name}"
define_method method_name, &callable
slot[:renderable_function] = instance_method(method_name)
else
raise(InvalidSlotDefinitionError)
end
slot
end
def validate_plural_slot_name(slot_name)
if RESERVED_NAMES[:plural].include?(slot_name.to_sym)
raise ReservedPluralSlotNameError.new(name, slot_name)
end
raise_if_slot_name_uncountable(slot_name)
raise_if_slot_conflicts_with_call(slot_name)
raise_if_slot_ends_with_question_mark(slot_name)
raise_if_slot_registered(slot_name)
end
def validate_singular_slot_name(slot_name)
if slot_name.to_sym == :content
raise ContentSlotNameError.new(name)
end
if RESERVED_NAMES[:singular].include?(slot_name.to_sym)
raise ReservedSingularSlotNameError.new(name, slot_name)
end
raise_if_slot_conflicts_with_call(slot_name)
raise_if_slot_ends_with_question_mark(slot_name)
raise_if_slot_registered(slot_name)
end
def raise_if_slot_registered(slot_name)
if registered_slots.key?(slot_name)
# TODO remove? This breaks overriding slots when slots are inherited
raise RedefinedSlotError.new(name, slot_name)
end
end
def raise_if_slot_ends_with_question_mark(slot_name)
raise SlotPredicateNameError.new(name, slot_name) if slot_name.to_s.end_with?("?")
end
def raise_if_slot_conflicts_with_call(slot_name)
if slot_name.start_with?("call_")
raise InvalidSlotNameError, "Slot cannot start with 'call_'. Please rename #{slot_name}"
end
end
def raise_if_slot_name_uncountable(slot_name)
slot_name = slot_name.to_s
if slot_name.pluralize == slot_name.singularize
raise UncountableSlotNameError.new(name, slot_name)
end
end
end
def get_slot(slot_name)
content unless content_evaluated? # ensure content is loaded so slots will be defined
slot = self.class.registered_slots[slot_name]
@__vc_set_slots ||= {}
if @__vc_set_slots[slot_name]
return @__vc_set_slots[slot_name]
end
if slot[:collection]
[]
end
end
def set_slot(slot_name, slot_definition = nil, *args, &block)
slot_definition ||= self.class.registered_slots[slot_name]
slot = Slot.new(self)
# Passing the block to the sub-component wrapper like this has two
# benefits:
#
# 1. If this is a `content_area` style sub-component, we will render the
# block via the `slot`
#
# 2. Since we have to pass block content to components when calling
# `render`, evaluating the block here would require us to call
# `view_context.capture` twice, which is slower
slot.__vc_content_block = block if block
# If class
if slot_definition[:renderable]
slot.__vc_component_instance = slot_definition[:renderable].new(*args)
# If class name as a string
elsif slot_definition[:renderable_class_name]
slot.__vc_component_instance =
self.class.const_get(slot_definition[:renderable_class_name]).new(*args)
# If passed a lambda
elsif slot_definition[:renderable_function]
# Use `bind(self)` to ensure lambda is executed in the context of the
# current component. This is necessary to allow the lambda to access helper
# methods like `content_tag` as well as parent component state.
renderable_function = slot_definition[:renderable_function].bind(self)
renderable_value =
if block
renderable_function.call(*args) do |*rargs|
view_context.capture(*rargs, &block)
end
else
renderable_function.call(*args)
end
# Function calls can return components, so if it's a component handle it specially
if renderable_value.respond_to?(:render_in)
slot.__vc_component_instance = renderable_value
else
slot.__vc_content = renderable_value
end
end
@__vc_set_slots ||= {}
if slot_definition[:collection]
@__vc_set_slots[slot_name] ||= []
@__vc_set_slots[slot_name].push(slot)
else
@__vc_set_slots[slot_name] = slot
end
slot
end
ruby2_keywords(:set_slot) if respond_to?(:ruby2_keywords, true)
def set_polymorphic_slot(slot_name, poly_type = nil, *args, &block)
slot_definition = self.class.registered_slots[slot_name]
if !slot_definition[:collection] && (defined?(@__vc_set_slots) && @__vc_set_slots[slot_name])
raise ContentAlreadySetForPolymorphicSlotError.new(slot_name)
end
poly_def = slot_definition[:renderable_hash][poly_type]
set_slot(slot_name, poly_def, *args, &block)
end
ruby2_keywords(:set_polymorphic_slot) if respond_to?(:ruby2_keywords, true)
end
end
view_component-3.12.1/LICENSE.txt 0000644 0000041 0000041 00000002103 14624365352 016513 0 ustar www-data www-data MIT License
Copyright (c) 2018-present ViewComponent contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
view_component-3.12.1/docs/ 0000755 0000041 0000041 00000000000 14624365352 015624 5 ustar www-data www-data view_component-3.12.1/docs/CHANGELOG.md 0000644 0000041 0000041 00000210174 14624365352 017442 0 ustar www-data www-data ---
layout: default
title: Changelog
nav_order: 5
---
# Changelog
## main
## 3.12.1
* Ensure content is rendered correctly for forwarded slots.
*Cameron Dutro*
## 3.12.0
* Remove offline links from resources.
*Paulo Henrique Meneses*
* Fix templates not being correctly populated when caller location label has a prefix.
On the upstream version of Ruby, method owners are now included in backtraces as prefixes. This caused the call stack filtering to not work as intended and thus `source_location` to be incorrect for child ViewComponents, consequently not populating templates correctly.
*Allan Pires, Jason Kim*
* Use component path for generating RSpec files.
When generating new RSpec files for components, the generator will use the `view_component_path` value in the config to decide where to put the new spec file. For instance, if the `view_component_path` option has been changed to `app/views/components`, the generator will put the spec file in `spec/views/components`. **If the `view_component_path` doesn't start with `app/`, then the generator will fall back to `spec/components/`.**
This feature is enabled via the `config.view_component.generate.use_component_path_for_rspec_tests` option, defaulting to `false`. The default will change to `true` in ViewComponent v4.
*William Mathewson*
## 3.11.0
* Fix running non-integration tests under Rails main.
*Cameron Dutro*
* Better name and link for Avo.
*Adrian Marin*
* Document using rack-mini-profiler with ViewComponent.
*Thomas Carr*
* Move dependencies to gemspec.
*Joel Hawksley*
* Include ViewComponent::UseHelpers by default.
*Reegan Viljoen*
* Bump `puma` in Gemfile.lock.
*Cameron Dutro*
* Add Keenly to users list.
*Vinoth*
## 3.10.0
* Fix html escaping in `#call` for non-strings.
*Reegan Viljoen, Cameron Dutro*
* Add `output_preamble` to match `output_postamble`, using the same safety checks.
*Kali Donovan, Michael Daross*
* Exclude html escaping of I18n reserved keys with `I18n::RESERVED_KEYS` rather than `I18n.reserved_keys_pattern`.
*Nick Coyne*
* Update CI configuration to use `Appraisal`.
*Hans Lemuet, Simon Fish*
## 3.9.0
* Don’t break `rails stats` if ViewComponent path is missing.
*Claudio Baccigalupo*
* Add deprecation warnings for EOL ruby and Rails versions and patches associated with them.
*Reegan Viljoen*
* Add support for Ruby 3.3.
*Reegan Viljoen*
* Allow translations to be inherited and overridden in subclasses.
*Elia Schito*
* Resolve console warnings when running test suite.
*Joel Hawksley*
* Fix spelling in a local variable.
*Olle Jonsson*
* Avoid duplicating rendered string when `output_postamble` is blank.
*Mitchell Henke*
* Ensure HTML output safety.
*Cameron Dutro*
## 3.8.0
* Use correct value for the `config.action_dispatch.show_exceptions` config option for edge Rails.
*Cameron Dutro*
* Remove unsupported versions of Rails & Ruby from CI matrix.
*Reegan Viljoen*
* Raise error when uncountable slot names are used in `renders_many`
*Hugo Chantelauze*
*Reegan Viljoen*
* Replace usage of `String#ends_with?` with `String#end_with?` to reduce the dependency on ActiveSupport core extensions.
*halo*
* Don't add ActionDispatch::Static middleware unless `public_file_server.enabled`.
*Daniel Gonzalez*
*Reegan Viljoen*
* Resolve an issue where slots starting with `call` would cause a `NameError`
*Blake Williams*
* Add `use_helper` API.
*Reegan Viljoen*
* Fix bug where the `Rails` module wasn't being searched from the root namespace.
*Zenéixe*
* Fix bug where `#with_request_url`, set the incorrect `request.fullpath`.
*Nachiket Pusalkar*
* Allow setting method when using the `with_request_url` test helper.
*Andrew Duthie*
## 3.7.0
* Support Rails 7.1 in CI.
*Reegan Viljoen*
*Cameron Dutro*
* Document the capture compatibility patch on the Known issues page.
*Simon Fish*
* Add Simundia to list of companies using ViewComponent.
*Alexandre Ignjatovic*
* Reduce UnboundMethod objects by memoizing initialize_parameters.
*Rainer Borene*
* Improve docs about inline templates interpolation.
*Hans Lemuet*
* Update generators.md to clarify the way of changing `config.view_component.view_component_path`.
*Shozo Hatta*
* Attempt to fix Ferrum timeout errors by creating driver with unique name.
*Cameron Dutro*
## 3.6.0
* Refer to `helpers` in `NameError` message in development and test environments.
*Simon Fish*
* Fix API documentation and revert unnecessary change in `preview.rb`.
*Richard Macklin*
* Initialize ViewComponent::Config with defaults before framework load.
*Simon Fish*
* Add 3.2 to the list of Ruby CI versions
*Igor Drozdov*
* Stop running PVC's `docs:preview` rake task in CI, as the old docsite has been removed.
*Cameron Dutro*
* Minor testing documentation improvement.
*Travis Gaff*
* Add SearchApi to users list.
*Sebastjan Prachovskij*
* Fix `#with_request_url` to ensure `request.query_parameters` is an instance of ActiveSupport::HashWithIndifferentAccess.
*milk1000cc*
* Add PeopleForce to list of companies using ViewComponent.
*Volodymyr Khandiuk*
## 3.5.0
* Add Skroutz to users list.
*Chris Nitsas*
* Improve implementation of `#render_parent` so it respects variants and deep inheritance hierarchies.
*Cameron Dutro*
* Add CharlieHR to users list.
*Alex Balhatchet*
## 3.4.0
* Avoid including Rails `url_helpers` into `Preview` class when they're not defined.
*Richard Macklin*
* Allow instrumentation to be automatically included in Server-Timing headers generated by Rails. To enable this set the config `config.use_deprecated_instrumentation_name = false`. The old key `!render.view_component` is deprecated: update ActiveSupport::Notification subscriptions to `render.view_component`.
*Travis Gaff*
## 3.3.0
* Include InlineTemplate by default in Base. **Note:** It's no longer necessary to include `ViewComponent::InlineTemplate` to use inline templates.
*Joel Hawksley*
* Allow Setting host when using the `with_request_url` test helper.
*Daniel Alfaro*
* Resolve ambiguous preview paths when using components without the Component suffix.
*Reed Law*
## 3.2.0
* Fix viewcomponent.org Axe violations.
*Joel Hawksley*
* Fix example of RSpec configuration in docs
*Pasha Kalashnikov*
* Add URL helpers to previews
*Reegan Viljoen*
## 3.1.0
* Check `defined?(Rails) && Rails.application` before using `ViewComponent::Base.config.view_component_path`.
*Donapieppo*
* Allow customization of polymorphic slot setters.
*Cameron Dutro*
* Fix duplication in configuration docs.
*Tom Chen*
* Fix helpers not reloading in development.
*Jonathan del Strother*
* Add `SECURITY.md`.
*Joel Hawksley*
* Add Ophelos to list of companies using ViewComponent.
*Graham Rogers*
* Add FlightLogger to list of companies using ViewComponent.
*Joseph Carpenter*
* Fix coverage reports overwriting each other when running locally.
*Jonathan del Strother*
* Add @reeganviljoen to triage team.
*Reegan Viljoen*
### v3.0.0
1,000+ days and 100+ releases later, the 200+ contributors to ViewComponent are proud to ship v3.0.0!
We're so grateful for all the work of community members to get us to this release. Whether it’s filing bug reports, designing APIs in long-winded discussion threads, or writing code itself, ViewComponent is built by the community, for the community. We couldn’t be more proud of what we’re building together :heart:
This release makes the following breaking changes, many of which have long been deprecated:
* BREAKING: Remove deprecated slots setter methods. Use `with_SLOT_NAME` instead.
*Joel Hawksley*
For example:
```diff
<%= render BlogComponent.new do |component| %>
- <% component.header do %>
+ <% component.with_header do %>
<%= link_to "My blog", root_path %>
<% end %>
<% end %>
```
* BREAKING: Remove deprecated SlotsV1 in favor of current SlotsV2.
*Joel Hawksley*
* BREAKING: Remove deprecated `content_areas` feature. Use Slots instead.
*Joel Hawksley*
* BREAKING: Remove deprecated support for loading ViewComponent engine manually. Make sure `require "view_component/engine"` is removed from `Gemfile`.
*Joel Hawksley*
* BREAKING: Remove deprecated `generate_*` methods. Use `generate.*` instead.
*Joel Hawksley*
* BREAKING: Remove deprecated `with_variant` method.
*Joel Hawksley*
* BREAKING: Remove deprecated `rendered_component` in favor of `rendered_content`.
*Joel Hawksley*
* BREAKING: Remove deprecated `config.preview_path` in favor of `config.preview_paths`.
*Joel Hawksley*
* BREAKING: Support Ruby 2.7+ instead of 2.4+
*Joel Hawksley*
* BREAKING: Remove deprecated `before_render_check`.
*Joel Hawksley*
* BREAKING: Change counter variable to start iterating from `0` instead of `1`.
*Frank S*
* BREAKING: `#SLOT_NAME` getter no longer accepts arguments. This change was missed as part of the earlier deprecation in `3.0.0.rc1`.
*Joel Hawksley*
* BREAKING: Raise `TranslateCalledBeforeRenderError`, `ControllerCalledBeforeRenderError`, or `HelpersCalledBeforeRenderError` instead of `ViewContextCalledBeforeRenderError`.
*Joel Hawksley*
* BREAKING: Raise `SlotPredicateNameError`, `RedefinedSlotError`, `ReservedSingularSlotNameError`, `ContentSlotNameError`, `InvalidSlotDefinitionError`, `ReservedPluralSlotNameError`, `ContentAlreadySetForPolymorphicSlotErrror`, `SystemTestControllerOnlyAllowedInTestError`, `SystemTestControllerNefariousPathError`, `NoMatchingTemplatesForPreviewError`, `MultipleMatchingTemplatesForPreviewError`, `DuplicateContentError`, `EmptyOrInvalidInitializerError`, `MissingCollectionArgumentError`, `ReservedParameterError`, `InvalidCollectionArgumentError`, `MultipleInlineTemplatesError`, `MissingPreviewTemplateError`, `DuplicateSlotContentError` or `NilWithContentError` instead of generic error classes.
*Joel Hawksley*
* BREAKING: Rename `SlotV2` to `Slot` and `SlotableV2` to `Slotable`.
*Joel Hawksley*
* BREAKING: Incorporate `PolymorphicSlots` into `Slotable`. To migrate, remove any references to `PolymorphicSlots` as they are no longer necessary.
*Joel Hawksley*
* BREAKING: Rename private TestHelpers#controller, #build_controller, #request, and #preview_class to avoid conflicts. Note: While these methods were undocumented and marked as private, they were accessible in tests. As such, we're considering this to be a breaking change.
*Joel Hawksley*
* Add support for CSP nonces inside of components.
*Reegan Viljoen*
### v3.0.0.rc6
Run into an issue with this release candidate? [Let us know](https://github.com/ViewComponent/view_component/issues/1629). We hope to release v3.0.0 in the near future!
* BREAKING: `#SLOT_NAME` getter no longer accepts arguments. This change was missed as part of the earlier deprecation in `3.0.0.rc1`.
*Joel Hawksley*
* BREAKING: Raise `TranslateCalledBeforeRenderError`, `ControllerCalledBeforeRenderError`, or `HelpersCalledBeforeRenderError` instead of `ViewContextCalledBeforeRenderError`.
*Joel Hawksley*
* BREAKING: Raise `SlotPredicateNameError`, `RedefinedSlotError`, `ReservedSingularSlotNameError`, `ContentSlotNameError`, `InvalidSlotDefinitionError`, `ReservedPluralSlotNameError`, `ContentAlreadySetForPolymorphicSlotErrror`, `SystemTestControllerOnlyAllowedInTestError`, `SystemTestControllerNefariousPathError`, `NoMatchingTemplatesForPreviewError`, `MultipleMatchingTemplatesForPreviewError`, `DuplicateContentError`, `EmptyOrInvalidInitializerError`, `MissingCollectionArgumentError`, `ReservedParameterError`, `InvalidCollectionArgumentError`, `MultipleInlineTemplatesError`, `MissingPreviewTemplateError`, `DuplicateSlotContentError` or `NilWithContentError` instead of generic error classes.
*Joel Hawksley*
* Fix bug where `content?` and `with_content` didn't work reliably with slots.
*Derek Kniffin, Joel Hawksley*
* Add `with_SLOT_NAME_content` helper.
*Will Cosgrove*
* Allow ActiveRecord objects to be passed to `renders_many`.
*Leigh Halliday*
* Fix broken links in documentation.
*Ellen Keal*
* Run `standardrb` against markdown in docs.
*Joel Hawksley*
* Allow `.with_content` to be redefined by components.
*Joel Hawksley*
* Run `standardrb` against markdown in docs.
*Joel Hawksley*
* Raise error if translations are used in initializer.
*Joel Hawksley*
## v3.0.0.rc5
Run into an issue with this release candidate? [Let us know](https://github.com/ViewComponent/view_component/issues/1629).
* Fix bug where `mkdir_p` failed due to incorrect permissions.
*Joel Hawksley*
* Check for inline `erb_template` calls when deciding whether to compile a component's superclass.
*Justin Kenyon*
* Protect against `SystemStackError` if `CaptureCompatibility` module is included more than once.
*Cameron Dutro*
## v3.0.0.rc4
Run into an issue with this release candidate? [Let us know](https://github.com/ViewComponent/view_component/issues/1629).
* Add `TestHelpers#vc_test_request`.
*Joel Hawksley*
## v3.0.0.rc3
Run into an issue with this release candidate? [Let us know](https://github.com/ViewComponent/view_component/issues/1629).
* Fix typos in generator docs.
*Sascha Karnatz*
* Add `TestHelpers#vc_test_controller`.
*Joel Hawksley*
* Document `config.view_component.capture_compatibility_patch_enabled` as option for the known incompatibilities with Rails form helpers.
*Tobias L. Maier*
* Add support for experimental inline templates.
*Blake Williams*
* Expose `translate` and `t` I18n methods on component classes.
*Elia Schito*
* Protect against Arbitrary File Read edge case in `ViewComponentsSystemTestController`.
*Nick Malcolm*
## v3.0.0.rc2
Run into an issue with this release? [Let us know](https://github.com/ViewComponent/view_component/issues/1629).
* BREAKING: Rename `SlotV2` to `Slot` and `SlotableV2` to `Slotable`.
*Joel Hawksley*
* BREAKING: Incorporate `PolymorphicSlots` into `Slotable`. To migrate, remove any references to `PolymorphicSlots` as they are no longer necessary.
*Joel Hawksley*
* BREAKING: Rename private TestHelpers#controller, #build_controller, #request, and #preview_class to avoid conflicts. Note: While these methods were undocumented and marked as private, they were accessible in tests. As such, we're considering this to be a breaking change.
*Joel Hawksley*
* Avoid loading ActionView::Base during Rails initialization. Originally submitted in #1528.
*Jonathan del Strother*
* Improve documentation of known incompatibilities with Rails form helpers.
*Tobias L. Maier*
* Remove dependency on environment task from `view_component:statsetup`.
*Svetlin Simonyan*
* Add experimental `config.view_component.capture_compatibility_patch_enabled` option resolving rendering issues related to forms, capture, turbo frames, etc.
*Blake Williams*
* Add `#content?` method that indicates if content has been passed to component.
*Joel Hawksley*
* Added example of a custom preview controller.
*Graham Rogers*
* Add Krystal to list of companies using ViewComponent.
*Matt Bearman*
* Add Mon Ami to list of companies using ViewComponent.
*Ethan Lee-Tyson*
## 3.0.0.rc1
1,000+ days and 100+ releases later, the 200+ contributors to ViewComponent are proud to ship v3.0.0!
We're so grateful for all the work of community members to get us to this release. Whether it’s filing bug reports, designing APIs in long-winded discussion threads, or writing code itself, ViewComponent is built by the community, for the community. We couldn’t be more proud of what we’re building together :heart:
This release makes the following breaking changes, many of which have long been deprecated:
* BREAKING: Remove deprecated slots setter methods. Use `with_SLOT_NAME` instead.
*Joel Hawksley*
* BREAKING: Remove deprecated SlotsV1 in favor of current SlotsV2.
*Joel Hawksley*
* BREAKING: Remove deprecated `content_areas` feature. Use Slots instead.
*Joel Hawksley*
* BREAKING: Remove deprecated support for loading ViewComponent engine manually. Make sure `require "view_component/engine"` is removed from `Gemfile`.
*Joel Hawksley*
* BREAKING: Remove deprecated `generate_*` methods. Use `generate.*` instead.
*Joel Hawksley*
* BREAKING: Remove deprecated `with_variant` method.
*Joel Hawksley*
* BREAKING: Remove deprecated `rendered_component` in favor of `rendered_content`.
*Joel Hawksley*
* BREAKING: Remove deprecated `config.preview_path` in favor of `config.preview_paths`.
*Joel Hawksley*
* BREAKING: Support Ruby 2.7+ instead of 2.4+
*Joel Hawksley*
* BREAKING: Remove deprecated `before_render_check`.
*Joel Hawksley*
* BREAKING: Change counter variable to start iterating from `0` instead of `1`.
*Frank S*
Run into an issue with this release? [Let us know](https://github.com/ViewComponent/view_component/issues/1629).
## 2.82.0
* Revert "Avoid loading ActionView::Base during initialization (#1528)"
*Jon Rohan*
* Fix tests using `with_rendered_component_path` with custom layouts.
*Ian Hollander*
## 2.81.0
* Adjust the way response objects are set on the preview controller to work around a recent change in Rails main.
*Cameron Dutro*
* Fix typo in "Generate a Stimulus controller" documentation.
*Ben Trewern*
* Modify the `render_in_view_context` test helper to forward its args to the block.
*Cameron Dutro*
## 2.80.0
* Move system test endpoint out of the unrelated previews controller.
*Edwin Mak*
* Display Ruby 2.7 deprecation notice only once, when starting the application.
*Henrik Hauge Bjørnskov*
* Require Rails 5.2+ in gemspec and update documentation.
*Drew Bragg*
* Add documentation for using `with_rendered_component_path` with RSpec.
*Edwin Mak*
## 2.79.0
* Add ability to pass explicit `preview_path` to preview generator.
*Erinna Chen*
* Add `with_rendered_component_path` helper for writing component system tests.
*Edwin Mak*
* Include gem name and deprecation horizon in every deprecation message.
*Jan Klimo*
## 2.78.0
* Support variants with dots in their names.
*Javi Martín*
## 2.77.0
* Support variants with dashes in their names.
*Javi Martín*
## 2.76.0
* `Component.with_collection` supports components that accept splatted keyword arguments.
*Zee Spencer*
* Remove `config.view_component.use_consistent_rendering_lifecycle` since it is no longer planned for 3.0.
*Blake Williams*
* Prevent polymorphic slots from calculating `content` when setting a slot.
*Blake Williams*
* Add ability to pass in the preview class to `render_preview`.
*Jon Rohan*
* Fix issue causing PVC tests to fail in CI.
*Cameron Dutro*
* Fix YARD docs build task.
*Hans Lemuet*
* Add Startup Jobs to list of companies using ViewComponent.
*Marc Köhlbrugge*
* Run PVC's accessibility tests in a single process to avoid resource contention in CI.
*Cameron Dutro*
## 2.75.0
* Avoid loading ActionView::Base during Rails initialization.
*Jonathan del Strother*
* Mention lambda slots rendering returned values lazily in the guide.
*Graham Rogers*
* Add "ViewComponent In The Wild" articles to resources.
*Alexander Baygeldin*
## 2.74.1
* Add more users of ViewComponent to docs.
*Joel Hawksley*
* Add a known issue for usage with `turbo_frame_tag` to the documentation.
*Vlad Radulescu*
* Add note about system testing components with previews.
*Joel Hawksley*
* Remove locking mechanisms from the compiler.
*Cameron Dutro*
## 2.74.0
* Add Avo to list of companies using ViewComponent.
*Adrian Marin*
* Promote experimental `_output_postamble` method to public API as `output_postamble`.
*Joel Hawksley*
* Promote experimental `_sidecar_files` method to public API as `sidecar_files`.
*Joel Hawksley*
* Fix `show_previews` regression introduced in 2.73.0.
*Andy Baranov*
* `with_request_url` test helper supports router constraints (such as Devise).
*Aotokitsuruya*
## 2.73.0
* Remove experimental `_after_compile` lifecycle method.
*Joel Hawksley*
* Fix capitalization of JavaScript in docs.
*Erinna Chen*
* Add PrintReleaf to list of companies using ViewComponent.
*Ry Kulp*
* Simplify CI configuration to a single build per Ruby/Rails version.
*Joel Hawksley*
* Correctly document `generate.sidecar` config option.
*Ruben Smit*
* Add Yobbers to list of companies using ViewComponent.
*Anton Prins*
## 2.72.0
* Deprecate support for Ruby < 2.7 for removal in v3.0.0.
*Joel Hawksley*
* Add `changelog_uri` to gemspec.
*Joel Hawksley*
* Link to `CHANGELOG.md` instead of symlink.
*Joel Hawksley.
* Add Aluuno to list of companies using ViewComponent.
*Daniel Naves de Carvalho*
* Add `source_code_uri` to gemspec.
*Yoshiyuki Hirano*
* Update link to benchmark script in docs.
*Daniel Diekmeier*
* Add special exception message for `renders_one :content` explaining that content passed as a block will be assigned to the `content` accessor without having to create an explicit slot.
*Daniel Diekmeier*
## 2.71.0
**ViewComponent has moved to a new organization: [https://github.com/viewcomponent/view_component](https://github.com/viewcomponent/view_component). See [https://github.com/viewcomponent/view_component/issues/1424](https://github.com/viewcomponent/view_component/issues/1424) for more details.**
## 2.70.0
* `render_preview` can pass parameters to preview.
*Joel Hawksley*
* Fix docs typos.
*Joel Hawksley*
* Add architectural decisions to documentation and rename sidebar sections.
*Joel Hawksley*
* Clarify documentation on testability of Rails views.
*Joel Hawksley*
* Add Arrows to list of companies using ViewComponent.
*Matt Swanson*
* Add WIP to list of companies using ViewComponent.
*Marc Köhlbrugge*
* Update slots documentation to include how to reference slots.
*Brittany Ellich*
* Add Clio to list of companies using ViewComponent.
*Mike Buckley*
## 2.69.0
* Add missing `require` to fix `pvc` build.
*Joel Hawksley*
* Add `config.view_component.use_consistent_rendering_lifecycle` to ensure side-effects in `content` are consistently evaluated before components are rendered. This change effectively means that `content` is evaluated for every component render where `render?` returns true. As a result, code that's passed to a component via a block/content will now always be evaluated, before `#call`, which can reveal bugs in existing components. This configuration option defaults to `false` but will be enabled in 3.0 and the old behavior will be removed.
*Blake Williams*
* Update Prism to version 1.28.0.
*Thomas Hutterer*
* Corrects the deprecation warning for named slots to show the file and line where the slot is called.
*River Bailey*
## 2.68.0
* Update `gemspec` author to be ViewComponent team.
*Joel Hawksley*
* Fix bug where `ViewComponent::Compiler` wasn't required.
*Joel Hawksley*
## 2.67.0
* Use ViewComponent::Base.config as the internal endpoint for config.
*Simon Fish*
* Fix bug where `#with_request_url`, when used with query string, set the incorrect `request.path` and `request.fullpath`.
*Franz Liedke*
* Add link to [ViewComponentAttributes](https://github.com/amba-Health/view_component_attributes) in Resources section of docs.
*Romaric Pascal*
* `render_preview` test helper is available by default. It is no longer necessary to include `ViewComponent::RenderPreviewHelper`.
*Joel Hawksley*
## 2.66.0
* 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.
Before this change it was possible to define Slots with conflicting names, for example:
```ruby
class MyComponent < ViewComponent::Base
renders_one :item
renders_many :items
end
```
*Joel Hawksley*
## 2.64.0
* Add `warn_on_deprecated_slot_setter` flag to opt-in to deprecation warning.
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`.
To enable the coming deprecation warning, add `warn_on_deprecated_slot_setter`:
```ruby
class DeprecatedSlotsSetterComponent < ViewComponent::Base
warn_on_deprecated_slot_setter
end
```
*Joel Hawksley*
* Add [`m`](https://rubygems.org/gems/m) to development environment.
*Joel Hawksley*
* Fix potential deadlock scenario in the compiler's development mode.
*Blake Williams*
## 2.63.0
* Fixed typo in `renders_many` documentation.
*Graham Rogers*
* Add documentation about working with `turbo-rails`.
*Matheus Poli Camilo*
* Fix issue causing helper methods to not be available in nested components when the render monkey patch is disabled and `render_component` is used.
*Daniel Scheffknecht*
## 2.62.0
* Remove the experimental global output buffer feature.
* Restore functionality that used to attempt to compile templates on each call to `#render_in`.
* Un-pin `rails` `main` dependency.
*Cameron Dutro*
* Add blank space between "in" and "ViewComponent" in a deprecation warning.
*Vikram Dighe*
* Add HappyCo to list of companies using ViewComponent.
*Josh Clayton*
* Add predicate method support to polymorphic slots.
*Graham Rogers*
## 2.61.1
* Revert `Expose Capybara DSL methods directly inside tests.` This change unintentionally broke other Capybara methods and thus introduced a regression. We aren't confident that we can fail forward so we have decided to revert this change.
*Joel Hawksley, Blake Williams*
* Revert change making content evaluation consistent.
*Blake Williams*
* Pin `rails` `main` dependency due to incompatibility with Global Output Buffer.
*Joel Hawksley*
## 2.61.0
* Ensure side-effects in `content` are consistently evaluated before components are rendered. This change effectively means that `content` is evaluated for every component render where `render?` returns true. As a result, code that is passed to a component via a block/content will now always be evaluated, before `#call`, which can reveal bugs in existing components.
*Blake Williams*
## 2.60.0
* Add support for `render_preview` in RSpec tests.
*Thomas Hutterer*
## 2.59.0
* Expose Capybara DSL methods directly inside tests.
The following Capybara methods are now available directly without having to use the `page` method:
* [`all`](https://rubydoc.info/github/teamcapybara/capybara/Capybara%2FNode%2FFinders:all)
* [`first`](https://rubydoc.info/github/teamcapybara/capybara/Capybara%2FNode%2FFinders:first)
* [`text`](https://rubydoc.info/github/teamcapybara/capybara/Capybara%2FNode%2FSimple:text)
* [`find`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FFinders:find)
* [`find_all`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FFinders:find_all)
* [`find_button`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FFinders:find_button)
* [`find_by_id`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FFinders:find_by_id)
* [`find_field`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FFinders:find_field)
* [`find_link`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FFinders:find_link)
* [`has_content?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_content%3F)
* [`has_text?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_text%3F)
* [`has_css?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_css%3F)
* [`has_no_content?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_no_content%3F)
* [`has_no_text?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_no_text%3F)
* [`has_no_css?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_no_css%3F)
* [`has_no_xpath?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_no_xpath%3F)
* [`has_xpath?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_xpath%3F)
* [`has_link?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_link%3F)
* [`has_no_link?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_no_link%3F)
* [`has_button?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_button%3F)
* [`has_no_button?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_no_button%3F)
* [`has_field?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_field%3F)
* [`has_no_field?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_no_field%3F)
* [`has_checked_field?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_checked_field%3F)
* [`has_unchecked_field?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_unchecked_field%3F)
* [`has_no_table?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_no_table%3F)
* [`has_table?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_table%3F)
* [`has_select?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_select%3F)
* [`has_no_select?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_no_select%3F)
* [`has_selector?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_selector%3F)
* [`has_no_selector?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_no_selector%3F)
* [`has_no_checked_field?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_no_checked_field%3F)
* [`has_no_unchecked_field?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_no_unchecked_field%3F)
* Add support for `within*` Capybara DLS methods:
* [`within`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FSession:within)
* [`within_element`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FSession:within)
* [`within_fieldset`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FSession:within_fieldset)
* [`within_table`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FSession:within_table)
*Jacob Carlborg*
## 2.58.0
* Switch to `standardrb`.
*Joel Hawksley*
* Add BootrAils article to resources.
*Joel Hawksley*
* Add @boardfish and @spone as maintainers.
*Joel Hawksley, Cameron Dutro, Blake Williams*
* Re-compile updated, inherited templates when class caching is disabled.
*Patrick Arnett*
* Add the latest version to the docs index.
* Improve the docs: add the versions various features were introduced in.
*Hans Lemuet*
* Update docs to reflect lack of block content support in controllers.
*Joel Hawksley*
* Prevent adding duplicates to `autoload_paths`.
*Thomas Hutterer*
* Add FreeAgent to list of companies using ViewComponent.
*Simon Fish*
* Include polymorphic slots in `ViewComponent::Base` by default.
*Cameron Dutro*
* Add per-component config option for stripping newlines from templates before compilation.
*Cameron Dutro*
* Add link to article by Matouš Borák.
*Joel Hawksley*
## 2.57.1
* Fix issue causing `NoMethodError`s when calling helper methods from components rendered as part of a collection.
* Fix syntax error in the ERB example in the polymorphic slots docs.
*Cameron Dutro*
## 2.57.0
* Add missing `require` for `Translatable` module in `Base`.
*Hans Lemuet*
* Allow anything that responds to `#render_in` to be rendered in the parent component's view context.
*Cameron Dutro*
* Fix script/release so it honors semver.
*Cameron Dutro*
## 2.56.2
* Restore removed `rendered_component`, marking it for deprecation in v3.0.0.
*Tyson Gach, Richard Macklin, Joel Hawksley*
## 2.56.1
* Rename private accessor `rendered_component` to `rendered_content`.
*Yoshiyuki Hirano, Simon Dawson*
## 2.56.0
* Introduce experimental `render_preview` test helper. Note: `@rendered_component` in `TestHelpers` has been renamed to `@rendered_content`.
*Joel Hawksley*
* Move framework tests into sandbox application.
*Joel Hawksley*
* Add G2 to list of companies that use ViewComponent.
*Jack Shuff*
* Add Within3 to list of companies that use ViewComponent.
*Drew Bragg*
* Add Mission Met to list of companies that use ViewComponent.
*Nick Smith*
* Fix `#with_request_url` test helper not parsing nested query parameters into nested hashes.
*Richard Marbach*
## 2.55.0
* Add `render_parent` convenience method to avoid confusion between `<%= super %>` and `<% super %>` in template code.
*Cameron Dutro*
* Add note about discouraging inheritance.
*Joel Hawksley*
* Clean up grammar in documentation.
*Joel Hawksley*
* The ViewComponent team at GitHub is hiring! We're looking for a Rails engineer with accessibility experience: [https://boards.greenhouse.io/github/jobs/4020166](https://boards.greenhouse.io/github/jobs/4020166). Reach out to joelhawksley@github.com with any questions!
* The ViewComponent team is hosting a happy hour at RailsConf. Join us for snacks, drinks, and stickers: [https://www.eventbrite.com/e/viewcomponent-happy-hour-tickets-304168585427](https://www.eventbrite.com/e/viewcomponent-happy-hour-tickets-304168585427)
## 2.54.1
* Update docs dependencies.
*Joel Hawksley*
* Resolve warning in slots API.
* Raise in the test environment when ViewComponent code emits a warning.
*Blake Williams*
## 2.54.0
* Add `with_*` slot API for defining slots. Note: we plan to deprecate the non `with_*` API for slots in an upcoming release.
*Blake Williams*
* Add QuickNode to list of companies that use ViewComponent.
*Luc Castera*
* Include the `Translatable` module by default.
*Elia Schito*
* Update docs dependencies.
*Joel Hawksley*
## 2.53.0
* Add support for relative I18n scopes to translations.
*Elia Schito*
* Update CI configuration to use latest Rails 7.0.
*Hans Lemuet*
* Document how to use blocks with lambda slots.
*Sam Partington*
* Skip Rails 5.2 in local test environment if using incompatible Ruby version.
*Cameron Dutro, Blake Williams, Joel Hawksley*
* Improve landing page documentation.
*Jason Swett*
* Add Bearer to list of companies that use ViewComponent.
*Yaroslav Shmarov*
* Add articles to resources page.
*Joel Hawksley*
* Enable rendering arbitrary block contents in the view context in tests.
*Cameron Dutro*
## 2.52.0
* Add ADR for separate slot getter/setter API.
*Blake Williams*
* Add the option to use a "global" output buffer so `form_for` and friends can be used with view components.
*Cameron Dutro, Blake Williams*
* Fix fragment caching in partials when global output buffer is enabled.
* Fix template inheritance when eager loading is disabled.
*Cameron Dutro*
## 2.51.0
* Update the docs only when releasing a new version.
*Hans Lemuet*
* Alphabetize companies using ViewComponent and add Brightline to the list.
*Jack Schuss*
* Add CMYK value for ViewComponent Red color on logo page.
*Dylan Smith*
* Improve performance by moving template compilation from `#render_in` to `#render_template_for`.
*Cameron Dutro*
## 2.50.0
* Add tests for `layout` usage when rendering via controller.
*Felipe Sateler*
* Support returning Arrays from i18n files, and support marking them as HTML-safe translations.
*foca*
* Add Cometeer and Framework to users list.
*Elia Schito*
* Update Microsoft Vale styles.
*Simon Fish*
* Fix example in testing guide for how to setup default Rails tests.
*Steven Hansen*
* Update benchmark script to render multiple components/partials instead of a single instance per-run.
*Blake Williams*
* Add predicate methods `#{slot_name}?` to slots.
*Hans Lemuet*
* Use a dedicated deprecation instance, silence it while testing.
*Max Beizer, Hans Lemuet, Elia Schito*
* Fix Ruby warnings.
*Hans Lemuet*
* Place all generator options under `config.generate` namespace.
*Simon Fish*
* Allow preview generator to use provided component attributes.
* Add config option `config.view_component.generate.preview` to enable project-wide preview generation.
* Ensure all generated `.rb` files include `# frozen_string_literal: true` statement.
*Bob Maerten*
* Add Shogun to users list.
*Bernie Chiu*
## 2.49.1
* Patch XSS vulnerability in `ViewComponent::Translatable` module caused by improperly escaped interpolation arguments.
*Cameron Dutro*
## 2.49.0
* Fix path handling for evaluated view components that broke in Ruby 3.1.
*Adam Hess*
* Fix support for the `default:` option for a global translation.
*Elia Schito*
* Ensure i18n scope is a symbol to protect lookups.
*Simon Fish*
* Small update to preview docs to include rspec mention.
*Leigh Halliday*
* Small improvements to collection iteration docs.
*Brian O'Rourke*
* Add good and bad examples to `ViewComponents in practice`.
*Joel Hawksley*
* Add Ruby 3.1 and Rails 7.0 to CI.
*Peter Goldstein*
* Move preview logic to module for easier app integration.
*Sammy Henningsson*
## 2.48.0
* Correct path in example test command in Contributing docs.
*Mark Wilkinson*
* Update link to GOV.UK Components library in the resources list.
*Peter Yates*
* Add Lookbook to Resources docs page.
*Mark Perkins*
* Add blocking compiler mode for use in Rails development and testing modes, improving thread safety.
*Horia Radu*
* Add generators to support `tailwindcss-rails`.
*Dino Maric, Hans Lemuet*
* Add a namespaced component example to docs.
*Hans Lemuet*
* Setup `Appraisal` to add flexibility when testing ViewComponent against multiple Rails versions.
*Hans Lemuet*
* Return correct values for `request.path` and `request.query_string` methods when using the `with_request_url` test helper.
*Vasiliy Matyushin*
* Improve style in generators docs.
*Hans Lemuet*
* Correctly type Ruby version strings and update Rails versions used in CI configuration.
*Hans Lemuet*
* Make `ViewComponent::Collection` act like a collection of view components.
*Sammy Henningsson*
* Update `@param` of `#render_inline` to include `ViewComponent::Collection`.
*Yutaka Kamei*
* Add Wecasa to users list.
*Mohamed Ziata*
## 2.47.0
* Display preview source on previews that exclusively use templates.
*Edwin Mak*
* Add a test to ensure trailing newlines are stripped when rendering with `#render_in`.
*Simon Fish*
* Add WEBrick as a depenency to the application.
*Connor McQuillan*
* Update Ruby version in `.tool-versions`.
*Connor McQuillan*
* Add a test to ensure blocks can be passed into lambda slots without the need for any other arguments.
*Simon Fish*
* Add linters for file consistency.
*Simon Fish*
* Add @boardfish to docs/index.md and sort contributors.
*Simon Fish*
* Set up Codespaces for bug replication.
*Simon Fish*
* Add instructions for replicating bugs and failures.
*Simon Fish*
* Make @boardfish a committer.
*Joel Hawksley*
* Validate collection parameter with Active Model attribute names.
*Simon Fish*
* Fix `helpers` not working with component slots when rendered more than 2 component levels deep.
*Blake Williams*
* Update ruby to the latest versions
*Pedro Paiva*
* Fix `vale` linter config options.
*Hans Lemuet*
* Improve Contributing docs to include how to run tests for a specific version on Rails.
*Hans Lemuet*
* Add failing test for default form builder and documentation around known issue.
*Simon Fish*
* Add `--locale` flag to the component generator. Generates as many locale files as defined in `I18n.available_locales`, alongside the component.
* Add config option `config.view_component.generate_locale` to enable project-wide locale generation.
* Add config option `config.view_component.generate_distinct_locale_files` to enable project-wide per-locale translations file generation.
*Bob Maerten*
* Add config option `config.view_component.generate_sidecar` to always generate in the sidecar directory.
*Gleydson Tavares*
## 2.46.0
* Add thread safety to the compiler.
*Horia Radu*
* Add theme-specific logo images to readme.
*Dylan Smith*
* Add Orbit to users list.
*Nicolas Goutay*
* Increase clarity around purpose and use of slots.
*Simon Fish*
* Deprecate loading `view_component/engine` directly.
**Upgrade notice**: You should update your `Gemfile` like this:
```diff
- gem "view_component", require: "view_component/engine"`
+ gem "view_component"
```
*Yoshiyuki Hirano*
## 2.45.0
* Remove internal APIs from API documentation, fix link to license.
*Joel Hawksley*
* Add @yhirano55 to triage team.
*Joel Hawksley*
* Correct a typo in the sample slots code.
*Simon Fish*
* Add note about `allowed_queries`.
*Joel Hawksley*
* Add `vale` content linter.
*Joel Hawksley*
* Remove `require "rails/generators/test_case"` in generator tests.
*Yoshiyuki Hirano*
* Suppress zeitwerk warning about circular require.
*Yoshiyuki Hirano*
* Move `test_unit_generator_test.rb` from `test/view_component/` to `test/generators/`.
*Yoshiyuki Hirano*
* Unify test code of `TestUnitGeneratorTest` with the other generators tests.
*Yoshiyuki Hirano*
## 2.44.0
* Rename internal accessor to use private naming.
*Joel Hawksley, Blake Williams, Cameron Dutro*
* Add Github repo link to docs website header.
*Hans Lemuet*
* Change logo in README for dark theme readability.
*Dylan Smith*
* Add Litmus to users list.
*Dylan Smith*
* Add @dylanatsmith as codeowner of the ViewComponent logo and member of committers team.
*Joel Hawksley*
* Autoload `CompileCache`, which is optionally called in `engine.rb`.
*Gregory Igelmund*
* Move frequently asked questions to other pages, add History page.
*Joel Hawksley*
* Fix typo.
*James Hart*
* Add `require "method_source"` if it options.show_previews_source is enabled.
*Yoshiyuki Hirano*
* Move show_previews_source definition to Previewable.
*Yoshiyuki Hirano*
* Clear cache in MethodSource to apply the change odf preview code without app server restart.
*Yoshiyuki Hirano*
## 2.43.1
* Remove unnecessary call to `ruby2_keywords` for polymorphic slot getters.
*Cameron Dutro*
## 2.43.0
* Add note about tests and instance methods.
*Joel Hawksley*
* Flesh out `ViewComponents in practice`.
*Joel Hawksley*
* Add CODEOWNERS entries for feature areas owned by community committers.
*Joel Hawksley*
* Separate lint and CI workflows.
*Blake Williams*
* Add support for `image_path` helper in previews.
*Tobias Ahlin, Joel Hawksley*
* Add section to docs listing users of ViewComponent. Please submit a PR to add your team to the list!
*Joel Hawksley*
* Fix loading issue with Stimulus generator and add specs for Stimulus generator.
*Peter Sumskas*
* Remove dependency on `ActionDispatch::Static` in Rails middleware stack when enabling statics assets for source code preview.
*Gregory Igelmund*
* Require `view_component/engine` automatically.
*Cameron Dutro*
## 2.42.0
* Add logo files and page to docs.
*Dylan Smith*
* Add `ViewComponents in practice` documentation.
*Joel Hawksley*
* Fix bug where calling lambda slots without arguments would break in Ruby < 2.7.
*Manuel Puyol*
* Improve Stimulus controller template to import from `stimulus` or `@hotwired/stimulus`.
*Mario Schüttel*
* Fix bug where `helpers` would instantiate and use a new `view_context` in each component.
*Blake Williams, Ian C. Anderson*
* Implement polymorphic slots as experimental feature. See the Slots documentation to learn more.
*Cameron Dutro*
## 2.41.0
* Add `sprockets-rails` development dependency to fix test suite failures when using rails@main.
*Blake Williams*
* Fix Ruby indentation warning.
*Blake Williams*
* Add `--parent` generator option to specify the parent class.
* Add config option `config.view_component.component_parent_class` to change it project-wide.
*Hans Lemuet*
* Update docs to add example for using Devise helpers in tests.
*Matthew Rider*
* Fix bug where `with_collection_parameter` didn't inherit from parent component.
*Will Drexler, Christian Campoli*
* Allow query parameters in `with_request_url` test helper.
*Javi Martín*
* Add "how to render a component to a string" to FAQ.
*Hans Lemuet*
* Add `#render_in` to API docs.
*Hans Lemuet*
* Forward keyword arguments from slot wrapper to component instance using ruby2_keywords.
*Cameron Dutro*
## 2.40.0
* Replace antipatterns section in the documentation with best practices.
*Blake Williams*
* Add components to `rails stats` task.
*Nicolas Brousse*
* Fix bug when using Slim and writing a slot whose block evaluates to `nil`.
*Yousuf Jukaku*
* Add documentation for test helpers.
*Joel Hawksley*
## 2.39.0
* Clarify documentation of `with_variant` as an override of Action Pack.
*Blake Williams, Cameron Dutro, Joel Hawksley*
* Update docs page to be called Javascript and CSS, rename Building ViewComponents to Guide.
*Joel Hawksley*
* Deprecate `Base#with_variant`.
*Cameron Dutro*
* Error out in the CI if docs/api.md has to be regenerated.
*Dany Marcoux*
## 2.38.0
* Add `--stimulus` flag to the component generator. Generates a Stimulus controller alongside the component.
* Add config option `config.view_component.generate_stimulus_controller` to always generate a Stimulus controller.
*Sebastien Auriault*
## 2.37.0
* Clarify slots example in docs to reduce naming confusion.
*Joel Hawksley, Blake Williams*
* Fix error in documentation for `render_many` passthrough slots.
*Ollie Nye*
* Add test case for conflict with internal `@variant` variable.
*David Backeus*
* Document decision to not change naming convention recommendation to remove `-Component` suffix.
*Joel Hawksley*
* Fix typo in documentation.
*Ryo.gift*
* Add inline template example to benchmark script.
*Andrew Tait*
* Fix benchmark scripts.
*Andrew Tait*
* Run benchmarks in CI.
*Joel Hawksley*
## 2.36.0
* Add `slot_type` helper method.
*Jon Palmer*
* Add test case for rendering a ViewComponent with slots in a controller.
*Simon Fish*
* Add example ViewComponent to documentation landing page.
*Joel Hawksley*
* Set maximum line length to 120.
*Joel Hawksley*
* Setting a collection slot with the plural setter (`component.items(array)` for `renders_many :items`) returns the array of slots.
*Jon Palmer*
* Update error messages to be more descriptive and helpful.
*Joel Hawksley*
* Raise an error if the slot name for renders_many is :contents
*Simon Fish*
## 2.35.0
* Only load assets for Preview source highlighting if previews are enabled.
*Joel Hawksley*
* Fix references to moved documentation files.
*Richard Macklin*
* Ensure consistent indentation with Rubocop.
*Joel Hawksley*
* Bump `activesupport` upper bound from `< 7.0` to `< 8.0`.
*Richard Macklin*
* Add ERB Lint for a few basic rules.
*Joel Hawksley*
* Sort `gemspec` dependencies alphabetically.
*Joel Hawksley*
* Lock `method_source` at `1.0` to avoid open-ended dependency.
*Joel Hawksley*
* Require all PRs to include changelog entries.
*Joel Hawksley*
* Rename test app and move files under /test/sandbox.
*Matt-Yorkley*
* Make view_component_path config option available on ViewComponent::Base.
*Matt-Yorkley*
* Add @boardfish to Triage.
*Joel Hawksley*
* Adds support to change default components path (app/components) with `config.view_component.view_component_path`.
*lfalcao*
* Rename private instance variables (such as @variant) to reduce potential conflicts with subclasses.
*Joel Hawksley*
* Add documentation for configuration options.
*Joel Hawksley*
* Add view helper `preview_source` for rendering a source code preview below previews.
* Add config option `config.view_component.show_previews_source` for enabling the source preview.
*Johannes Engl*
* Add documentation for compatibility with ActionText.
*Jared Planter*
## 2.34.0
* Add the ability to enable ActiveSupport notifications (`!render.view_component` event) with `config.view_component.instrumentation_enabled`.
*Svyatoslav Kryukov*
* Add [Generators](https://viewcomponent.org/guide/generators.html) page to documentation.
*Hans Lemuet*
* Fix bug where ViewComponents didn't work in ActionMailers.
*dark-panda*
## 2.33.0
* Add support for `_iteration` parameter when rendering in a collection
*Will Cosgrove*
* Don't raise an error when rendering empty components.
*Alex Robbin*
## 2.32.0
* Enable previews by default in test environment.
*Edouard Piron*
* Fix test helper compatibility with Rails 7.0, TestRequest, and TestSession.
*Leo Correa*
* Add experimental `_output_postamble` lifecyle method.
*Joel Hawksley*
* Add compatibility notes on FAQ.
*Matheus Richard*
* Add Bridgetown on Compatibility documentation.
*Matheus Richard*
* Are you interested in building the future of ViewComponent? GitHub is looking to hire a Senior Engineer to work on Primer ViewComponents and ViewComponent. Apply here: [US/Canada](https://github.com/careers) / [Europe](https://boards.greenhouse.io/github/jobs/3132294). Feel free to reach out to joelhawksley@github.com with any questions.
*Joel Hawksley*
## 2.31.2
* Patch XSS vulnerability in `ViewComponent::Translatable` module caused by improperly escaped interpolation arguments.
*Cameron Dutro*
## 2.31.1
* Fix `DEPRECATION WARNING: before_render_check` when compiling `ViewComponent::Base`
*Dave Kroondyk*
## 2.31.0
_Note: This release includes an underlying change to Slots that may affect incorrect usage of the API, where Slots were set on a line prefixed by `<%=`. The result of setting a Slot shouldn't be returned. (`<%`)_
* Add `#with_content` to allow setting content without a block.
*Jordan Raine, Manuel Puyol*
* Add `with_request_url` test helper.
*Mario Schüttel*
* Improve feature parity with Rails translations
* Don't create a translation back end if the component has no translation file
* Mark translation keys ending with `html` as HTML-safe
* Always convert keys to String
* Support multiple keys
*Elia Schito*
* Fix errors on `asset_url` helpers when `asset_host` has no protocol.
*Elia Schito*
* Prevent slots from overriding the `#content` method when registering a slot with that name.
*Blake Williams*
* Deprecate `with_slot` in favor of the new [slots API](https://viewcomponent.org/guide/slots.html).
*Manuel Puyol*
## 2.30.0
* Deprecate `with_content_areas` in favor of [slots](https://viewcomponent.org/guide/slots.html).
*Joel Hawksley*
## 2.29.0
* Allow Slot lambdas to share data from the parent component and allow chaining on the returned component.
*Sjors Baltus, Blake Williams*
* Experimental: Add `ViewComponent::Translatable`
* `t` and `translate` now will look first into the sidecar YAML translations file.
* `helpers.t` and `I18n.t` still reference the global Rails translation files.
* `l` and `localize` will still reference the global Rails translation files.
*Elia Schito*
* Fix rendering output of pass through slots when using HAML.
*Alex Robbin, Blake Williams*
* Experimental: call `._sidecar_files` to fetch the sidecar files for a given list of extensions, for example passing `["yml", "yaml"]`.
*Elia Schito*
* Fix bug where a single `jbuilder` template matched multiple template handlers.
*Niels Slot*
## 2.28.0
* Include SlotableV2 by default in Base. **Note:** It's no longer necessary to include `ViewComponent::SlotableV2` to use Slots.
*Joel Hawksley*
* Prepend Preview routes instead of appending, accounting for cases where host application has catchall route.
*Joel Hawksley*
* Fix bug where blocks passed to lambda slots will render incorrectly in certain situations.
*Blake Williams*
## 2.27.0
* Allow customization of the controller used in component tests.
*Alex Robbin*
* Generate preview at overridden path if one exists when using `--preview` flag.
*Nishiki Liu*
## 2.26.1
* Fix bug that raises when trying to use a collection before the component has been compiled.
*Blake Williams*
## 2.26.0
* Delay evaluating component `content` in `render?`, preventing the `content` block from being evaluated when `render?` returns false.
*Blake Williams*
* Don't generate template when using `--inline` flag.
*Hans Lemuet*
* Add `--inline` option to the Haml and Slim generators
*Hans Lemuet*
## 2.25.1
* Experimental: call `._after_compile` class method after a component is compiled.
*Joel Hawksley*
* Fix bug where SlotV2 was rendered as an HTML string when using Slim.
*Manuel Puyol*
## 2.25.0
* Add `--preview` generator option to create an associated preview file.
*Bob Maerten*
* Add argument validation to avoid `content` override.
*Manuel Puyol*
## 2.24.0
* Add `--inline` option to the erb generator. Prevents default erb template from being created and creates a component with a call method.
*Nachiket Pusalkar*
* Add test case for checking presence of `content` in `#render?`.
*Joel Hawksley*
* Rename `master` branch to `main`.
*Joel Hawksley*
## 2.23.2
* Fix bug where rendering a component `with_collection` from a controller raised an error.
*Joel Hawksley*
## 2.23.1
* Fixed out-of-order rendering bug in `ActionView::SlotableV2`
*Blake Williams*
## 2.23.0
* Add `ActionView::SlotableV2`
* `with_slot` becomes `renders_one`.
* `with_slot collection: true` becomes `renders_many`.
* Slot definitions now accept either a component class, component class name, or a lambda instead of a `class_name:` keyword argument.
* Slots now support positional arguments.
* Slots no longer use the `content` attribute to render content, instead relying on `to_s`. for example `<%= my_slot %>`.
* Slot values are no longer set via the `slot` method, and instead use the name of the slot.
*Blake Williams*
* Add `frozen_string_literal: true` to generated component template.
*Max Beizer*
## 2.22.1
* Revert refactor that broke rendering for some users.
*Joel Hawksley*
## 2.22.0
* Add #with_variant to enable inline component variant rendering without template files.
*Nathan Jones*
## 2.21.0
* Only compile components at application initialization if eager loading is enabled.
*Joel Hawksley*
## 2.20.0
* Don't add `/test/components/previews` to preview_paths if directory doesn't exist.
*Andy Holland*
* Add `preview_controller` option to override the controller used for component previews.
*Matt Swanson, Blake Williams, Juan Manuel Ramallo*
## 2.19.1
* Check if `Rails.application` is loaded.
*Gleydson Tavares*
* Add documentation for webpack configuration when using Stimulus controllers.
*Ciprian Redinciuc*
## 2.19.0
* Extend documentation for using Stimulus within sidecar directories.
*Ciprian Redinciuc*
* Subclassed components inherit templates from parent.
*Blake Williams*
* Fix uninitialized constant error from `with_collection` when `eager_load` is disabled.
*Josh Gross*
## 2.18.2
* Raise an error if controller or view context is accessed during initialize as they're only available in render.
*Julian Nadeau*
* Collate test coverage across CI builds, ensuring 100% test coverage.
*Joel Hawksley*
## 2.18.1
* Fix bug where previews didn't work when monkey patch was disabled.
*Mixer Gutierrez*
## 2.18.0
* Fix auto loading of previews (changes no longer require a server restart)
*Matt Brictson*
* Add `default_preview_layout` configuration option to load custom app/views/layouts.
*Jared White, Juan Manuel Ramallo*
* Calculate virtual_path once for all instances of a component class to improve performance.
*Brad Parker*
## 2.17.1
* Fix bug where rendering Slot with empty block resulted in error.
*Joel Hawksley*
## 2.17.0
* Slots return stripped HTML, removing leading and trailing whitespace.
*Jason Long, Joel Hawksley*
## 2.16.0
* Add `--sidecar` option to the erb, haml and slim generators. Places the generated template in the sidecar directory.
*Michael van Rooijen*
## 2.15.0
* Add support for templates as ViewComponent::Preview examples.
*Juan Manuel Ramallo*
## 2.14.1
* Allow using `render_inline` in test when the render monkey patch is disabled with `config.view_component.render_monkey_patch_enabled = false` in versions of Rails < 6.1.
*Clément Joubert*
* Fix kwargs warnings in slotable.
Fixes:
```console
view_component/lib/view_component/slotable.rb:98: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
view_component/test/app/components/slots_component.rb:18: warning: The called method `initialize' is defined here
```
*Eileen M. Uchitelle*
## 2.14.0
* Add `config.preview_paths` to support multiple locations of component preview files. Deprecate `config.preview_path`.
*Tomas Celizna*
* Only print warning about a missing capybara dependency if the `DEBUG` environment variable is set.
*Richard Macklin*
## 2.13.0
* Add the ability to disable the render monkey patch with `config.view_component.render_monkey_patch_enabled`. In versions of Rails < 6.1, add `render_component` and `render_component_to_string` methods which can be used for rendering components instead of `render`.
*Johannes Engl*
## 2.12.0
* Implement Slots as potential successor to Content Areas.
*Jens Ljungblad, Brian Bugh, Jon Palmer, Joel Hawksley*
## 2.11.1
* Fix kwarg warnings in Ruby 2.7.
*Joel Hawksley*
## 2.11.0
* Ensure Rails configuration is available within components.
*Trevor Broaddus*
* Fix bug where global Rails helpers are inaccessible from nested components. Before, `helpers` was pointing to parent component.
*Franco Sebregondi*
## 2.10.0
* Raise an `ArgumentError` with a helpful message when Ruby can't parse a component class.
*Max Beizer*
## 2.9.0
* Cache components per-request in development, preventing unnecessary recompilation during a single request.
*Felipe Sateler*
## 2.8.0
* Add `before_render`, deprecating `before_render_check`.
*Joel Hawksley*
## 2.7.0
* Add `rendered_component` method to `ViewComponent::TestHelpers` which exposes the raw output of the rendered component.
*Richard Macklin*
* Support sidecar directories for views and other assets.
*Jon Palmer*
## 2.6.0
* Add `config.view_component.preview_route` to set the endpoint for component previews. By default `/rails/view_components` is used.
*Juan Manuel Ramallo*
* Raise error when initializer omits with_collection_parameter.
*Joel Hawksley*
## 2.5.1
* Compile component before rendering collection.
*Rainer Borene*
## v2.5.0
* Add counter variables when rendering collections.
*Frank S*
* Add the ability to access params from preview examples.
*Fabio Cantoni*
## v2.4.0
* Add `#render_to_string` support.
*Jarod Reid*
* Declare explicit dependency on `activesupport`.
*Richard Macklin*
* Remove `autoload`s of internal modules (`Previewable`, `RenderMonkeyPatch`, `RenderingMonkeyPatch`).
*Richard Macklin*
* Remove `capybara` dependency.
*Richard Macklin*
## v2.3.0
* Allow using inline render method(s) defined on a parent.
*Simon Rand*
* Fix bug where inline variant render methods would never be called.
*Simon Rand*
* ViewComponent preview index views use Rails internal layout instead of application's layout
*Juan Manuel Ramallo*
## v2.2.2
* Add `Base.format` for better compatibility with `ActionView::Template`.
*Joel Hawksley*
## v2.2.1
* Fix bug where template couldn't be found if `inherited` was redefined.
*Joel Hawksley*
## v2.2.0
* Add support for `config.action_view.annotate_template_file_names` (coming in Rails 6.1).
*Joel Hawksley*
* Remove initializer requirement from the component.
*Vasiliy Ermolovich*
## v2.1.0
* Support rendering collections (for example, `render(MyComponent.with_collection(@items))`).
*Tim Clem*
## v2.0.0
* Move to `ViewComponent` namespace, removing all references to `ActionView`.
* The gem name is now `view_component`.
* ViewComponent previews are now accessed at `/rails/view_components`.
* ViewComponents can _only_ be rendered with the instance syntax: `render(MyComponent.new)`. Support for all other syntaxes has been removed.
* ActiveModel::Validations have been removed. ViewComponent generators no longer include validations.
* In Rails 6.1, no monkey patching is used.
* `to_component_class` has been removed.
* All gem configuration is now in `config.view_component`.
## v1.17.0
* Support Ruby 2.4 in CI.
*Andrew Mason*
* ViewComponent generators don't not prompt for content requirement.
*Joel Hawksley*
* Add post-install message that gem has been renamed to `view_component`.
*Joel Hawksley*
## v1.16.0
* Add `refute_component_rendered` test helper.
*Joel Hawksley*
* Check for Rails before invocation.
*Dave Paola*
* Allow components to be rendered without a template file (aka inline component).
*Rainer Borene*
## v1.15.0
* Re-introduce ActionView::Component::TestHelpers.
*Joel Hawksley*
* Bypass monkey patch on Rails 6.1 builds.
*Joel Hawksley*
* Make `ActionView::Helpers::TagHelper` available in Previews.
```ruby
def with_html_content
render(MyComponent.new) do
tag.div do
content_tag(:span, "Hello")
end
end
end
```
*Sean Doyle*
## v1.14.1
* Fix bug where generator created invalid test code.
*Joel Hawksley*
## v1.14.0
* Rename ActionView::Component::Base to ViewComponent::Base
*Joel Hawksley*
## v1.13.0
* Allow components to be rendered inside controllers.
*Joel Hawksley*
* Improve backtraces from exceptions raised in templates.
*Blake Williams*
## v1.12.0
* Revert: Remove initializer requirement for Ruby 2.7+
*Joel Hawksley*
* Restructure Railtie into Engine
*Sean Doyle*
* Allow components to override before_render_check
*Joel Hawksley*
## v1.11.1
* Relax Capybara requirement.
*Joel Hawksley*
## v1.11.0
* Add support for Capybara matchers.
*Joel Hawksley*
* Add erb, haml, & slim template generators
*Asger Behncke Jacobsen*
## v1.10.0
* Deprecate all `render` syntaxes except for `render(MyComponent.new(foo: :bar))`
*Joel Hawksley*
## v1.9.0
* Remove initializer requirement for Ruby 2.7+
*Dylan Clark*
## v1.8.1
* Run validation checks before calling `#render?`.
*Ash Wilson*
## v1.8.0
* Remove the unneeded ComponentExamplesController and simplify preview rendering.
*Jon Palmer*
* Add `#render?` hook to allow components to be no-ops.
*Kyle Fox*
* Don't assume ApplicationController exists.
*Jon Palmer*
* Allow some additional checks to overrided render?
*Sergey Malykh*
* Fix generator placing namespaced components in the root directory.
*Asger Behncke Jacobsen*
* Fix cache test.
*Sergey Malykh*
## v1.7.0
* Simplify validation of templates and compilation.
*Jon Palmer*
* Add support for multiple content areas.
*Jon Palmer*
## v1.6.2
* Fix Uninitialized Constant error.
*Jon Palmer*
* Add basic github issue and PR templates.
*Dylan Clark*
* Update readme phrasing around previews.
*Justin Coyne*
## v1.6.1
* Allow Previews to have no layout.
*Jon Palmer*
* Bump rack from 2.0.7 to 2.0.8.
*Dependabot*
* Compile components on application boot when eager loading is enabled.
*Joel Hawksley*
* Previews support content blocks.
*Cesario Uy*
* Follow Rails conventions. (refactor)
*Rainer Borene*
* Fix edge case issue with extracting variants from less conventional source_locations.
*Ryan Workman*
## v1.6.0
* Avoid dropping elements in the render_inline test helper.
*@dark-panda*
* Add test for helpers.asset_url.
*Christopher Coleman*
* Add rudimentary compatibility with better_html.
*Joel Hawksley*
* Template-less variants fall back to default template.
*Asger Behncke Jacobsen, Cesario Uy*
* Generated tests use new naming convention.
*Simon Træls Ravn*
* Eliminate sqlite dependency.
*Simon Dawson*
* Add support for rendering components via #to_component_class
*Vinicius Stock*
## v1.5.3
* Add support for RSpec to generators.
*Dylan Clark, Ryan Workman*
* Require controllers as part of setting autoload paths.
*Joel Hawksley*
## v1.5.2
* Disable eager loading initializer.
*Kasper Meyer*
## v1.5.1
* Update railties class to work with Rails 6.
*Juan Manuel Ramallo*
## v1.5.0
Note: `actionview-component` is now loaded by requiring `actionview/component`, not `actionview/component/base`.
* Fix issue with generating component method signatures.
*Ryan Workman, Dylan Clark*
* Create component generator.
*Vinicius Stock*
* Add helpers proxy.
*Kasper Meyer*
* Introduce ActionView::Component::Previews.
*Juan Manuel Ramallo*
## v1.4.0
* Fix bug where components broke in application paths with periods.
*Anton, Joel Hawksley*
* Add support for `cache_if` in component templates.
*Aaron Patterson, Joel Hawksley*
* Add support for variants.
*Juan Manuel Ramallo*
* Fix bug in virtual path lookup.
*Juan Manuel Ramallo*
* Preselect the rendered component in render_inline.
*Elia Schito*
## v1.3.6
* Allow template file names without format.
*Joel Hawksley*
* Add support for translations.
*Juan Manuel Ramallo*
## v1.3.5
* Re-expose `controller` method.
*Michael Emhofer, Joel Hawksley*
* Gem version numbers are now accessible through `ActionView::Component::VERSION`
*Richard Macklin*
* Fix typo in README
*ars moriendi*
## v1.3.4
* Template errors surface correct file and line number.
*Justin Coyne*
* Allow access to `request` inside components.
*Joel Hawksley*
## v1.3.3
* Don't raise error when sidecar files that aren't templates exist.
*Joel Hawksley*
## v1.3.2
* Support rendering views from inside component templates.
*Patrick Sinclair*
## v1.3.1
* Fix bug where rendering nested content caused an error.
*Joel Hawksley, Aaron Patterson*
## v1.3.0
* Components are rendered with enough controller context to support rendering of partials and forms.
*Patrick Sinclair, Joel Hawksley, Aaron Patterson*
## v1.2.1
* `actionview-component` is now tested against Ruby 2.3/2.4 and Rails 5.0.0.
## v1.2.0
* The `render_component` test helper has been renamed to `render_inline`. `render_component` has been deprecated and will be removed in v2.0.0.
*Joel Hawksley*
* Components are now rendered with `render MyComponent, foo: :bar` syntax. The existing `render MyComponent.new(foo: :bar)` syntax has been deprecated and will be removed in v2.0.0.
*Joel Hawksley*
## v1.1.0
* Components now inherit from ActionView::Component::Base
*Joel Hawksley*
## v1.0.1
* Always recompile component templates outside production.
*Joel Hawksley, John Hawthorn*
## v1.0.0
This release extracts the `ActionView::Component` library from the GitHub application.
It will be published on RubyGems under the existing `actionview-component` gem name, as @chancancode has passed us ownership of the gem.
view_component-3.12.1/view_component.gemspec 0000644 0000041 0000041 00000021137 14624365352 021301 0 ustar www-data www-data #########################################################
# This file has been automatically generated by gem2tgz #
#########################################################
# -*- encoding: utf-8 -*-
# stub: view_component 3.12.1 ruby lib
Gem::Specification.new do |s|
s.name = "view_component".freeze
s.version = "3.12.1"
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
s.metadata = { "allowed_push_host" => "https://rubygems.org", "changelog_uri" => "https://github.com/ViewComponent/view_component/blob/main/docs/CHANGELOG.md", "source_code_uri" => "https://github.com/viewcomponent/view_component" } if s.respond_to? :metadata=
s.require_paths = ["lib".freeze]
s.authors = ["ViewComponent Team".freeze]
s.date = "2024-04-17"
s.files = ["LICENSE.txt".freeze, "README.md".freeze, "app/assets/vendor/prism.css".freeze, "app/assets/vendor/prism.min.js".freeze, "app/controllers/concerns/view_component/preview_actions.rb".freeze, "app/controllers/view_components_controller.rb".freeze, "app/controllers/view_components_system_test_controller.rb".freeze, "app/helpers/preview_helper.rb".freeze, "app/views/test_mailer/test_email.html.erb".freeze, "app/views/view_components/_preview_source.html.erb".freeze, "app/views/view_components/index.html.erb".freeze, "app/views/view_components/preview.html.erb".freeze, "app/views/view_components/previews.html.erb".freeze, "docs/CHANGELOG.md".freeze, "lib/rails/generators/abstract_generator.rb".freeze, "lib/rails/generators/component/USAGE".freeze, "lib/rails/generators/component/component_generator.rb".freeze, "lib/rails/generators/component/templates/component.rb.tt".freeze, "lib/rails/generators/erb/component_generator.rb".freeze, "lib/rails/generators/erb/templates/component.html.erb.tt".freeze, "lib/rails/generators/haml/component_generator.rb".freeze, "lib/rails/generators/haml/templates/component.html.haml.tt".freeze, "lib/rails/generators/locale/component_generator.rb".freeze, "lib/rails/generators/preview/component_generator.rb".freeze, "lib/rails/generators/preview/templates/component_preview.rb.tt".freeze, "lib/rails/generators/rspec/component_generator.rb".freeze, "lib/rails/generators/rspec/templates/component_spec.rb.tt".freeze, "lib/rails/generators/slim/component_generator.rb".freeze, "lib/rails/generators/slim/templates/component.html.slim.tt".freeze, "lib/rails/generators/stimulus/component_generator.rb".freeze, "lib/rails/generators/stimulus/templates/component_controller.js.tt".freeze, "lib/rails/generators/tailwindcss/component_generator.rb".freeze, "lib/rails/generators/tailwindcss/templates/component.html.erb.tt".freeze, "lib/rails/generators/test_unit/component_generator.rb".freeze, "lib/rails/generators/test_unit/templates/component_test.rb.tt".freeze, "lib/view_component.rb".freeze, "lib/view_component/base.rb".freeze, "lib/view_component/capture_compatibility.rb".freeze, "lib/view_component/collection.rb".freeze, "lib/view_component/compile_cache.rb".freeze, "lib/view_component/compiler.rb".freeze, "lib/view_component/component_error.rb".freeze, "lib/view_component/config.rb".freeze, "lib/view_component/deprecation.rb".freeze, "lib/view_component/docs_builder_component.html.erb".freeze, "lib/view_component/docs_builder_component.rb".freeze, "lib/view_component/engine.rb".freeze, "lib/view_component/errors.rb".freeze, "lib/view_component/inline_template.rb".freeze, "lib/view_component/instrumentation.rb".freeze, "lib/view_component/preview.rb".freeze, "lib/view_component/rails/tasks/view_component.rake".freeze, "lib/view_component/render_component_helper.rb".freeze, "lib/view_component/render_component_to_string_helper.rb".freeze, "lib/view_component/render_monkey_patch.rb".freeze, "lib/view_component/render_to_string_monkey_patch.rb".freeze, "lib/view_component/rendering_component_helper.rb".freeze, "lib/view_component/rendering_monkey_patch.rb".freeze, "lib/view_component/slot.rb".freeze, "lib/view_component/slotable.rb".freeze, "lib/view_component/system_test_case.rb".freeze, "lib/view_component/system_test_helpers.rb".freeze, "lib/view_component/test_case.rb".freeze, "lib/view_component/test_helpers.rb".freeze, "lib/view_component/translatable.rb".freeze, "lib/view_component/use_helpers.rb".freeze, "lib/view_component/version.rb".freeze, "lib/view_component/with_content_helper.rb".freeze, "lib/yard/mattr_accessor_handler.rb".freeze]
s.homepage = "https://viewcomponent.org".freeze
s.licenses = ["MIT".freeze]
s.required_ruby_version = Gem::Requirement.new(">= 2.7.0".freeze)
s.rubygems_version = "3.3.15".freeze
s.summary = "A framework for building reusable, testable & encapsulated view components in Ruby on Rails.".freeze
if s.respond_to? :specification_version then
s.specification_version = 4
end
if s.respond_to? :add_runtime_dependency then
s.add_runtime_dependency(%q.freeze, [">= 5.2.0", "< 8.0"])
s.add_development_dependency(%q.freeze, ["~> 2.4"])
s.add_development_dependency(%q.freeze, ["~> 2.13.0"])
s.add_development_dependency(%q.freeze, [">= 0"])
s.add_development_dependency(%q.freeze, ["~> 2"])
s.add_development_dependency(%q.freeze, ["~> 3"])
s.add_runtime_dependency(%q.freeze, ["~> 1.0"])
s.add_development_dependency(%q.freeze, ["~> 0.15"])
s.add_development_dependency(%q.freeze, [">= 0"])
s.add_development_dependency(%q.freeze, [">= 0"])
s.add_development_dependency(%q.freeze, ["~> 6"])
s.add_development_dependency(%q.freeze, ["~> 2"])
s.add_development_dependency(%q.freeze, ["~> 1"])
s.add_runtime_dependency(%q.freeze, ["~> 1.0"])
s.add_development_dependency(%q.freeze, ["~> 5.18"])
s.add_development_dependency(%q.freeze, [">= 0"])
s.add_development_dependency(%q.freeze, [">= 0"])
s.add_development_dependency(%q.freeze, [">= 0"])
s.add_development_dependency(%q.freeze, ["~> 0.13"])
s.add_development_dependency(%q.freeze, ["~> 6"])
s.add_development_dependency(%q.freeze, ["~> 13.0"])
s.add_development_dependency(%q.freeze, ["~> 5"])
s.add_development_dependency(%q.freeze, ["~> 1"])
s.add_development_dependency(%q.freeze, ["= 4.9.0"])
s.add_development_dependency(%q.freeze, ["~> 0.22.0"])
s.add_development_dependency(%q.freeze, ["~> 0.9.1"])
s.add_development_dependency(%q.freeze, ["~> 5.1"])
s.add_development_dependency(%q.freeze, ["~> 3.4.2"])
s.add_development_dependency(%q.freeze, ["~> 1"])
s.add_development_dependency(%q.freeze, [">= 0"])
s.add_development_dependency(%q.freeze, ["~> 0.9.34"])
s.add_development_dependency(%q.freeze, ["~> 0.0.1"])
else
s.add_dependency(%q.freeze, [">= 5.2.0", "< 8.0"])
s.add_dependency(%q.freeze, ["~> 2.4"])
s.add_dependency(%q.freeze, ["~> 2.13.0"])
s.add_dependency(%q.freeze, [">= 0"])
s.add_dependency(%q.freeze, ["~> 2"])
s.add_dependency(%q.freeze, ["~> 3"])
s.add_dependency(%q.freeze, ["~> 1.0"])
s.add_dependency(%q.freeze, ["~> 0.15"])
s.add_dependency(%q.freeze, [">= 0"])
s.add_dependency(%q.freeze, [">= 0"])
s.add_dependency(%q.freeze, ["~> 6"])
s.add_dependency(%q.freeze, ["~> 2"])
s.add_dependency(%q.freeze, ["~> 1"])
s.add_dependency(%q.freeze, ["~> 1.0"])
s.add_dependency(%q.freeze, ["~> 5.18"])
s.add_dependency(%q.freeze, [">= 0"])
s.add_dependency(%q.freeze, [">= 0"])
s.add_dependency(%q.freeze, [">= 0"])
s.add_dependency(%q.freeze, ["~> 0.13"])
s.add_dependency(%q.freeze, ["~> 6"])
s.add_dependency(%q.freeze, ["~> 13.0"])
s.add_dependency(%q.freeze, ["~> 5"])
s.add_dependency(%q.freeze, ["~> 1"])
s.add_dependency(%q.freeze, ["= 4.9.0"])
s.add_dependency(%q.freeze, ["~> 0.22.0"])
s.add_dependency(%q.freeze, ["~> 0.9.1"])
s.add_dependency(%q.freeze, ["~> 5.1"])
s.add_dependency(%q.freeze, ["~> 3.4.2"])
s.add_dependency(%q.freeze, ["~> 1"])
s.add_dependency(%q.freeze, [">= 0"])
s.add_dependency(%q.freeze, ["~> 0.9.34"])
s.add_dependency(%q.freeze, ["~> 0.0.1"])
end
end
view_component-3.12.1/app/ 0000755 0000041 0000041 00000000000 14624365352 015454 5 ustar www-data www-data view_component-3.12.1/app/views/ 0000755 0000041 0000041 00000000000 14624365352 016611 5 ustar www-data www-data view_component-3.12.1/app/views/view_components/ 0000755 0000041 0000041 00000000000 14624365352 022030 5 ustar www-data www-data view_component-3.12.1/app/views/view_components/index.html.erb 0000644 0000041 0000041 00000000542 14624365352 024575 0 ustar www-data www-data <% @previews.each do |preview| %>