Module: Compony
- Defined in:
- lib/compony.rb,
lib/compony/engine.rb,
lib/compony/version.rb,
lib/compony/component.rb,
lib/compony/model_mixin.rb,
lib/compony/view_helpers.rb,
lib/compony/components/new.rb,
lib/compony/components/edit.rb,
lib/compony/components/form.rb,
lib/compony/request_context.rb,
lib/compony/controller_mixin.rb,
lib/compony/model_fields/url.rb,
lib/compony/natural_ordering.rb,
lib/compony/components/button.rb,
lib/compony/model_fields/base.rb,
lib/compony/model_fields/date.rb,
lib/compony/model_fields/text.rb,
lib/compony/model_fields/time.rb,
lib/compony/components/destroy.rb,
lib/compony/model_fields/color.rb,
lib/compony/model_fields/email.rb,
lib/compony/model_fields/float.rb,
lib/compony/model_fields/phone.rb,
lib/compony/model_fields/string.rb,
lib/compony/components/with_form.rb,
lib/compony/model_fields/boolean.rb,
lib/compony/model_fields/decimal.rb,
lib/compony/model_fields/integer.rb,
lib/compony/model_fields/currency.rb,
lib/compony/model_fields/datetime.rb,
lib/compony/method_accessible_hash.rb,
lib/compony/model_fields/rich_text.rb,
lib/compony/model_fields/attachment.rb,
lib/compony/model_fields/percentage.rb,
lib/compony/model_fields/anchormodel.rb,
lib/compony/model_fields/association.rb,
lib/compony/component_mixins/resourceful.rb,
lib/compony/component_mixins/default/labelling.rb,
lib/compony/component_mixins/default/standalone.rb,
lib/compony/component_mixins/default/standalone/verb_dsl.rb,
lib/compony/component_mixins/default/standalone/standalone_dsl.rb,
lib/compony/component_mixins/default/standalone/resourceful_verb_dsl.rb
Overview
Root module, containing confguration and pure helpers. For
the setters, create an initializer config/initializers/compony.rb
and call
them from there.
Defined Under Namespace
Modules: ComponentMixins, Components, ControllerMixin, ModelFields, ModelMixin, Version, ViewHelpers Classes: Component, Engine, MethodAccessibleHash, NaturalOrdering, RequestContext
Class Method Summary collapse
-
.authentication_before_action ⇒ Object
Getter for the name of the Rails
before_action
that enforces authentication. -
.authentication_before_action=(authentication_before_action) ⇒ Object
Setter for the name of the Rails
before_action
that should be called to ensure that users are authenticated before accessing the component. -
.button(comp_name_or_cst_or_class, model_or_family_name_or_cst = nil, label_opts: nil, params: nil, feasibility_action: nil, feasibility_target: nil, method: nil, standalone_name: nil, **override_kwargs) ⇒ Object
Given a component and a family/model, this instanciates and returns a button component.
-
.button_component_class ⇒ Object
Getter for the global button component class.
-
.button_component_class=(button_component_class) ⇒ Object
Setter for the global button component class.
-
.button_defaults ⇒ Object
Getter for current button defaults.
-
.comp_class_for(comp_name_or_cst, model_or_family_name_or_cst) ⇒ Object
Given a component and a family/model, this returns the matching component class if any, or nil if the component does not exist.
-
.comp_class_for!(comp_name_or_cst, model_or_family_name_or_cst) ⇒ Object
Same as Compony#comp_class_for but fails if none found.
-
.content_after_root_comp(&block) ⇒ Object
Setter for a content block that runs after the root component gets rendered (standalone only).
-
.content_after_root_comp_block ⇒ Object
Getter for content_after_root_comp_block.
-
.content_before_root_comp(&block) ⇒ Object
Setter for a content block that runs before the root component gets rendered (standalone only).
-
.content_before_root_comp_block ⇒ Object
Getter for content_before_root_comp_block.
-
.family_name_for(model_or_family_name_or_cst) ⇒ Object
Given a family name or a model-like class, this returns the suitable family name as String.
-
.model_field_class_for(constant) ⇒ Object
Goes through model_field_namespaces and returns the first hit for the given constant.
-
.model_field_namespaces ⇒ Object
Getter for the global field namespaces.
-
.model_field_namespaces=(model_field_namespaces) ⇒ Object
Setter for the global field namespaces.
-
.path(comp_name_or_cst, model_or_family_name_or_cst = nil, *args_for_path_helper, standalone_name: nil, **kwargs_for_path_helper) ⇒ Object
Generates a Rails path to a component.
-
.path_helper_name ⇒ Object
Given a component and a family, this returns the name of the Rails URL helper returning the path to this component.
The parameters are the same as for Compony.rails_action_name.
Example usage:send("#{path_helper_name(:index, :users)}_url)
. -
.rails_action_name(comp_name_or_cst_or_class, model_or_family_name_or_cst, name = nil) ⇒ Object
Given a component and a family or a component class, this returns the name of the ComponyController action for this component.
Optionally can pass a name for extra standalone configs. -
.root_comp ⇒ Object
Returns the current root component, if any.
-
.with_button_defaults(**keys_to_overwrite, &block) ⇒ Object
Overwrites the keys of the current button defaults by the ones provided during the execution of a given block and restores them afterwords.
Class Method Details
.authentication_before_action ⇒ Object
Getter for the name of the Rails before_action
that enforces authentication.
77 78 79 |
# File 'lib/compony.rb', line 77 def self.authentication_before_action @authentication_before_action end |
.authentication_before_action=(authentication_before_action) ⇒ Object
Setter for the name of the Rails before_action
that should be called to
ensure that users are authenticated before accessing the component. For
instance, implement a method def enforce_authentication
in your
ApplicationController
. In the method, make sure the user has a session and
redirect to the login page if they don't.
The action must be accessible
by ComponyController and the easiest way to achieve this is to implement
the action in your ApplicationController
. If this is never called,
authentication is disabled.
39 40 41 |
# File 'lib/compony.rb', line 39 def self.authentication_before_action=(authentication_before_action) @authentication_before_action = authentication_before_action.to_sym end |
.button(comp_name_or_cst_or_class, model_or_family_name_or_cst = nil, label_opts: nil, params: nil, feasibility_action: nil, feasibility_target: nil, method: nil, standalone_name: nil, **override_kwargs) ⇒ Object
Given a component and a family/model, this instanciates and returns a button component.
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
# File 'lib/compony.rb', line 176 def self.(comp_name_or_cst_or_class, model_or_family_name_or_cst = nil, label_opts: nil, params: nil, feasibility_action: nil, feasibility_target: nil, method: nil, standalone_name: nil, **override_kwargs) label_opts ||= [:label_opts] || {} params ||= [:params] || {} model = model_or_family_name_or_cst.respond_to?(:model_name) ? model_or_family_name_or_cst : nil if comp_name_or_cst_or_class.is_a?(Class) && (comp_name_or_cst_or_class <= Compony::Component) target_comp_instance = comp_name_or_cst_or_class.new(data: model) else target_comp_instance = Compony.comp_class_for!(comp_name_or_cst_or_class, model_or_family_name_or_cst).new(data: model) end feasibility_action ||= [:feasibility_action] || comp_name_or_cst_or_class.to_s.underscore.to_sym feasibility_target ||= [:feasibility_target] || model = { label: target_comp_instance.label(model, **label_opts), icon: target_comp_instance.icon, color: target_comp_instance.color, path: Compony.path(target_comp_instance.comp_name, target_comp_instance.family_name, model, standalone_name:, **params), method:, visible: ->(controller) { target_comp_instance.standalone_access_permitted_for?(controller, standalone_name:, verb: method) } } if feasibility_target .merge!({ enabled: feasibility_target.feasible?(feasibility_action), title: feasibility_target.(feasibility_action).presence }) end .merge!(override_kwargs.symbolize_keys) return Compony..new(**.symbolize_keys) end |
.button_component_class ⇒ Object
Getter for the global button component class.
63 64 65 66 67 |
# File 'lib/compony.rb', line 63 def self. @button_component_class ||= Components::Button @button_component_class = const_get(@button_component_class) if @button_component_class.is_a?(String) return @button_component_class end |
.button_component_class=(button_component_class) ⇒ Object
Setter for the global button component class. This allows you to implement a custom button component and have all Compony button helpers use your custom button component instead of Compony::Components::Button.
15 16 17 |
# File 'lib/compony.rb', line 15 def self.() @button_component_class = end |
.button_defaults ⇒ Object
document params
Getter for current button defaults
232 233 234 |
# File 'lib/compony.rb', line 232 def self. RequestStore.store[:button_defaults] || {} end |
.comp_class_for(comp_name_or_cst, model_or_family_name_or_cst) ⇒ Object
Given a component and a family/model, this returns the matching component class if any, or nil if the component does not exist.
122 123 124 125 126 127 128 129 |
# File 'lib/compony.rb', line 122 def self.comp_class_for(comp_name_or_cst, model_or_family_name_or_cst) family_cst_str = family_name_for(model_or_family_name_or_cst).camelize comp_cst_str = comp_name_or_cst.to_s.camelize return nil unless ::Components.const_defined?(family_cst_str) family_constant = ::Components.const_get(family_cst_str) return nil unless family_constant.const_defined?(comp_cst_str) return family_constant.const_get(comp_cst_str) end |
.comp_class_for!(comp_name_or_cst, model_or_family_name_or_cst) ⇒ Object
Same as Compony#comp_class_for but fails if none found
133 134 135 136 137 |
# File 'lib/compony.rb', line 133 def self.comp_class_for!(comp_name_or_cst, model_or_family_name_or_cst) comp_class_for(comp_name_or_cst, model_or_family_name_or_cst) || fail( "No component found for [#{comp_name_or_cst.inspect}, #{model_or_family_name_or_cst.inspect}]" ) end |
.content_after_root_comp(&block) ⇒ Object
Setter for a content block that runs after the root component gets rendered (standalone only). Usage is the same as content
.
The block runs after render
, i.e. after the last content
block.
52 53 54 55 |
# File 'lib/compony.rb', line 52 def self.content_after_root_comp(&block) fail('`Compony.content_after` requires a block.') unless block_given? @content_after_root_comp_block = block end |
.content_after_root_comp_block ⇒ Object
Getter for content_after_root_comp_block
89 90 91 |
# File 'lib/compony.rb', line 89 def self.content_after_root_comp_block @content_after_root_comp_block end |
.content_before_root_comp(&block) ⇒ Object
Setter for a content block that runs before the root component gets rendered (standalone only). Usage is the same as content
.
The block runs between before_render
and render
, i.e. before the first content
block.
45 46 47 48 |
# File 'lib/compony.rb', line 45 def self.content_before_root_comp(&block) fail('`Compony.content_before` requires a block.') unless block_given? @content_before_root_comp_block = block end |
.content_before_root_comp_block ⇒ Object
Getter for content_before_root_comp_block
83 84 85 |
# File 'lib/compony.rb', line 83 def self.content_before_root_comp_block @content_before_root_comp_block end |
.family_name_for(model_or_family_name_or_cst) ⇒ Object
Given a family name or a model-like class, this returns the suitable family name as String.
222 223 224 225 226 227 228 |
# File 'lib/compony.rb', line 222 def self.family_name_for(model_or_family_name_or_cst) if model_or_family_name_or_cst.respond_to?(:model_name) return model_or_family_name_or_cst.model_name.plural else return model_or_family_name_or_cst.to_s.underscore end end |
.model_field_class_for(constant) ⇒ Object
Goes through model_field_namespaces and returns the first hit for the given constant
263 264 265 266 267 268 269 270 271 |
# File 'lib/compony.rb', line 263 def self.model_field_class_for(constant) model_field_namespaces.each do |model_field_namespace| model_field_namespace = model_field_namespace.constantize if model_field_namespace.is_a?(::String) if model_field_namespace.const_defined?(constant, false) return model_field_namespace.const_get(constant, false) end end fail("No `model_field_namespace` implements ...::#{constant}. Configured namespaces: #{Compony.model_field_namespaces.inspect}") end |
.model_field_namespaces ⇒ Object
Getter for the global field namespaces.
71 72 73 |
# File 'lib/compony.rb', line 71 def self.model_field_namespaces return @model_field_namespaces ||= ['Compony::ModelFields'] end |
.model_field_namespaces=(model_field_namespaces) ⇒ Object
Setter for the global field namespaces. This allows you to implement custom Fields, be it new ones or overrides for existing Compony model fields. Must give an array of strings of namespaces that contain field classes named after the field type. The array is queried in order, if the first namespace does not contain the class we're looking for, the next is considered and so on. The classes defined in the namespace must inherit from Compony::ModelFields::Base
26 27 28 |
# File 'lib/compony.rb', line 26 def self.model_field_namespaces=(model_field_namespaces) @model_field_namespaces = model_field_namespaces end |
.path(comp_name_or_cst, model_or_family_name_or_cst = nil, *args_for_path_helper, standalone_name: nil, **kwargs_for_path_helper) ⇒ Object
Generates a Rails path to a component. Examples: Compony.path(:index, :users)
, Compony.path(:show, User.first)
107 108 109 110 111 112 113 114 115 |
# File 'lib/compony.rb', line 107 def self.path(comp_name_or_cst, model_or_family_name_or_cst = nil, *args_for_path_helper, standalone_name: nil, **kwargs_for_path_helper) # Extract model if any, to get the ID kwargs_for_path_helper.merge!(id: model_or_family_name_or_cst.id) if model_or_family_name_or_cst.respond_to?(:model_name) return Rails.application.routes.url_helpers.send( "#{path_helper_name(comp_name_or_cst, model_or_family_name_or_cst, standalone_name&.to_sym)}_path", *args_for_path_helper, **kwargs_for_path_helper ) end |
.path_helper_name ⇒ Object
Given a component and a family, this returns the name of the Rails URL helper returning the path to this component.
The parameters are the same as for rails_action_name.
Example usage: send("#{path_helper_name(:index, :users)}_url)
144 145 146 |
# File 'lib/compony.rb', line 144 def self.path_helper_name(...) "#{rails_action_name(...)}_comp" end |
.rails_action_name(comp_name_or_cst_or_class, model_or_family_name_or_cst, name = nil) ⇒ Object
Given a component and a family or a component class, this returns the name of the ComponyController action for this component.
Optionally can pass a name for extra standalone configs.
154 155 156 157 158 159 160 161 |
# File 'lib/compony.rb', line 154 def self.rails_action_name(comp_name_or_cst_or_class, model_or_family_name_or_cst, name = nil) if comp_name_or_cst_or_class.is_a?(Class) && (comp_name_or_cst_or_class <= Compony::Component) comp_class = comp_name_or_cst_or_class comp_name_or_cst_or_class = comp_class.comp_name model_or_family_name_or_cst = comp_class.family_name end [name.presence, comp_name_or_cst_or_class.to_s.underscore, family_name_for(model_or_family_name_or_cst)].compact.join('_') end |
.root_comp ⇒ Object
Returns the current root component, if any
214 215 216 |
# File 'lib/compony.rb', line 214 def self.root_comp RequestStore.store[:compony_root_comp] end |
.with_button_defaults(**keys_to_overwrite, &block) ⇒ Object
Overwrites the keys of the current button defaults by the ones provided during the execution of a given block and restores them afterwords. This method is useful when the same set of options is to be given to a multitude of buttons.
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 |
# File 'lib/compony.rb', line 240 def self.(**keys_to_overwrite, &block) # Lazy initialize butto_defaults store if it hasn't been yet RequestStore.store[:button_defaults] ||= {} keys_to_overwrite.transform_keys!(&:to_sym) old_values = {} newly_defined_keys = keys_to_overwrite.keys - RequestStore.store[:button_defaults].keys keys_to_overwrite.each do |key, new_value| # Assign new value old_values[key] = RequestStore.store[:button_defaults][key] RequestStore.store[:button_defaults][key] = new_value end return_value = block.call # Restore previous value keys_to_overwrite.each_key do |key| RequestStore.store[:button_defaults][key] = old_values[key] end # Undefine keys that were not there previously newly_defined_keys.each { |key| RequestStore.store[:button_defaults].delete(key) } return return_value end |