Class: FormObject::Base
- Inherits:
-
Object
- Object
- FormObject::Base
- Includes:
- ActiveModel::AttributeAssignment, ActiveModel::Model, MultiModelWizard::DynamicValidation
- Defined in:
- lib/form_object/base.rb
Constant Summary collapse
- ATTRIBUTES =
These are the default atrributes for all form objects
%i[ current_step new_form ]
Instance Attribute Summary collapse
-
#dynamic_models ⇒ Object
readonly
Returns the value of attribute dynamic_models.
-
#extra_attributes ⇒ Object
readonly
Returns the value of attribute extra_attributes.
-
#models ⇒ Object
readonly
Returns the value of attribute models.
-
#multiple_instance_models ⇒ Object
readonly
Returns the value of attribute multiple_instance_models.
Class Method Summary collapse
-
.create_form {|instance| ... } ⇒ Object
Creates a new instance of the form object with all models and configuration.
-
.form_steps ⇒ Object
Needs to be overridden by child class.
Instance Method Summary collapse
-
#add_dynamic_model(prefix:, model:) ⇒ Object
This method should be used when instantiating a new object.
-
#add_extra_attributes(prefix: nil, attributes:, model: nil) ⇒ Object
This method should be used when instantiating a new object.
-
#add_model(model, prefix: nil) ⇒ Object
The add_model is an instance method that is used for adding ActiveRecord models.
-
#add_multiple_instance_model(attribute_name: nil, model:, instances: []) ⇒ Object
The add_multiple_instance_model is an instance method that is used for adding ActiveRecord models multiple instance models are models that would be child models in a has_many belongs_to relationship EXAMPLE: Car has_many parts In this example the multiple instance would be parts because a car can have an infinte number of parts.
-
#as_json ⇒ Object
This method is used to turn the attributes of the form into a stringified object that resembles json.
-
#attribute_keys ⇒ Object
Returns an list of all attribute names as symbols.
-
#attributes ⇒ Object
Gets all of the form objects present attributes and returns them in a hash.
-
#attributes_for(model) ⇒ Object
Returns all of the attributes for a model.
-
#create ⇒ Object
Create all of the models from the form object and their realations EXAMPLE: def create created = false begin ActiveRecord::Base.transaction do car = Car.new(attributes_for(Car)) car.parts = car_parts car.save! end created = true rescue StandardError => err return created end created end.
-
#first_step? ⇒ Boolean
Boolean method returns if the object is on the first step or not.
-
#initialize ⇒ Base
constructor
A new instance of Base.
-
#invalidate!(error_msg = nil) ⇒ Object
Add a custom error message and makes the object invalid.
-
#persist! ⇒ Object
Persist is used to update or create all of the models from the form object.
-
#required_for_step?(step) ⇒ Boolean
This method is used to help validate the form object.
-
#save ⇒ Object
Checks if the form and its attributes are valid.
-
#set_attributes(attributes_hash) ⇒ Object
Takes a hash of key value pairs and assigns those attributes values to its corresponding methods/instance variables.
-
#update ⇒ Object
Update all of the models from the form object and their realations EXAMPLE: def update updated = false begin ActiveRecord::Base.transaction do car = Car.find(car_id) car.attributes = attributes_for(Car) car.parts = car_parts car.save! end updated = true rescue StandardError => err return updated end updated end.
-
#validate_attributes(*attributes) ⇒ Object
Given n number of atrribute names this method will iterate over each attribute and validate that attribute using the model that the attribute orignated from to validate it the an DynamicValidation module and is not built in with ActiveRecord The model errors are using the original attribute names.
-
#validate_multiple_instance_model(attribute) ⇒ Object
Much like #validate_attributes this method will validate the attributes of a instance model using the original model the an DynamicValidation module and is not built in with ActiveRecord The model errors are using the original attribute names.
Methods included from MultiModelWizard::DynamicValidation
#valid_attribute?, #validate_attribute_with_message
Constructor Details
#initialize ⇒ Base
Returns a new instance of Base.
48 49 50 51 52 53 54 |
# File 'lib/form_object/base.rb', line 48 def initialize @models = [] @dynamic_models = [] @multiple_instance_models = [] @extra_attributes = [] @new_form = true end |
Instance Attribute Details
#dynamic_models ⇒ Object (readonly)
Returns the value of attribute dynamic_models.
39 40 41 |
# File 'lib/form_object/base.rb', line 39 def dynamic_models @dynamic_models end |
#extra_attributes ⇒ Object (readonly)
Returns the value of attribute extra_attributes.
39 40 41 |
# File 'lib/form_object/base.rb', line 39 def extra_attributes @extra_attributes end |
#models ⇒ Object (readonly)
Returns the value of attribute models.
39 40 41 |
# File 'lib/form_object/base.rb', line 39 def models @models end |
#multiple_instance_models ⇒ Object (readonly)
Returns the value of attribute multiple_instance_models.
39 40 41 |
# File 'lib/form_object/base.rb', line 39 def multiple_instance_models @multiple_instance_models end |
Class Method Details
.create_form {|instance| ... } ⇒ Object
This is how all forms should be instantiated
Creates a new instance of the form object with all models and configuration
18 19 20 21 22 23 |
# File 'lib/form_object/base.rb', line 18 def create_form instance = new yield(instance) instance.send(:init_attributes) instance end |
.form_steps ⇒ Object
This method needs to be overridden with an array of symbols
Needs to be overridden by child class.
28 29 30 |
# File 'lib/form_object/base.rb', line 28 def form_steps raise NotImplementedError end |
Instance Method Details
#add_dynamic_model(prefix:, model:) ⇒ Object
model can be an instance or the class
This method should be used when instantiating a new object. It is used to add dynamic models to the form object. Dynamic models are models that share a base class and are of the same family but can vary depending on child class Example: A Truck model, Racecar model, and a Semi model who all have a base class of Vehicle This method allows your form to recieve any of these models and keep the UI and method calls the same.
231 232 233 234 235 236 |
# File 'lib/form_object/base.rb', line 231 def add_dynamic_model(prefix:, model:) raise ArgumentError, 'Prefix must be a String' unless prefix.is_a?(String) model_is_activerecord?(model) @dynamic_models << { prefix: prefix, model: instance_of_model(model) } end |
#add_extra_attributes(prefix: nil, attributes:, model: nil) ⇒ Object
to have these attributes validated using the #validate_attributes method you must pass in a model
model can be an instance or the class
attributes should be an array of symbols
This method should be used when instantiating a new object. It is used to add extra attributes to the form object that may not be accessible from the models passed in.
207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
# File 'lib/form_object/base.rb', line 207 def add_extra_attributes(prefix: nil, attributes:, model: nil ) if prefix.present? raise ArgumentError, 'Prefix must be a String' unless prefix.is_a?(String) end raise ArgumentError, 'All attributes must be Symbols' unless attributes.all? { |x| x.is_a?(Symbol) } model_is_activerecord?(model) hash = { prefix: prefix || model_prefix(model), attributes: attributes, model: model } extra_attributes << hash end |
#add_model(model, prefix: nil) ⇒ Object
The add_model is an instance method that is used for adding ActiveRecord models
262 263 264 265 266 267 268 269 270 271 |
# File 'lib/form_object/base.rb', line 262 def add_model(model, prefix: nil) if prefix.present? raise ArgumentError, 'Prefix must be a String' unless prefix.is_a?(String) end model_is_activerecord?(model) hash = { prefix: prefix || model_prefix(model), model: instance_of_model(model) } @models << hash end |
#add_multiple_instance_model(attribute_name: nil, model:, instances: []) ⇒ Object
The add_multiple_instance_model is an instance method that is used for adding ActiveRecord models multiple instance models are models that would be child models in a has_many belongs_to relationship EXAMPLE: Car has_many parts In this example the multiple instance would be parts because a car can have an infinte number of parts
246 247 248 249 250 251 252 253 254 255 256 |
# File 'lib/form_object/base.rb', line 246 def add_multiple_instance_model(attribute_name: nil, model:, instances: []) if attribute_name.present? raise ArgumentError, 'Attribute name must be a String' unless attribute_name.is_a?(String) end model_is_activerecord?(model) attribute_name = attribute_name || model_prefix(model, pluralize: true) hash = { attribute_name: attribute_name, model: instance_of_model(model), instances: instances } @multiple_instance_models << hash end |
#as_json ⇒ Object
This method is used to turn the attributes of the form into a stringified object that resembles json
275 276 277 278 279 |
# File 'lib/form_object/base.rb', line 275 def as_json instance_variables.each_with_object({}) do |var, obj| obj[var.to_s.gsub('@','')] = instance_variable_get(var) end.stringify_keys end |
#attribute_keys ⇒ Object
Returns an list of all attribute names as symbols
99 100 101 |
# File 'lib/form_object/base.rb', line 99 def attribute_keys ATTRIBUTES end |
#attributes ⇒ Object
This method will only return a key value pair for attributes that are not nil
It ignores the models arrays, errors, etc.
Gets all of the form objects present attributes and returns them in a hash
84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/form_object/base.rb', line 84 def attributes hash = ActiveSupport::HashWithIndifferentAccess.new instance_variables.each_with_object(hash) do |attribute, object| next if %i[@errors @validation_context @models @dynamic_models @multiple_instance_models @extra_attributes].include?(attribute) key = attribute.to_s.gsub('@','').to_sym object[key] = self.instance_variable_get(attribute) end end |
#attributes_for(model) ⇒ Object
If you dont pass a model to extra attributes they will not show up here
attributes for model does not work for multiple_instance_models
Returns all of the attributes for a model
108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/form_object/base.rb', line 108 def attributes_for(model) model_is_activerecord?(model) hash = ActiveSupport::HashWithIndifferentAccess.new attribute_lookup.each_with_object(hash) do |value, object| lookup = value[1] form_attribute = value[0] object[lookup[:original_method]] = attributes[form_attribute] if lookup[:model] == model.name end end |
#create ⇒ Object
this should be done in an ActiveRecord transaction block
Create all of the models from the form object and their realations EXAMPLE: def create
created = false
begin
ActiveRecord::Base.transaction do
car = Car.new(attributes_for(Car))
car.parts = car_parts
car.save!
end
created = true
rescue StandardError => err
return created
end
created
end
317 318 319 |
# File 'lib/form_object/base.rb', line 317 def create true end |
#first_step? ⇒ Boolean
Boolean method returns if the object is on the first step or not
73 74 75 76 77 78 |
# File 'lib/form_object/base.rb', line 73 def first_step? return false if current_step.nil? return true unless current_step.to_sym form_steps.first == current_step.to_sym end |
#invalidate!(error_msg = nil) ⇒ Object
Add a custom error message and makes the object invalid
66 67 68 69 |
# File 'lib/form_object/base.rb', line 66 def invalidate!(error_msg = nil) errors.add(:associated_model, error_msg) unless error_msg.nil? errors.add(:associated_model, 'could not be properly save') end |
#persist! ⇒ Object
the create method and update method that this method use will have to manually implemented by the child class
the create and update need to return a boolean based on their success or failure to udpate
Persist is used to update or create all of the models from the form object
295 296 297 |
# File 'lib/form_object/base.rb', line 295 def persist! new_form ? create : update end |
#required_for_step?(step) ⇒ Boolean
This method is used to help validate the form object. Use required for step to do step contional validations of attributes
284 285 286 287 288 289 |
# File 'lib/form_object/base.rb', line 284 def required_for_step?(step) # note: this line is specific if using the wicked gem return true if current_step == 'wicked_finish' || current_step.nil? form_steps.index(step.to_sym) <= form_steps.index(current_step.to_sym) end |
#save ⇒ Object
This method is here because the Wicked gem automagically runs this methdo to move to the next step
This method needs return a boolean after attempting to create the records
Checks if the form and its attributes are valid
60 61 62 |
# File 'lib/form_object/base.rb', line 60 def save valid? end |
#set_attributes(attributes_hash) ⇒ Object
If you give it a key that is not a defined method of the class it will simply move on to the next
Takes a hash of key value pairs and assigns those attributes values to its corresponding methods/instance variables
125 126 127 128 129 130 131 132 133 134 |
# File 'lib/form_object/base.rb', line 125 def set_attributes(attributes_hash) attributes_hash.each do |pair| key = "#{pair[0].to_s}=" value = pair[1] self.send(key, value) rescue NoMethodError next end self end |
#update ⇒ Object
this should be done in an ActiveRecord transaction block
Update all of the models from the form object and their realations EXAMPLE: def update
updated = false
begin
ActiveRecord::Base.transaction do
car = Car.find(car_id)
car.attributes = attributes_for(Car)
car.parts = car_parts
car.save!
end
updated = true
rescue StandardError => err
return updated
end
updated
end
340 341 342 |
# File 'lib/form_object/base.rb', line 340 def update true end |
#validate_attributes(*attributes) ⇒ Object
this method uses a special method #validate_attribute_with_message this method comes from
this method will add the model errors to your object instance and invalidate it.
Given n number of atrribute names this method will iterate over each attribute and validate that attribute using the model that the attribute orignated from to validate it the an DynamicValidation module and is not built in with ActiveRecord The model errors are using the original attribute names
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/form_object/base.rb', line 145 def validate_attributes(*attributes) raise ArgumentError, 'Attributes must be a Symbol' unless attributes.all? { |x| x.is_a?(Symbol) } attributes.map do |single_attr| unless respond_to?(single_attr) raise FormObject::AttributeNameError, "#{single_attr.to_s} is not a valid attribute of this form object" end original_attribute = attribute_lookup.dig(single_attr.to_sym, :original_method) attribute_hash = { "#{original_attribute}": send(single_attr) } instance = attribute_lookup.dig(single_attr.to_sym, :model)&.constantize&.new instance&.send("#{original_attribute}=", send(single_attr)) next if instance.nil? validation = (attribute_hash, model_instance: instance) if validation.valid.eql?(false) validation..each { |err| errors.add(single_attr.to_sym, err) } end validation.valid end.compact.all?(true) end |
#validate_multiple_instance_model(attribute) ⇒ Object
this method uses a special method #validate_attribute_with_message this method comes from
this method will add the model errors to your object instance and invalidate it.
Much like #validate_attributes this method will validate the attributes of a instance model using the original model the an DynamicValidation module and is not built in with ActiveRecord The model errors are using the original attribute names
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/form_object/base.rb', line 176 def validate_multiple_instance_model(attribute) raise ArgumentError, 'Attribute must be a Symbol' unless attribute.is_a?(Symbol) unless respond_to?(attribute) raise FormObject::AttributeNameError, "#{attribute.to_s} is not a valid attribute of this form object" end model_instance = attribute_lookup.dig(attribute.to_sym, :model)&.constantize&.new return nil if model_instance.nil? send(attribute).map do |hash_instance| hash_instance.map do |key, value| model_instance&.send("#{key}=", value) validation = ({ "#{key}": value }, model_instance: model_instance ) if validation.valid.eql?(false) validation..each { |err| errors.add(attribute.to_sym, err) } end validation.valid end end.compact.all?(true) end |