class Bridgetown::Component
Attributes
@return [Bridgetown::Site]
@return [Bridgetown::RubyTemplateView, Bridgetown::Component]
Public Class Methods
Read the template file.
@return [String]
# File lib/bridgetown-core/component.rb, line 83 def component_template_content @_tmpl_content ||= File.read(component_template_path) end
Find the first matching template path based on source location and extension.
@return [String]
# File lib/bridgetown-core/component.rb, line 58 def component_template_path @_tmpl_path ||= begin stripped_path = File.join( File.dirname(source_location), File.basename(source_location, ".*") ) supported_template_extensions.each do |ext| test_path = "#{stripped_path}.#{ext}" break test_path if File.exist?(test_path) test_path = "#{stripped_path}.html.#{ext}" break test_path if File.exist?(test_path) end end unless @_tmpl_path.is_a?(String) raise "#{name}: no matching template could be found in #{File.dirname(source_location)}" end @_tmpl_path end
# File lib/bridgetown-core/component.rb, line 18 def inherited(child) # Code cribbed from ViewComponent by GitHub: # Derive the source location of the component Ruby file from the call stack child.source_location = caller_locations(1, 10).reject do |l| l.label == "inherited" end[0].absolute_path super end
Return the appropriate template renderer for a given extension. TODO: make this extensible
@param ext [String] erb, slim, etc.
# File lib/bridgetown-core/component.rb, line 32 def renderer_for_ext(ext, &block) @_tmpl ||= case ext when "erb" include ERBCapture Tilt::ErubiTemplate.new(component_template_path, outvar: "@_erbout", bufval: "Bridgetown::OutputBuffer.new", engine_class: Bridgetown::ERBEngine, &block) when "serb" # requires serbea include Serbea::Helpers Tilt::SerbeaTemplate.new(component_template_path, &block) when "slim" # requires bridgetown-slim Slim::Template.new(component_template_path, &block) when "haml" # requires bridgetown-haml Tilt::HamlTemplate.new(component_template_path, &block) else raise NameError end rescue NameError, LoadError raise "No component rendering engine could be found for .#{ext} templates" end
A list of extensions supported by the renderer TODO: make this extensible
@return [Array<String>]
# File lib/bridgetown-core/component.rb, line 91 def supported_template_extensions %w(erb serb slim haml) end
Public Instance Methods
# File lib/bridgetown-core/component.rb, line 162 def _renderer @_renderer ||= begin ext = File.extname(self.class.component_template_path).delete_prefix(".") self.class.renderer_for_ext(ext) { self.class.component_template_content } end end
Subclasses can override this method to perform tasks before a render.
# File lib/bridgetown-core/component.rb, line 154 def before_render; end
Typically not used but here as a compatibility nod toward ViewComponent.
# File lib/bridgetown-core/component.rb, line 149 def call nil end
If a content block was originally passed into via `render`, capture its output.
@return [String] or nil
# File lib/bridgetown-core/component.rb, line 99 def content @_content ||= begin view_context.capture(self, &@_content_block) if @_content_block end end
# File lib/bridgetown-core/component.rb, line 170 def method_missing(method, *args, &block) if helpers.respond_to?(method.to_sym) helpers.send method.to_sym, *args, &block else super end end
Provide a render helper for evaluation within the component context.
@param item [Object] a component supporting `render_in` or a partial name @param options [Hash] passed to the `partial` helper if needed @return [String]
# File lib/bridgetown-core/component.rb, line 110 def render(item, options = {}, &block) if item.respond_to?(:render_in) result = "" capture do # this ensures no leaky interactions between BT<=>VC blocks result = item.render_in(self, &block) end result&.html_safe else partial(item, options, &block)&.html_safe end end
Subclasses can override this method to determine if the component should be rendered based on initialized data or other logic.
# File lib/bridgetown-core/component.rb, line 158 def render? true end
This is where the magic happens. Render the component within a view context.
@param view_context
[Bridgetown::RubyTemplateView]
# File lib/bridgetown-core/component.rb, line 125 def render_in(view_context, &block) @view_context = view_context @_content_block = block if render? before_render template else "" end rescue StandardError => e Bridgetown.logger.error "Component error:", "#{self.class} encountered an error while "\ "rendering `#{self.class.component_template_path}'" raise e end
# File lib/bridgetown-core/component.rb, line 178 def respond_to_missing?(method, include_private = false) helpers.respond_to?(method.to_sym, include_private) || super end
Subclasses can override this method to return a string from their own template handling.
# File lib/bridgetown-core/component.rb, line 144 def template call || _renderer.render(self) end