Class: ViewComponent::Base
- Inherits:
-
ActionView::Base
- Object
- ActionView::Base
- ViewComponent::Base
- Includes:
- ActiveSupport::Configurable, Previewable
- Defined in:
- lib/view_component/base.rb
Class Attribute Summary collapse
-
.source_location ⇒ Object
Returns the value of attribute source_location.
Class Method Summary collapse
- .call_method_name(variant) ⇒ Object
- .collection_parameter_name ⇒ Object
-
.compile(raise_template_errors: false) ⇒ Object
Compile templates to instance methods, assuming they haven’t been compiled already.
- .compile! ⇒ Object
- .compiled? ⇒ Boolean
- .format ⇒ Object
- .identifier ⇒ Object
- .inherited(child) ⇒ Object
- .short_identifier ⇒ Object
-
.type ⇒ Object
we’ll eventually want to update this to support other types.
-
.with_collection(*args) ⇒ Object
Render a component collection.
-
.with_collection_parameter(param) ⇒ Object
Support overriding this component’s collection parameter name.
- .with_content_areas(*areas) ⇒ Object
Instance Method Summary collapse
- #before_render_check ⇒ Object
- #controller ⇒ Object
-
#format ⇒ Object
:nodoc:.
-
#helpers ⇒ Object
Provides a proxy to access helper methods through.
-
#initialize ⇒ Base
constructor
A new instance of Base.
- #render(options = {}, args = {}, &block) ⇒ Object
- #render? ⇒ Boolean
-
#render_in(view_context, &block) ⇒ Object
Entrypoint for rendering components.
- #view_cache_dependencies ⇒ Object
-
#virtual_path ⇒ Object
Removes the first part of the path and the extension.
- #with(area, content = nil, &block) ⇒ Object
Constructor Details
#initialize ⇒ Base
Returns a new instance of Base.
84 |
# File 'lib/view_component/base.rb', line 84 def initialize(*); end |
Class Attribute Details
.source_location ⇒ Object
Returns the value of attribute source_location.
144 145 146 |
# File 'lib/view_component/base.rb', line 144 def source_location @source_location end |
Class Method Details
.call_method_name(variant) ⇒ Object
159 160 161 162 163 164 165 |
# File 'lib/view_component/base.rb', line 159 def call_method_name(variant) if variant.present? && variants.include?(variant) "call_#{variant}" else "call" end end |
.collection_parameter_name ⇒ Object
240 241 242 |
# File 'lib/view_component/base.rb', line 240 def collection_parameter_name (@with_collection_parameter || name.demodulize.underscore.chomp("_component")).to_sym end |
.compile(raise_template_errors: false) ⇒ Object
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.
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
# File 'lib/view_component/base.rb', line 178 def compile(raise_template_errors: false) return if compiled? if template_errors.present? raise ViewComponent::TemplateError.new(template_errors) if raise_template_errors return false end define_singleton_method(:variants) do templates.map { |template| template[:variant] } + variants_from_inline_calls(inline_calls) end # If template name annotations are turned on, a line is dynamically # added with a comment. In this case, we want to return a different # starting line number so errors that are raised will point to the # correct line in the component template. line_number = if ActionView::Base.respond_to?(:annotate_template_file_names) && ActionView::Base.annotate_template_file_names -2 else -1 end templates.each do |template| class_eval <<-RUBY, template[:path], line_number def #{call_method_name(template[:variant])} @output_buffer = ActionView::OutputBuffer.new #{compiled_template(template[:path])} end RUBY end @compiled = true end |
.compile! ⇒ Object
171 172 173 |
# File 'lib/view_component/base.rb', line 171 def compile! compile(raise_template_errors: true) end |
.compiled? ⇒ Boolean
167 168 169 |
# File 'lib/view_component/base.rb', line 167 def compiled? @compiled && ActionView::Base.cache_template_loading end |
.format ⇒ Object
219 220 221 |
# File 'lib/view_component/base.rb', line 219 def format :html end |
.identifier ⇒ Object
223 224 225 |
# File 'lib/view_component/base.rb', line 223 def identifier source_location end |
.inherited(child) ⇒ Object
146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/view_component/base.rb', line 146 def inherited(child) if defined?(Rails) child.include Rails.application.routes.url_helpers unless child < 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. child.source_location = caller_locations(1, 10).reject { |l| l.label == "inherited" }[0].absolute_path super end |
.short_identifier ⇒ Object
80 81 82 |
# File 'lib/view_component/base.rb', line 80 def self.short_identifier @short_identifier ||= defined?(Rails.root) ? source_location.sub("#{Rails.root}/", "") : source_location end |
.type ⇒ Object
we’ll eventually want to update this to support other types
215 216 217 |
# File 'lib/view_component/base.rb', line 215 def type "text/html" end |
.with_collection(*args) ⇒ Object
Render a component collection.
19 20 21 |
# File 'lib/view_component/base.rb', line 19 def self.with_collection(*args) Collection.new(self, *args) end |
.with_collection_parameter(param) ⇒ Object
Support overriding this component’s collection parameter name
236 237 238 |
# File 'lib/view_component/base.rb', line 236 def with_collection_parameter(param) @with_collection_parameter = param end |
.with_content_areas(*areas) ⇒ Object
227 228 229 230 231 232 233 |
# File 'lib/view_component/base.rb', line 227 def with_content_areas(*areas) if areas.include?(:content) raise ArgumentError.new ":content is a reserved content area name. Please use another name, such as ':body'" end attr_reader *areas self.content_areas = areas end |
Instance Method Details
#before_render_check ⇒ Object
72 73 74 |
# File 'lib/view_component/base.rb', line 72 def before_render_check # noop end |
#controller ⇒ Object
94 95 96 |
# File 'lib/view_component/base.rb', line 94 def controller @controller ||= view_context.controller end |
#format ⇒ Object
:nodoc:
112 113 114 |
# File 'lib/view_component/base.rb', line 112 def format # :nodoc: @variant end |
#helpers ⇒ Object
Provides a proxy to access helper methods through
99 100 101 |
# File 'lib/view_component/base.rb', line 99 def helpers @helpers ||= view_context end |
#render(options = {}, args = {}, &block) ⇒ Object
86 87 88 89 90 91 92 |
# File 'lib/view_component/base.rb', line 86 def render( = {}, args = {}, &block) if .is_a?(String) || (.is_a?(Hash) && .has_key?(:partial)) view_context.render(, args, &block) else super end end |
#render? ⇒ Boolean
76 77 78 |
# File 'lib/view_component/base.rb', line 76 def render? true end |
#render_in(view_context, &block) ⇒ Object
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
Example subclass:
app/components/my_component.rb: class MyComponent < ViewComponent::Base
def initialize(title:)
@title = title
end
end
app/components/my_component.html.erb <span title=“<%= @title %>”>Hello, <%= content %>!</span>
In use: <%= render MyComponent.new(title: “greeting”) do %>world<% end %> returns: <span title=“greeting”>Hello, world!</span>
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/view_component/base.rb', line 47 def render_in(view_context, &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(self, &block) if block_given? before_render_check if render? send(self.class.call_method_name(@variant)) else "" end ensure @current_template = old_current_template end |
#view_cache_dependencies ⇒ Object
108 109 110 |
# File 'lib/view_component/base.rb', line 108 def view_cache_dependencies [] end |
#virtual_path ⇒ Object
Removes the first part of the path and the extension.
104 105 106 |
# File 'lib/view_component/base.rb', line 104 def virtual_path self.class.source_location.gsub(%r{(.*app/components)|(\.rb)}, "") end |
#with(area, content = nil, &block) ⇒ Object
116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/view_component/base.rb', line 116 def with(area, content = nil, &block) unless content_areas.include?(area) raise ArgumentError.new "Unknown content_area '#{area}' - expected one of '#{content_areas}'" end if block_given? content = view_context.capture(&block) end instance_variable_set("@#{area}".to_sym, content) nil end |