Class: Bridgetown::Component
- Inherits:
-
Object
- Object
- Bridgetown::Component
- Extended by:
- Forwardable
- Defined in:
- lib/bridgetown-core/component.rb
Direct Known Subclasses
Class Attribute Summary collapse
-
.source_location ⇒ Object
Returns the value of attribute source_location.
Instance Attribute Summary collapse
- #site ⇒ Bridgetown::Site readonly
- #view_context ⇒ Bridgetown::RubyTemplateView, Bridgetown::Component readonly
Class Method Summary collapse
-
.component_template_content ⇒ String
Read the template file.
-
.component_template_path ⇒ String
Find the first matching template path based on source location and extension.
- .inherited(child) ⇒ Object
- .path_for_errors ⇒ Object
-
.renderer_for_ext(ext, &block) ⇒ Object
Return the appropriate template renderer for a given extension.
-
.supported_template_extensions ⇒ Array<String>
A list of extensions supported by the renderer TODO: make this extensible.
Instance Method Summary collapse
- #_renderer ⇒ Object
-
#before_render ⇒ Object
Subclasses can override this method to perform tasks before a render.
-
#call ⇒ Object
Typically not used but here as a compatibility nod toward ViewComponent.
-
#content ⇒ String
If a content block was originally passed into via
render
, capture its output. - #helpers ⇒ Object
- #method_missing(method, *args, **kwargs, &block) ⇒ Object
-
#render(item, options = {}, &block) ⇒ String
Provide a render helper for evaluation within the component context.
-
#render? ⇒ Boolean
Subclasses can override this method to determine if the component should be rendered based on initialized data or other logic.
-
#render_in(view_context, &block) ⇒ Object
This is where the magic happens.
- #respond_to_missing?(method, include_private = false) ⇒ Boolean
-
#slot(name, input = nil, replace: false, &block) ⇒ void
Define a new component slot.
- #slots ⇒ Array<Bridgetown::Slot>
-
#slotted(name, default_input = nil, &default_block) ⇒ String
Render out a component slot.
-
#slotted?(name) ⇒ Boolean
Check if a component slot has been defined.
-
#template ⇒ Object
Subclasses can override this method to return a string from their own template handling.
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *args, **kwargs, &block) ⇒ Object
229 230 231 232 233 234 235 |
# File 'lib/bridgetown-core/component.rb', line 229 def method_missing(method, *args, **kwargs, &block) if helpers.respond_to?(method.to_sym) helpers.send method.to_sym, *args, **kwargs, &block else super end end |
Class Attribute Details
.source_location ⇒ Object
Returns the value of attribute source_location.
16 17 18 |
# File 'lib/bridgetown-core/component.rb', line 16 def source_location @source_location end |
Instance Attribute Details
#site ⇒ Bridgetown::Site (readonly)
10 11 12 |
# File 'lib/bridgetown-core/component.rb', line 10 def site @site end |
#view_context ⇒ Bridgetown::RubyTemplateView, Bridgetown::Component (readonly)
13 14 15 |
# File 'lib/bridgetown-core/component.rb', line 13 def view_context @view_context end |
Class Method Details
.component_template_content ⇒ String
Read the template file.
81 82 83 |
# File 'lib/bridgetown-core/component.rb', line 81 def component_template_content @_tmpl_content ||= File.read(component_template_path) end |
.component_template_path ⇒ String
Find the first matching template path based on source location and extension.
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/bridgetown-core/component.rb', line 56 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 |
.inherited(child) ⇒ Object
18 19 20 21 22 23 24 25 26 |
# 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 |
.path_for_errors ⇒ Object
93 94 95 96 97 |
# File 'lib/bridgetown-core/component.rb', line 93 def path_for_errors component_template_path rescue RuntimeError source_location end |
.renderer_for_ext(ext, &block) ⇒ Object
Return the appropriate template renderer for a given extension. TODO: make this extensible
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/bridgetown-core/component.rb', line 32 def renderer_for_ext(ext, &block) @_tmpl ||= case ext.to_s when "erb" Tilt::ErubiTemplate.new(component_template_path, outvar: "@_erbout", bufval: "Bridgetown::OutputBuffer.new", engine_class: Bridgetown::ERBEngine, &block) when "serb" 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 |
.supported_template_extensions ⇒ Array<String>
A list of extensions supported by the renderer TODO: make this extensible
89 90 91 |
# File 'lib/bridgetown-core/component.rb', line 89 def supported_template_extensions %w(erb serb slim haml) end |
Instance Method Details
#_renderer ⇒ Object
214 215 216 217 218 219 220 221 |
# File 'lib/bridgetown-core/component.rb', line 214 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 }.tap do |rn| self.class.include(rn.is_a?(Tilt::SerbeaTemplate) ? Serbea::Helpers : ERBCapture) end end end |
#before_render ⇒ Object
Subclasses can override this method to perform tasks before a render.
206 |
# File 'lib/bridgetown-core/component.rb', line 206 def before_render; end |
#call ⇒ Object
Typically not used but here as a compatibility nod toward ViewComponent.
201 202 203 |
# File 'lib/bridgetown-core/component.rb', line 201 def call nil end |
#content ⇒ String
If a content block was originally passed into via render
, capture its output.
103 104 105 |
# File 'lib/bridgetown-core/component.rb', line 103 def content @_content ||= (view_context.capture(self, &@_content_block) if @_content_block) end |
#helpers ⇒ Object
223 224 225 226 227 |
# File 'lib/bridgetown-core/component.rb', line 223 def helpers @helpers ||= Bridgetown::RubyTemplateView::Helpers.new( self, view_context&.site || Bridgetown::Current.site ) end |
#render(item, options = {}, &block) ⇒ String
Provide a render helper for evaluation within the component context.
162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/bridgetown-core/component.rb', line 162 def render(item, = {}, &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, , &block)&.html_safe end end |
#render? ⇒ Boolean
Subclasses can override this method to determine if the component should be rendered based on initialized data or other logic.
210 211 212 |
# File 'lib/bridgetown-core/component.rb', line 210 def render? true end |
#render_in(view_context, &block) ⇒ Object
This is where the magic happens. Render the component within a view context.
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/bridgetown-core/component.rb', line 177 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.path_for_errors}'" raise e end |
#respond_to_missing?(method, include_private = false) ⇒ Boolean
237 238 239 |
# File 'lib/bridgetown-core/component.rb', line 237 def respond_to_missing?(method, include_private = false) helpers.respond_to?(method.to_sym, include_private) || super end |
#slot(name, input = nil, replace: false, &block) ⇒ void
This method returns an undefined value.
Define a new component slot
118 119 120 121 122 123 124 125 126 127 |
# File 'lib/bridgetown-core/component.rb', line 118 def slot(name, input = nil, replace: false, &block) content = block.nil? ? input.to_s : view_context.capture(&block) name = name.to_s slots.reject! { _1.name == name } if replace slots << Slot.new(name: name, content: content, context: self, transform: false) nil end |
#slots ⇒ Array<Bridgetown::Slot>
108 109 110 |
# File 'lib/bridgetown-core/component.rb', line 108 def slots @slots ||= [] end |
#slotted(name, default_input = nil, &default_block) ⇒ String
Render out a component slot
134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/bridgetown-core/component.rb', line 134 def slotted(name, default_input = nil, &default_block) content # ensure content block is processed name = name.to_s filtered_slots = slots.select do |slot| slot.name == name end return filtered_slots.map(&:content).join.html_safe if filtered_slots.length.positive? default_block.nil? ? default_input.to_s : capture(&default_block) end |
#slotted?(name) ⇒ Boolean
Check if a component slot has been defined
150 151 152 153 154 155 |
# File 'lib/bridgetown-core/component.rb', line 150 def slotted?(name) name = name.to_s slots.any? do |slot| slot.name == name end end |
#template ⇒ Object
Subclasses can override this method to return a string from their own template handling.
196 197 198 |
# File 'lib/bridgetown-core/component.rb', line 196 def template call || _renderer.render(self) end |