Class: Axn::FormObject

Inherits:
Object
  • Object
show all
Includes:
ActiveModel::Model
Defined in:
lib/axn/form_object.rb

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Class Attribute Details

.field_namesObject

Returns the value of attribute field_names.



18
19
20
# File 'lib/axn/form_object.rb', line 18

def field_names
  @field_names
end

Class Method Details

.attr_accessor(*attributes) ⇒ Object

Override attr_accessor to track field names for automatic #to_h support



28
29
30
31
32
33
34
35
36
# File 'lib/axn/form_object.rb', line 28

def attr_accessor(*attributes)
  # Initialize field_names if not already set
  self.field_names ||= []

  # Add new attributes to the field_names array
  self.field_names += attributes.map(&:to_sym)

  super
end

.inherited(subclass) ⇒ Object



20
21
22
23
24
25
# File 'lib/axn/form_object.rb', line 20

def inherited(subclass)
  # Inherit field_names from parent class, or initialize as empty array if parent doesn't have any
  subclass.field_names = (field_names || []).dup

  super
end

.nested_forms(**kwargs) ⇒ Object Also known as: nested_form

Add support for nested forms



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/axn/form_object.rb', line 50

def nested_forms(**kwargs)
  kwargs.each do |name, klass|
    validates name, presence: true

    define_method("#{name}=") do |params|
      return instance_variable_set("@#{name}", nil) if params.nil?

      child_params = params.dup

      # Automatically inject the parent into the child form if it has a parent= method
      child_params[:parent_form] = self if klass.instance_methods.include?(:parent_form=)

      instance_variable_set("@#{name}", klass.new(child_params))
    end

    validation_method_name = :"validate_#{name}_form"
    validate validation_method_name
    define_method(validation_method_name) do
      return if public_send(name).nil? || public_send(name).valid?

      public_send(name).errors.each do |error|
        errors.add("#{name}.#{error.attribute}", error.message)
      end
    end
    private validation_method_name
  end
end

.validates(*attributes) ⇒ Object

Automatically attr_accessor any attribute for which we add a validation



39
40
41
42
43
44
45
46
47
# File 'lib/axn/form_object.rb', line 39

def validates(*attributes)
  our_attributes = attributes.dup

  # Pulled from upstream: https://github.com/rails/rails/blob/6f0d1ad14b92b9f5906e44740fce8b4f1c7075dc/activemodel/lib/active_model/validations/validates.rb#L106
  our_attributes.extract_options!
  our_attributes.each { |attr| attr_accessor(attr) }

  super
end

Instance Method Details

#to_hObject



80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/axn/form_object.rb', line 80

def to_h
  return {} if self.class.field_names.nil?

  self.class.field_names.each_with_object({}) do |field_name, hash|
    next unless respond_to?(field_name)

    # Skip parent_form to avoid infinite recursion with circular references
    next if field_name == :parent_form

    value = public_send(field_name)
    hash[field_name] = value.is_a?(Axn::FormObject) ? value.to_h : value
  end
end