Class: Decidim::AttributeObject::Form
- Inherits:
-
Object
- Object
- Decidim::AttributeObject::Form
- Includes:
- ActiveModel::Validations, Model
- Defined in:
- lib/decidim/attribute_object/form.rb
Overview
This is the main Form class that provides the functionality for all core forms that take in user input from the user interface forms and converts the inputs to expected formats.
This replaces the Rectify::Form classes in Decidim which used to provide similar functionality. The API provided by this class is largely compatible with ‘Rectify::Form`.
Direct Known Subclasses
Constant Summary
Constants included from TypeMap
TypeMap::Boolean, TypeMap::Decimal
Instance Attribute Summary collapse
-
#context ⇒ Object
readonly
Returns the value of attribute context.
Class Method Summary collapse
- .ensure_hash(object) ⇒ Object
- .from_model(model) ⇒ Object
- .from_params(params, additional_params = {}) ⇒ Object
- .hash_from(params) ⇒ Object
- .infer_model_name ⇒ Object
- .mimic(model_name) ⇒ Object
- .mimicked_model_name ⇒ Object
-
.model_name ⇒ Object
Converts the mimicked name to ActiveModel naming.
Instance Method Summary collapse
-
#map_model(_model) ⇒ Object
Use the map_model method within the form implementations to map any custom form-specific attributes from the model to the form.
- #persisted? ⇒ Boolean
- #to_key ⇒ Object
-
#to_model ⇒ Object
Required for the active model naming to work correctly to form the HTML class attributes for the form elements (e.g. edit_account instead of edit_account_form).
- #to_param ⇒ Object
-
#valid?(_context = nil) ⇒ Boolean
Although we are running the nested attributes validations through the NestedValidator, we still need to check for the errors in the nested attributes after the main validations are run in case the main validations are adding errors to some of the nested attributes.
- #with_context(new_context) ⇒ Object
Methods included from Model
#[], #[]=, #attributes, #attributes_with_values, #initialize, #to_h
Instance Attribute Details
#context ⇒ Object (readonly)
Returns the value of attribute context.
16 17 18 |
# File 'lib/decidim/attribute_object/form.rb', line 16 def context @context end |
Class Method Details
.ensure_hash(object) ⇒ Object
85 86 87 88 89 90 91 |
# File 'lib/decidim/attribute_object/form.rb', line 85 def self.ensure_hash(object) if object.is_a?(Hash) object else {} end end |
.from_model(model) ⇒ Object
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/decidim/attribute_object/form.rb', line 40 def self.from_model(model) form_attributes = attribute_types.keys.each_with_object({}) do |key, attrs| next unless model.respond_to?(key) value = model.send(key) attrs[key] = case value when ActiveStorage::Attached::One value..try(:blob) when ActiveStorage::Attached::Many value..map(&:blob) when ActiveRecord::Associations::CollectionProxy, ActiveRecord::Relation, Array if attribute_types[key].type == :array value else # This is a sub-form that needs to read the properties directly # from the original model. We cannot pass an array here as it # would be passed to the form constructor causing an error. model end else value end end form = new(form_attributes) form.map_model(model) form end |
.from_params(params, additional_params = {}) ⇒ Object
71 72 73 74 75 76 77 78 |
# File 'lib/decidim/attribute_object/form.rb', line 71 def self.from_params(params, additional_params = {}) params_hash = hash_from(params) mimicked_params = ensure_hash(params_hash[mimicked_model_name]) attributes_hash = params_hash.merge(mimicked_params).merge(additional_params) new(attributes_hash) end |
.hash_from(params) ⇒ Object
80 81 82 83 |
# File 'lib/decidim/attribute_object/form.rb', line 80 def self.hash_from(params) params = params.to_unsafe_h if params.respond_to?(:to_unsafe_h) params.with_indifferent_access end |
.infer_model_name ⇒ Object
26 27 28 29 30 31 32 33 |
# File 'lib/decidim/attribute_object/form.rb', line 26 def self.infer_model_name return :form unless name class_name = name.split("::").last return :form if class_name == "Form" class_name.chomp("Form").underscore.to_sym end |
.mimic(model_name) ⇒ Object
18 19 20 |
# File 'lib/decidim/attribute_object/form.rb', line 18 def self.mimic(model_name) @model_name = model_name.to_s.underscore.to_sym end |
.mimicked_model_name ⇒ Object
22 23 24 |
# File 'lib/decidim/attribute_object/form.rb', line 22 def self.mimicked_model_name @model_name || infer_model_name end |
.model_name ⇒ Object
Converts the mimicked name to ActiveModel naming.
36 37 38 |
# File 'lib/decidim/attribute_object/form.rb', line 36 def self.model_name ActiveModel::Name.new(self, nil, mimicked_model_name.to_s) end |
Instance Method Details
#map_model(_model) ⇒ Object
Use the map_model method within the form implementations to map any custom form-specific attributes from the model to the form.
114 |
# File 'lib/decidim/attribute_object/form.rb', line 114 def map_model(_model); end |
#persisted? ⇒ Boolean
93 94 95 |
# File 'lib/decidim/attribute_object/form.rb', line 93 def persisted? id.present? && id.to_i.positive? end |
#to_key ⇒ Object
97 98 99 |
# File 'lib/decidim/attribute_object/form.rb', line 97 def to_key [id] end |
#to_model ⇒ Object
Required for the active model naming to work correctly to form the HTML class attributes for the form elements (e.g. edit_account instead of edit_account_form).
104 105 106 |
# File 'lib/decidim/attribute_object/form.rb', line 104 def to_model self end |
#to_param ⇒ Object
108 109 110 |
# File 'lib/decidim/attribute_object/form.rb', line 108 def to_param id.to_s end |
#valid?(_context = nil) ⇒ Boolean
Although we are running the nested attributes validations through the NestedValidator, we still need to check for the errors in the nested attributes after the main validations are run in case the main validations are adding errors to some of the nested attributes.
An example of such form is the Decidim::Budgets::Admin::ComponentForm which adds errors to the sub-attribute “settings” during its own validations. Because these errors are not added to the main form object, the main form object would be interpreted as valid without checking the sub-attribute validations.
This preserves the backwards compatibility with Rectify::Form which did the validations in this order and fails the main record validation in case one of the nested attributes is not valid. This is needed e.g. for the customized component validations (e.g. Budgets component form).
156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/decidim/attribute_object/form.rb', line 156 def valid?(_context = nil) super && self.class.attribute_types.none? do |name, type| value = public_send(name) if value.is_a?(Decidim::AttributeObject::Model) || (type.respond_to?(:validate_nested?) && type.validate_nested?) _value_has_errors?(value) else false end end end |
#with_context(new_context) ⇒ Object
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/decidim/attribute_object/form.rb', line 116 def with_context(new_context) @context = if new_context.is_a?(Hash) OpenStruct.new(new_context) else new_context end attributes.each do |_name, value| case value when Array value.each do |v| next unless v.respond_to?(:with_context) v.with_context(context) end else next unless value.respond_to?(:with_context) value.with_context(context) end end self end |