Class: Compony::Component
- Inherits:
-
Object
- Object
- Compony::Component
- Defined in:
- lib/compony/component.rb
Direct Known Subclasses
Compony::Components::Button, Compony::Components::Destroy, Compony::Components::Form, Compony::Components::WithForm
Instance Attribute Summary collapse
- #comp_opts ⇒ Object readonly
-
#content_blocks ⇒ Object
readonly
needed in RequestContext for nesting.
- #parent_comp ⇒ Object readonly
Class Method Summary collapse
-
.setup(&block) ⇒ Object
DSL method.
Instance Method Summary collapse
-
#action(action_name, before: nil, &block) ⇒ Object
DSL method Adds or replaces an action (for action buttons) If before: is specified, will insert the action before the named action.
-
#before_render(name = :main, before: nil, **kwargs, &block) ⇒ Object
DSL method Adds or overrides a before_render block.
-
#comp_cst ⇒ Object
Returns the name of the class constant of this component.
-
#comp_name ⇒ Object
Returns the component name.
-
#content(name = :main, before: nil, **kwargs, &block) ⇒ Object
DSL method Adds or overrides a content block.
-
#family_cst ⇒ Object
Returns the name of the module constant (=family) of this component.
-
#family_name ⇒ Object
Returns the family name.
-
#id ⇒ Object
Returns an identifier describing this component.
-
#initialize(parent_comp = nil, index: 0, **comp_opts) ⇒ Component
constructor
A new instance of Component.
- #inspect ⇒ Object
-
#param_name(unprefixed_param_name) ⇒ Object
Given an unprefixed name of a param, adds the path hash Do not overwrite.
-
#path ⇒ Object
Returns the id path from the root_comp.
-
#path_hash ⇒ Object
Returns a hash for the path.
-
#remove_content(name) ⇒ Object
DSL method Removes a content block.
-
#render(controller, standalone: false, **locals) ⇒ Object
Renders the component using the controller passsed to it and returns it as a string.
-
#render_actions(controller, wrapper_class: '', action_class: '') ⇒ Object
Used to render all actions of this component, each button wrapped in a div with the specified class.
-
#resourceful? ⇒ Boolean
Is true for resourceful components.
-
#root_comp ⇒ Object
Returns the current root comp.
-
#root_comp? ⇒ Boolean
Returns whether or not this is the root comp.
-
#skip_action(action_name) ⇒ Object
DSL method Marks an action for skip.
-
#sub_comp(component_class, **comp_opts) ⇒ Object
Instanciate a component with
selfas a parent.
Constructor Details
#initialize(parent_comp = nil, index: 0, **comp_opts) ⇒ Component
Returns a new instance of Component.
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# File 'lib/compony/component.rb', line 23 def initialize(parent_comp = nil, index: 0, **comp_opts) @parent_comp = parent_comp @sub_comps = [] @index = index @comp_opts = comp_opts @before_render_blocks = NaturalOrdering.new @content_blocks = NaturalOrdering.new @actions = NaturalOrdering.new @skipped_actions = Set.new init_standalone init_labelling fail "#{inspect} is missing a call to `setup`." unless setup_blocks&.any? setup_blocks.each do |setup_block| instance_exec(&setup_block) end end |
Instance Attribute Details
#comp_opts ⇒ Object (readonly)
9 10 11 |
# File 'lib/compony/component.rb', line 9 def comp_opts @comp_opts end |
#content_blocks ⇒ Object (readonly)
needed in RequestContext for nesting
10 11 12 |
# File 'lib/compony/component.rb', line 10 def content_blocks @content_blocks end |
#parent_comp ⇒ Object (readonly)
8 9 10 |
# File 'lib/compony/component.rb', line 8 def parent_comp @parent_comp end |
Class Method Details
.setup(&block) ⇒ Object
DSL method
16 17 18 19 20 21 |
# File 'lib/compony/component.rb', line 16 def self.setup(&block) fail("`setup` expects a block in #{inspect}.") unless block_given? self.setup_blocks ||= [] self.setup_blocks = setup_blocks.dup # This is required to prevent the parent class to see children's setup blocks. setup_blocks << block end |
Instance Method Details
#action(action_name, before: nil, &block) ⇒ Object
DSL method Adds or replaces an action (for action buttons) If before: is specified, will insert the action before the named action. When replacing, an element keeps its position unless before: is specified.
187 188 189 |
# File 'lib/compony/component.rb', line 187 def action(action_name, before: nil, &block) @actions.natural_push(action_name, block, before:) end |
#before_render(name = :main, before: nil, **kwargs, &block) ⇒ Object
DSL method Adds or overrides a before_render block. You can use controller.redirect_to to redirect away and halt the before_render/content chain
121 122 123 124 |
# File 'lib/compony/component.rb', line 121 def before_render(name = :main, before: nil, **kwargs, &block) fail("`before_render` expects a block in #{inspect}.") unless block_given? @before_render_blocks.natural_push(name, block, before:, **kwargs) end |
#comp_cst ⇒ Object
Returns the name of the class constant of this component. Do not override.
106 107 108 |
# File 'lib/compony/component.rb', line 106 def comp_cst self.class.name.demodulize.to_sym end |
#comp_name ⇒ Object
Returns the component name
111 112 113 |
# File 'lib/compony/component.rb', line 111 def comp_name comp_cst.to_s.underscore end |
#content(name = :main, before: nil, **kwargs, &block) ⇒ Object
DSL method Adds or overrides a content block.
132 133 134 135 136 137 138 |
# File 'lib/compony/component.rb', line 132 def content(name = :main, before: nil, **kwargs, &block) # A block is required here, but if this is an override (e.g. to hide another content block), we can tolerate the missing block. if !block_given? && @content_blocks.find { |b| b.name == name }.nil? fail("`content` expects a block in #{inspect}.") end @content_blocks.natural_push(name, block || :missing, before:, **kwargs) end |
#family_cst ⇒ Object
Returns the name of the module constant (=family) of this component. Do not override.
96 97 98 |
# File 'lib/compony/component.rb', line 96 def family_cst self.class.module_parent.to_s.demodulize.to_sym end |
#family_name ⇒ Object
Returns the family name
101 102 103 |
# File 'lib/compony/component.rb', line 101 def family_name family_cst.to_s.underscore end |
#id ⇒ Object
Returns an identifier describing this component. Must be unique among simplings under the same parent_comp. Do not override.
62 63 64 |
# File 'lib/compony/component.rb', line 62 def id "#{family_name}_#{comp_name}_#{@index}" end |
#inspect ⇒ Object
43 44 45 |
# File 'lib/compony/component.rb', line 43 def inspect "#<#{self.class.name}:#{hash}>" end |
#param_name(unprefixed_param_name) ⇒ Object
Given an unprefixed name of a param, adds the path hash Do not overwrite.
84 85 86 |
# File 'lib/compony/component.rb', line 84 def param_name(unprefixed_param_name) "#{path_hash}_#{unprefixed_param_name}" end |
#path ⇒ Object
Returns the id path from the root_comp. Do not overwrite.
68 69 70 71 72 73 74 |
# File 'lib/compony/component.rb', line 68 def path if root_comp? id else "#{parent_comp.path}/#{id}" end end |
#path_hash ⇒ Object
Returns a hash for the path. Used for params prefixing. Do not overwrite.
78 79 80 |
# File 'lib/compony/component.rb', line 78 def path_hash Digest::SHA1.hexdigest(path)[..4] end |
#remove_content(name) ⇒ Object
DSL method Removes a content block. Use this in subclasses if a content block defined in the parent should be removed from the child.
143 144 145 146 147 |
# File 'lib/compony/component.rb', line 143 def remove_content(name) name = name.to_sym existing_index = @content_blocks.find_index { |el| el.name == name } || fail("Content block #{name.inspect} not found for removal in #{inspect}.") @content_blocks.delete_at(existing_index) end |
#render(controller, standalone: false, **locals) ⇒ Object
Renders the component using the controller passsed to it and returns it as a string. Do not overwrite.
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/compony/component.rb', line 152 def render(controller, standalone: false, **locals) # Call before_render hooks (if any) and backfire instance variables back to the component @before_render_blocks.each do |element| RequestContext.new(self, controller, locals:).evaluate_with_backfire(&element.payload) # Stop if a `before_render` block issued a body (e.g. through redirecting) break unless controller.response_body.nil? end # Render, unless before_render has already issued a body (e.g. through redirecting). if controller.response_body.nil? fail "#{self.class.inspect} must define `content` or set a response body in `before_render`" if @content_blocks.none? return controller.render_to_string( type: :dyny, locals: { content_blocks: @content_blocks, standalone:, component: self, render_locals: locals }, inline: " if Compony.content_before_root_comp_block && standalone\n Compony::RequestContext.new(component, controller, helpers: self, locals: render_locals).evaluate(&Compony.content_before_root_comp_block)\n end\n content_blocks.reject{ |el| el.hidden }.each do |element|\n # Instanciate and evaluate a fresh RequestContext in order to use the buffer allocated by the ActionView (needed for `concat` calls)\n Compony::RequestContext.new(component, controller, helpers: self, locals: render_locals).evaluate(&element.payload)\n end\n if Compony.content_after_root_comp_block && standalone\n Compony::RequestContext.new(component, controller, helpers: self, locals: render_locals).evaluate(&Compony.content_after_root_comp_block)\n end\n RUBY\n )\n else\n return nil # Prevent double render errors\n end\nend\n" |
#render_actions(controller, wrapper_class: '', action_class: '') ⇒ Object
Used to render all actions of this component, each button wrapped in a div with the specified class
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
# File 'lib/compony/component.rb', line 198 def render_actions(controller, wrapper_class: '', action_class: '') h = controller.helpers h.content_tag(:div, class: wrapper_class) do = @actions.map do |action| next if @skipped_actions.include?(action.name) Compony.(feasibility_action: action.name.to_sym) do = action.payload.call(controller) next unless = .render(controller) next if .blank? h.content_tag(:div, , class: action_class) end end next h.safe_join end end |
#resourceful? ⇒ Boolean
Is true for resourceful components
216 217 218 |
# File 'lib/compony/component.rb', line 216 def resourceful? return false end |
#root_comp ⇒ Object
Returns the current root comp. Do not overwrite.
49 50 51 52 |
# File 'lib/compony/component.rb', line 49 def root_comp return self unless parent_comp return parent_comp.root_comp end |
#root_comp? ⇒ Boolean
Returns whether or not this is the root comp. Do not overwrite.
56 57 58 |
# File 'lib/compony/component.rb', line 56 def root_comp? parent_comp.nil? end |
#skip_action(action_name) ⇒ Object
DSL method Marks an action for skip
193 194 195 |
# File 'lib/compony/component.rb', line 193 def skip_action(action_name) @skipped_actions << action_name.to_sym end |
#sub_comp(component_class, **comp_opts) ⇒ Object
Instanciate a component with self as a parent
89 90 91 92 93 |
# File 'lib/compony/component.rb', line 89 def sub_comp(component_class, **comp_opts) sub = component_class.new(self, index: @sub_comps.count, **comp_opts) @sub_comps << sub return sub end |