Class: Bridgetown::Component
- Inherits:
-
Object
- Object
- Bridgetown::Component
- Extended by:
- Forwardable
- Includes:
- Streamlined
- Defined in:
- bridgetown-core/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) ⇒ 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) ⇒ 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.
Methods included from Streamlined
Methods included from ERBCapture
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method) ⇒ Object
236 237 238 239 240 241 242 |
# File 'bridgetown-core/lib/bridgetown-core/component.rb', line 236 def method_missing(method, ...) if helpers.respond_to?(method.to_sym) helpers.send(method.to_sym, ...) else super end end |
Class Attribute Details
.source_location ⇒ Object
Returns the value of attribute source_location.
17 18 19 |
# File 'bridgetown-core/lib/bridgetown-core/component.rb', line 17 def source_location @source_location end |
Instance Attribute Details
#site ⇒ Bridgetown::Site (readonly)
11 12 13 |
# File 'bridgetown-core/lib/bridgetown-core/component.rb', line 11 def site @site end |
#view_context ⇒ Bridgetown::RubyTemplateView, Bridgetown::Component (readonly)
14 15 16 |
# File 'bridgetown-core/lib/bridgetown-core/component.rb', line 14 def view_context @view_context end |
Class Method Details
.component_template_content ⇒ String
Read the template file.
82 83 84 |
# File 'bridgetown-core/lib/bridgetown-core/component.rb', line 82 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.
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'bridgetown-core/lib/bridgetown-core/component.rb', line 57 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
19 20 21 22 23 24 25 26 27 |
# File 'bridgetown-core/lib/bridgetown-core/component.rb', line 19 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
94 95 96 97 98 |
# File 'bridgetown-core/lib/bridgetown-core/component.rb', line 94 def path_for_errors File.basename(component_template_path) rescue RuntimeError source_location end |
.renderer_for_ext(ext) ⇒ Object
Return the appropriate template renderer for a given extension. TODO: make this extensible
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'bridgetown-core/lib/bridgetown-core/component.rb', line 33 def renderer_for_ext(ext, &) @_tmpl ||= case ext.to_s when "erb" Tilt::ErubiTemplate.new(component_template_path, outvar: "@_erbout", bufval: "Bridgetown::OutputBuffer.new", engine_class: Bridgetown::ERBEngine, &) when "serb" Tilt::SerbeaTemplate.new(component_template_path, &) when "slim" # requires bridgetown-slim Slim::Template.new(component_template_path, &) when "haml" # requires bridgetown-haml Tilt::HamlTemplate.new(component_template_path, &) 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
90 91 92 |
# File 'bridgetown-core/lib/bridgetown-core/component.rb', line 90 def supported_template_extensions %w(erb serb slim haml) end |
Instance Method Details
#_renderer ⇒ Object
221 222 223 224 225 226 227 228 |
# File 'bridgetown-core/lib/bridgetown-core/component.rb', line 221 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.
213 |
# File 'bridgetown-core/lib/bridgetown-core/component.rb', line 213 def before_render; end |
#call ⇒ Object
Typically not used but here as a compatibility nod toward ViewComponent.
208 209 210 |
# File 'bridgetown-core/lib/bridgetown-core/component.rb', line 208 def call nil end |
#content ⇒ String
If a content block was originally passed into via render
, capture its output.
104 105 106 |
# File 'bridgetown-core/lib/bridgetown-core/component.rb', line 104 def content @_content ||= (view_context.capture(self, &@_content_block) if @_content_block) end |
#helpers ⇒ Object
230 231 232 233 234 |
# File 'bridgetown-core/lib/bridgetown-core/component.rb', line 230 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.
163 164 165 166 167 168 169 170 171 172 173 |
# File 'bridgetown-core/lib/bridgetown-core/component.rb', line 163 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.
217 218 219 |
# File 'bridgetown-core/lib/bridgetown-core/component.rb', line 217 def render? true end |
#render_in(view_context, &block) ⇒ Object
This is where the magic happens. Render the component within a view context.
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'bridgetown-core/lib/bridgetown-core/component.rb', line 178 def render_in(view_context, &block) @view_context = view_context @_content_block = block if render? if helpers.site.config.fast_refresh signal = helpers.site.tmp_cache["comp-signal:#{self.class.source_location}"] ||= Signalize.signal(1) # subscribe so resources are attached to this component within effect signal.value end 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
244 245 246 |
# File 'bridgetown-core/lib/bridgetown-core/component.rb', line 244 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
119 120 121 122 123 124 125 126 127 128 |
# File 'bridgetown-core/lib/bridgetown-core/component.rb', line 119 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:, content:, context: self, transform: false) nil end |
#slots ⇒ Array<Bridgetown::Slot>
109 110 111 |
# File 'bridgetown-core/lib/bridgetown-core/component.rb', line 109 def slots @slots ||= [] end |
#slotted(name, default_input = nil, &default_block) ⇒ String
Render out a component slot
135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'bridgetown-core/lib/bridgetown-core/component.rb', line 135 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
151 152 153 154 155 156 |
# File 'bridgetown-core/lib/bridgetown-core/component.rb', line 151 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.
203 204 205 |
# File 'bridgetown-core/lib/bridgetown-core/component.rb', line 203 def template call || _renderer.render(self) end |