Class: ActionView::Component::Base

Inherits:
Base
  • Object
show all
Includes:
Previews, ActiveModel::Validations, ActiveSupport::Configurable
Defined in:
lib/action_view/component/base.rb

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeBase

Returns a new instance of Base.



61
# File 'lib/action_view/component/base.rb', line 61

def initialize(*); end

Class Method Details

.call_method_name(variant) ⇒ Object



108
109
110
111
112
113
114
# File 'lib/action_view/component/base.rb', line 108

def call_method_name(variant)
  if variant.present? && variants.include?(variant)
    "call_#{variant}"
  else
    "call"
  end
end

.compileObject

Compile templates to instance methods, assuming they haven’t been compiled already. We could in theory do this on app boot, at least in production environments. Right now this just compiles the first time the component is rendered.



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/action_view/component/base.rb', line 132

def compile
  return if @compiled && ActionView::Base.cache_template_loading

  validate_templates

  templates.each do |template|
    class_eval "      def \#{call_method_name(template[:variant])}\n        @output_buffer = ActionView::OutputBuffer.new\n        \#{compiled_template(template[:path])}\n      end\n    RUBY\n  end\n\n  @compiled = true\nend\n", __FILE__, __LINE__ + 1

.identifierObject



158
159
160
# File 'lib/action_view/component/base.rb', line 158

def identifier
  source_location
end

.inherited(child) ⇒ Object



102
103
104
105
106
# File 'lib/action_view/component/base.rb', line 102

def inherited(child)
  child.include Rails.application.routes.url_helpers unless child < Rails.application.routes.url_helpers

  super
end

.source_locationObject

Raises:

  • (NotImplementedError)


116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/action_view/component/base.rb', line 116

def source_location
  # Require #initialize to be defined so that we can use
  # method#source_location to look up the file name
  # of the component.
  #
  # If we were able to only support Ruby 2.7+,
  # We could just use Module#const_source_location,
  # rendering this unnecessary.
  raise NotImplementedError.new("#{self} must implement #initialize.") unless self.instance_method(:initialize).owner == self

  instance_method(:initialize).source_location[0]
end

.typeObject

we’ll eventually want to update this to support other types



154
155
156
# File 'lib/action_view/component/base.rb', line 154

def type
  "text/html"
end

.variantsObject



149
150
151
# File 'lib/action_view/component/base.rb', line 149

def variants
  templates.map { |template| template[:variant] }
end

Instance Method Details

#controllerObject



71
72
73
# File 'lib/action_view/component/base.rb', line 71

def controller
  @controller ||= view_context.controller
end

#formatObject

:nodoc:



89
90
91
# File 'lib/action_view/component/base.rb', line 89

def format # :nodoc:
  @variant
end

#helpersObject

Provides a proxy to access helper methods through



76
77
78
# File 'lib/action_view/component/base.rb', line 76

def helpers
  @helpers ||= view_context
end

#render(options = {}, args = {}, &block) ⇒ Object



63
64
65
66
67
68
69
# File 'lib/action_view/component/base.rb', line 63

def render(options = {}, args = {}, &block)
  if options.is_a?(String) || (options.is_a?(Hash) && options.has_key?(:partial))
    view_context.render(options, args, &block)
  else
    super
  end
end

#render_in(view_context, *args, &block) ⇒ Object

Entrypoint for rendering components. Called by ActionView::Base#render.

view_context: ActionView context from calling view args(hash): params to be passed to component being rendered block: optional block to be captured within the view context

returns HTML that has been escaped by the respective template handler

Example subclass:

app/components/my_component.rb: class MyComponent < ActionView::Component::Base

def initialize(title:)
  @title = title
end

end

app/components/my_component.html.erb <span title=“<%= @title %>”>Hello, <%= content %>!</span>

In use: <%= render MyComponent, title: “greeting” do %>world<% end %> returns: <span title=“greeting”>Hello, world!</span>



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/action_view/component/base.rb', line 42

def render_in(view_context, *args, &block)
  self.class.compile
  @view_context = view_context
  @view_renderer ||= view_context.view_renderer
  @lookup_context ||= view_context.lookup_context
  @view_flow ||= view_context.view_flow
  @virtual_path ||= virtual_path
  @variant = @lookup_context.variants.first
  old_current_template = @current_template
  @current_template = self

  @content = view_context.capture(&block) if block_given?
  validate!

  send(self.class.call_method_name(@variant))
ensure
  @current_template = old_current_template
end

#view_cache_dependenciesObject



85
86
87
# File 'lib/action_view/component/base.rb', line 85

def view_cache_dependencies
  []
end

#virtual_pathObject

Removes the first part of the path and the extension.



81
82
83
# File 'lib/action_view/component/base.rb', line 81

def virtual_path
  self.class.source_location.gsub(%r{(.*app/components)|(\.rb)}, "")
end