Class: Kadmin::Form

Inherits:
Object
  • Object
show all
Extended by:
ActiveModel::Callbacks, ActiveModel::Translation
Includes:
ActiveModel::Validations, ActiveRecord::AttributeAssignment
Defined in:
app/components/kadmin/form.rb

Overview

Parsing is done by using attribute setters. If you have an attribute called name, then add a reader/writer for it, name and name=, and perform the parsing in name=. If there is no parsing to be done, you can simply delegate the method to the underlying model.

If the attribute is a nested form, in the writer, simply instantiate that form, and pass the attributes on to it, then update the model’s association (if any) to reflect the changes.

Validation is performed like on a normal model or ActiveRecord object. If you have no extra validation to perform than that of the model, simply delegate the validate and valid? methods to the model.

To use nested forms, you need to add a reader and a writer. For example, for a form called Person, with potentially X nested Person forms as children, you would have:

Examples:

class PersonForm < Form
  def children
    [@child1, @child2]
  end

  def children_attributes=(attributes)
    ...instantiate subforms and pass attributes...
  end
end

Instance Attribute Summary collapse

Attributes assignment/manipulation collapse

Persistence collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(model) ⇒ Form

Returns a new instance of Form.



47
48
49
50
51
52
# File 'app/components/kadmin/form.rb', line 47

def initialize(model)
  @errors = ActiveModel::Errors.new(self)
  @model = model
  @form_input = {}
  @associated_forms = Hash.new { |hash, key| hash[key] = [] }
end

Instance Attribute Details

#modelActiveModel::Model (readonly)

Returns underlying model to populate.

Returns:

  • (ActiveModel::Model)

    underlying model to populate



43
44
45
# File 'app/components/kadmin/form.rb', line 43

def model
  @model
end

Class Method Details

.delegate_association(association, to:) ⇒ Object

Delegates a specified associations to other another form object

Examples:

delegate_associations :child, :parent, to: 'Forms::PersonForm'


102
103
104
105
106
107
108
109
# File 'app/components/kadmin/form.rb', line 102

def delegate_association(association, to:)
  # add a reader attribute
  class_eval <<~METHOD, __FILE__, __LINE__ + 1
    def #{association}(index = 0)
      return associated_form('#{association}', form_class: '#{to}', index: index)
    end
  METHOD
end

.delegate_attributes(*attributes) ⇒ Object

Delegates the list of attributes to the model, both readers and writers. If the attribute value passed is a hash and not a symbol, assumes it is a hash of one key, whose value is an array contained :reader, :writer, or both.

Examples:

delegate_attributes :first_name, { last_name: [:reader] }

Parameters:

  • attributes (Array<Symbol, Hash<Symbol, Array<Symbol>>>)

    list of attributes to delegate to the model



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'app/components/kadmin/form.rb', line 82

def delegate_attributes(*attributes)
  delegates = attributes.each_with_object([]) do |attribute, acc|
    case attribute
    when Hash
      key, value = attribute.first
      acc << key if value.include?(:reader)
      acc << "#{key}=" if value.include?(:writer)
    when Symbol, String
      acc.push(attribute, "#{attribute}=")
    else
      raise(ArgumentError, 'Attribute must be one of: Hash, Symbol, String')
    end
  end

  delegate(*delegates, to: :model)
end

Instance Method Details

#sanitize_for_mass_assignment(attributes) ⇒ Object

For now, we overload the method to accept all attributes. This is removed in Rails 5, so once we upgrade we can remove the overload.



67
68
69
# File 'app/components/kadmin/form.rb', line 67

def sanitize_for_mass_assignment(attributes)
  return attributes
end

#saveObject



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'app/components/kadmin/form.rb', line 175

def save
  saved = false
  # TODO: Check if the top level transaction is necessary here
  # run_callbacks is already a transaction?
  @model.class.transaction do
    run_callbacks :save do
      saved = @model.save
      @associated_forms.values.flatten do |form|
        saved &&= form.save
      end

      raise ActiveRecord::Rollback unless saved
    end
  end

  return saved
end

#save!Object



193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'app/components/kadmin/form.rb', line 193

def save!
  saved = false
  @model.class.transaction do
    run_callbacks :save do
      saved = @model.save!
      @associated_forms.values.flatten.each do |form|
        saved &&= form.save! # no need to raise anything, save! will do so
      end
    end
  end

  return saved
end

#to_modelObject



54
55
56
# File 'app/components/kadmin/form.rb', line 54

def to_model
  return @model
end