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
- #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.
-
#add_content(index = -1,, &block) ⇒ Object
DSL method Adds a content block that will be executed after all previous ones.
-
#before_render(&block) ⇒ Object
DSL method.
- #comp_class_for ⇒ Object
- #comp_class_for! ⇒ Object
-
#comp_cst ⇒ Object
Returns the name of the class constant of this component.
-
#comp_name ⇒ Object
Returns the component name.
-
#content(&block) ⇒ Object
DSL method Overrides previous content (also from superclasses).
-
#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.
-
#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
self
as a parent.
Constructor Details
#initialize(parent_comp = nil, index: 0, **comp_opts) ⇒ Component
Returns a new instance of Component.
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
# File 'lib/compony/component.rb', line 22 def initialize(parent_comp = nil, index: 0, **comp_opts) @parent_comp = parent_comp @sub_comps = [] @index = index @comp_opts = comp_opts @before_render_block = nil @content_blocks = [] @actions = [] @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 |
#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
15 16 17 18 19 20 |
# File 'lib/compony/component.rb', line 15 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.
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/compony/component.rb', line 181 def action(action_name, before: nil, &block) action_name = action_name.to_sym before_name = before&.to_sym action = MethodAccessibleHash.new(name: action_name, block:) existing_index = @actions.find_index { |el| el.name == action_name } if existing_index.present? && before_name.present? @actions.delete_at(existing_index) # Replacing an existing element with a before: directive - must delete before calculating indices end if before_name.present? before_index = @actions.find_index { |el| el.name == before_name } || fail("Action #{before_name} for :before not found in #{inspect}.") end if before_index.present? @actions.insert(before_index, action) elsif existing_index.present? @actions[existing_index] = action else @actions << action end end |
#add_content(index = -1,, &block) ⇒ Object
DSL method
Adds a content block that will be executed after all previous ones.
It is safe to use this method even if content
has never been called
You can use dyny here.
141 142 143 144 145 |
# File 'lib/compony/component.rb', line 141 def add_content(index = -1, &block) fail("`content` expects a block in #{inspect}.") unless block_given? @content_blocks ||= [] @content_blocks.insert(index, block) end |
#before_render(&block) ⇒ Object
DSL method
125 126 127 |
# File 'lib/compony/component.rb', line 125 def before_render(&block) @before_render_block = block end |
#comp_class_for ⇒ Object
deprecate (check for usages beforehand)
115 116 117 |
# File 'lib/compony/component.rb', line 115 def comp_class_for(...) Compony.comp_class_for(...) end |
#comp_class_for! ⇒ Object
deprecate (check for usages beforehand)
120 121 122 |
# File 'lib/compony/component.rb', line 120 def comp_class_for!(...) Compony.comp_class_for!(...) end |
#comp_cst ⇒ Object
Returns the name of the class constant of this component. Do not override.
105 106 107 |
# File 'lib/compony/component.rb', line 105 def comp_cst self.class.name.demodulize.to_sym end |
#comp_name ⇒ Object
Returns the component name
110 111 112 |
# File 'lib/compony/component.rb', line 110 def comp_name comp_cst.to_s.underscore end |
#content(&block) ⇒ Object
DSL method Overrides previous content (also from superclasses). Will be the first content block to run. You can use dyny here.
132 133 134 135 |
# File 'lib/compony/component.rb', line 132 def content(&block) fail("`content` expects a block in #{inspect}.") unless block_given? @content_blocks = [block] end |
#family_cst ⇒ Object
Returns the name of the module constant (=family) of this component. Do not override.
95 96 97 |
# File 'lib/compony/component.rb', line 95 def family_cst self.class.module_parent.to_s.demodulize.to_sym end |
#family_name ⇒ Object
Returns the family name
100 101 102 |
# File 'lib/compony/component.rb', line 100 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.
61 62 63 |
# File 'lib/compony/component.rb', line 61 def id "#{family_name}_#{comp_name}_#{@index}" end |
#inspect ⇒ Object
42 43 44 |
# File 'lib/compony/component.rb', line 42 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.
83 84 85 |
# File 'lib/compony/component.rb', line 83 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.
67 68 69 70 71 72 73 |
# File 'lib/compony/component.rb', line 67 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.
77 78 79 |
# File 'lib/compony/component.rb', line 77 def path_hash Digest::SHA1.hexdigest(path)[..4] 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.
150 151 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 |
# File 'lib/compony/component.rb', line 150 def render(controller, standalone: false, **locals) # Call before_render hook if any and backfire instance variables back to the component # TODO: Can .request_context be removed from the next line? Test well! RequestContext.new(self, controller, locals:).request_context.evaluate_with_backfire(&@before_render_block) if @before_render_block # 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: <<~RUBY if Compony.content_before_root_comp_block && standalone Compony::RequestContext.new(component, controller, helpers: self, locals: render_locals).evaluate(&Compony.content_before_root_comp_block) end content_blocks.each do |block| # Instanciate and evaluate a fresh RequestContext in order to use the buffer allocated by the ActionView (needed for `concat` calls) Compony::RequestContext.new(component, controller, helpers: self, locals: render_locals).evaluate(&block) end if Compony.content_after_root_comp_block && standalone Compony::RequestContext.new(component, controller, helpers: self, locals: render_locals).evaluate(&Compony.content_after_root_comp_block) end RUBY ) else return nil # Prevent double render errors end end |
#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
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
# File 'lib/compony/component.rb', line 210 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.block.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
228 229 230 |
# File 'lib/compony/component.rb', line 228 def resourceful? return false end |
#root_comp ⇒ Object
Returns the current root comp. Do not overwrite.
48 49 50 51 |
# File 'lib/compony/component.rb', line 48 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.
55 56 57 |
# File 'lib/compony/component.rb', line 55 def root_comp? parent_comp.nil? end |
#skip_action(action_name) ⇒ Object
DSL method Marks an action for skip
205 206 207 |
# File 'lib/compony/component.rb', line 205 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
88 89 90 91 92 |
# File 'lib/compony/component.rb', line 88 def sub_comp(component_class, **comp_opts) sub = component_class.new(self, index: @sub_comps.count, **comp_opts) @sub_comps << sub return sub end |