Class: Component

Inherits:
Object
  • Object
show all
Defined in:
lib/subcomponent/component.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, locals, lookup_context, parent, block) ⇒ Component

Use Component::ComponentHelper to create components.



5
6
7
8
9
10
11
12
13
14
15
# File 'lib/subcomponent/component.rb', line 5

def initialize(name, locals, lookup_context, parent, block)
  @_name = name
  @_parent = parent
  @_locals = locals
  @_lookup_context = lookup_context
  @_block = block
  @_index = 0

  @_components = {}
  @_building = false
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(symbol, *args, **kwargs, &block) ⇒ "Hello"

Specify and use local values or sub-components using method calls.

Locals

In the component:

Sub-components

In the component:

Checking for locals or sub-components

Examples:


= component :header do |c|
  c.title = "Hello"

= this.title

= component :card do |c|
  - c.header do |c|
    - c.title = "Hello"

= this.render :header

= this.header.render

= this.title?
= this.header?

Returns:

  • ("Hello")

    Hello“

  • The rendered component

  • true or false



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/subcomponent/component.rb', line 77

def method_missing symbol, *args, **kwargs, &block
  # This is used when building a component
  if _building && symbol != :to_ary
    if symbol.ends_with?("=") && args.length == 1
      _locals[symbol[0..-2].to_sym] = args.first
    else
      _components[symbol] ||= []

      child = Component.new(symbol, args.first || kwargs, _lookup_context, self, block)
      child._renderer = _renderer
      child._capture = _capture
      child._capture_self

      _components[symbol] << child
    end
    nil

  # This is used when rendering a component
  elsif symbol.ends_with?("?")
    _locals.key?(symbol[0..-2].to_sym) || _components.key?(symbol[0..-2].to_sym)
  else
    _locals[symbol] || _components[symbol]
  end
end

Instance Attribute Details

#_captureObject

Returns the value of attribute _capture.



2
3
4
# File 'lib/subcomponent/component.rb', line 2

def _capture
  @_capture
end

#_rendererObject

Returns the value of attribute _renderer.



2
3
4
# File 'lib/subcomponent/component.rb', line 2

def _renderer
  @_renderer
end

Instance Method Details

#_capture_selfObject

:nodoc:



211
212
213
214
215
216
# File 'lib/subcomponent/component.rb', line 211

def _capture_self
  self._building = true
  self._captured = _capture.call(self, _block)
ensure
  self._building = false
end

#_yield_rendererObject

:nodoc:



205
206
207
208
# File 'lib/subcomponent/component.rb', line 205

def _yield_renderer
  locals = _locals.merge(this: self)
  _renderer.call(_partial, locals, _captured)
end

#components(key) ⇒ Object

This is used to access sub-components passed to the component.

Examples:


components :header

returns: [<Component>, <Component>, ...]

Parameters:

  • key (Symbol)

    The name of the sub-component



134
135
136
# File 'lib/subcomponent/component.rb', line 134

def components(key)
  _components[key] || []
end

#copy_components(from, to) ⇒ Object

Copy all sub-components from one name to another. This is useful when you want to render a sub-component using two+ subcomponents

this.copy_components :links, :mobile_links


178
179
180
181
182
183
184
185
186
# File 'lib/subcomponent/component.rb', line 178

def copy_components(from, to)
  return if _components[from].nil?

  _components[to] = _components[from].map(&:dup).tap do |comps|
    comps.each do |comp|
      comp._name = to
    end
  end
end

#indexObject

This will be set to the index of the component when using ‘render_all`

Returns an integer



192
193
194
# File 'lib/subcomponent/component.rb', line 192

def index
  _index
end

#initialize_dup(other) ⇒ Object

:nodoc:



18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/subcomponent/component.rb', line 18

def initialize_dup(other)
  @_name = other._name
  @_parent = other._parent
  @_locals = other._locals.dup
  @_lookup_context = other._lookup_context
  @_block = other._block
  @_components = other._components.dup
  @_building = false
  @_renderer = other._renderer
  @_capture = other._capture
  @_index = 0

  super
end

#local(key) ⇒ Object

This is used to access locals passed to the component.



139
140
141
# File 'lib/subcomponent/component.rb', line 139

def local(key)
  _locals[key]
end

#render(symbol = nil) ⇒ Object

This is used to render a sub-component.

From within a component:

this.render :header

Or calling directly on a sub-component:

this.header.render


153
154
155
156
157
158
159
160
161
# File 'lib/subcomponent/component.rb', line 153

def render(symbol = nil)
  if symbol.nil?
    raise "Cannot render a component without a symbol when it has a parent." if _parent.nil?

    return _yield_renderer

  end
  _components[symbol]&.first&._yield_renderer
end

#render_all(symbol) ⇒ Object

Render all sub-components of a given name.

this.render_all :header

Returns a string of all rednered sub-components.



169
170
171
# File 'lib/subcomponent/component.rb', line 169

def render_all(symbol)
  _components[symbol]&.each_with_index { |e, i| e._index = i }&.map(&:_yield_renderer)&.join&.html_safe
end

#require(*local_keys) ⇒ Object

This is used to require local keys or sub-components.

Parameters:

  • loacl_keys (Array<Symbol>)

    require :title, :body



117
118
119
120
121
122
# File 'lib/subcomponent/component.rb', line 117

def require *local_keys
  missing = local_keys.reject { |k| _locals.key?(k) || _components.key?(k) }
  raise "The #{_name} component requires #{missing.join(", ")} local(s) or component(s)." if missing.count > 0

  nil
end

#respond_to_missing?(symbol, *args) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


103
104
105
106
107
108
109
# File 'lib/subcomponent/component.rb', line 103

def respond_to_missing? symbol, *args
  if symbol != :to_ary
    true
  else
    super
  end
end

#yieldObject

Yield the block passed to the component.

this.yield


200
201
202
# File 'lib/subcomponent/component.rb', line 200

def yield
  _captured
end