Class: Bridgetown::Component
- Inherits:
-
Object
- Object
- Bridgetown::Component
- Extended by:
- Forwardable
- Includes:
- Streamlined
- 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) ⇒ 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
235 236 237 238 239 240 241 |
# File 'lib/bridgetown-core/component.rb', line 235 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.
18 19 20 |
# File 'lib/bridgetown-core/component.rb', line 18 def source_location @source_location end |
Instance Attribute Details
#site ⇒ Bridgetown::Site (readonly)
12 13 14 |
# File 'lib/bridgetown-core/component.rb', line 12 def site @site end |
#view_context ⇒ Bridgetown::RubyTemplateView, Bridgetown::Component (readonly)
15 16 17 |
# File 'lib/bridgetown-core/component.rb', line 15 def view_context @view_context end |
Class Method Details
.component_template_content ⇒ String
Read the template file.
83 84 85 |
# File 'lib/bridgetown-core/component.rb', line 83 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.
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# 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 |
.inherited(child) ⇒ Object
20 21 22 23 24 25 26 27 28 |
# File 'lib/bridgetown-core/component.rb', line 20 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
95 96 97 98 99 |
# File 'lib/bridgetown-core/component.rb', line 95 def path_for_errors File.basename(component_template_path) rescue RuntimeError, TypeError source_location end |
.renderer_for_ext(ext) ⇒ Object
Return the appropriate template renderer for a given extension. TODO: make this extensible
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/bridgetown-core/component.rb', line 34 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
91 92 93 |
# File 'lib/bridgetown-core/component.rb', line 91 def supported_template_extensions %w(erb serb slim haml) end |
Instance Method Details
#_renderer ⇒ Object
222 223 224 225 226 227 228 229 |
# File 'lib/bridgetown-core/component.rb', line 222 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.
214 |
# File 'lib/bridgetown-core/component.rb', line 214 def before_render; end |
#call ⇒ Object
Typically not used but here as a compatibility nod toward ViewComponent.
209 210 211 |
# File 'lib/bridgetown-core/component.rb', line 209 def call nil end |
#content ⇒ String
If a content block was originally passed into via render
, capture its output.
105 106 107 |
# File 'lib/bridgetown-core/component.rb', line 105 def content @_content ||= (view_context.capture(self, &@_content_block) if @_content_block) end |
#helpers ⇒ Object
231 232 233 |
# File 'lib/bridgetown-core/component.rb', line 231 def helpers @helpers ||= Bridgetown::RubyTemplateView::Helpers.new(self, view_context&.site) end |
#render(item, options = {}, &block) ⇒ String
Provide a render helper for evaluation within the component context.
164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/bridgetown-core/component.rb', line 164 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.
218 219 220 |
# File 'lib/bridgetown-core/component.rb', line 218 def render? true end |
#render_in(view_context, &block) ⇒ Object
This is where the magic happens. Render the component within a view context.
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/bridgetown-core/component.rb', line 179 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
243 244 245 |
# File 'lib/bridgetown-core/component.rb', line 243 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
120 121 122 123 124 125 126 127 128 129 |
# File 'lib/bridgetown-core/component.rb', line 120 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>
110 111 112 |
# File 'lib/bridgetown-core/component.rb', line 110 def slots @slots ||= [] end |
#slotted(name, default_input = nil, &default_block) ⇒ String
Render out a component slot
136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/bridgetown-core/component.rb', line 136 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
152 153 154 155 156 157 |
# File 'lib/bridgetown-core/component.rb', line 152 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.
204 205 206 |
# File 'lib/bridgetown-core/component.rb', line 204 def template (method(:call).arity.zero? ? call : nil) || _renderer.render(self) end |