Module: OptionsModel::Concerns::Attributes::ClassMethods

Defined in:
lib/options_model/concerns/attributes.rb

Instance Method Summary collapse

Instance Method Details

#attribute(name, cast_type, default: nil, array: false) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/options_model/concerns/attributes.rb', line 9

def attribute(name, cast_type, default: nil, array: false)
  check_not_finalized!

  name = name.to_sym
  check_name_validity! name

  ActiveModel::Type.lookup(cast_type)

  attribute_defaults[name] = default
  default_extractor =
    if default.respond_to?(:call)
      ".call"
    elsif default.duplicable?
      ".deep_dup"
    else
      ""
    end

  generated_attribute_methods.module_eval "    def \#{name}\n      value = attributes[:\#{name}]\n      return value unless value.nil?\n      attributes[:\#{name}] = self.class.attribute_defaults[:\#{name}]\#{default_extractor}\n      attributes[:\#{name}]\n    end\n  STR\n\n  if array\n    generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1\n      def \#{name}=(value)\n        if value.respond_to?(:to_a)\n          attributes[:\#{name}] = value.to_a.map { |i| ActiveModel::Type.lookup(:\#{cast_type}).cast(i) }\n        elsif value.nil?\n          attributes[:\#{name}] = self.class.attribute_defaults[:\#{name}]\#{default_extractor}\n        else\n          raise ArgumentError,\n                \"`value` should respond to `to_a`, but got \\\#{value.class} -- \\\#{value.inspect}\"\n        end\n      end\n    STR\n  else\n    generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1\n      def \#{name}=(value)\n        attributes[:\#{name}] = ActiveModel::Type.lookup(:\#{cast_type}).cast(value)\n      end\n    STR\n\n    generated_attribute_methods.send :alias_method, :\"\#{name}?\", name if cast_type == :boolean\n  end\n\n  attribute_names_for_inlining << name\n\n  self\nend\n", __FILE__, __LINE__ + 1

#attribute_defaultsObject



128
129
130
# File 'lib/options_model/concerns/attributes.rb', line 128

def attribute_defaults
  @attribute_defaults ||= ActiveSupport::HashWithIndifferentAccess.new
end

#attribute_namesObject



144
145
146
# File 'lib/options_model/concerns/attributes.rb', line 144

def attribute_names
  attribute_names_for_nesting + attribute_names_for_inlining
end

#attribute_names_for_inliningObject



140
141
142
# File 'lib/options_model/concerns/attributes.rb', line 140

def attribute_names_for_inlining
  @attribute_names_for_inlining ||= Set.new
end

#attribute_names_for_nestingObject



136
137
138
# File 'lib/options_model/concerns/attributes.rb', line 136

def attribute_names_for_nesting
  @attribute_names_for_nesting ||= Set.new
end

#embeds_one(name, class_name: nil, anonymous_class: nil) ⇒ Object

Raises:

  • (ArgumentError)


87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/options_model/concerns/attributes.rb', line 87

def embeds_one(name, class_name: nil, anonymous_class: nil)
  check_not_finalized!

  raise ArgumentError, "must provide at least one of `class_name` or `anonymous_class`" if class_name.blank? && anonymous_class.nil?

  name = name.to_sym
  check_name_validity! name

  nested_classes[name] = if class_name.present?
    class_name.constantize
  else
    anonymous_class
  end

  generated_attribute_methods.module_eval "    def \#{name}\n      nested_attributes[:\#{name}] ||= self.class.nested_classes[:\#{name}].new(attributes[:\#{name}])\n    end\n  STR\n\n  generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1\n    def \#{name}=(value)\n      klass = self.class.nested_classes[:\#{name}]\n      if value.respond_to?(:to_h)\n        nested_attributes[:\#{name}] = klass.new(value.to_h)\n      elsif value.is_a? klass\n        nested_attributes[:\#{name}] = value\n      elsif value.nil?\n        nested_attributes[:\#{name}] = klass.new\n      else\n        raise ArgumentError,\n              \"`value` should respond to `to_h` or \\\#{klass}, but got \\\#{value.class}\"\n      end\n    end\n  STR\n\n  attribute_names_for_nesting << name\n\n  self\nend\n", __FILE__, __LINE__ + 1

#enum_attribute(name, enum, default: nil, allow_nil: false) ⇒ Object

Raises:

  • (ArgumentError)


64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/options_model/concerns/attributes.rb', line 64

def enum_attribute(name, enum, default: nil, allow_nil: false)
  check_not_finalized!

  raise ArgumentError, "enum should be an Array and can't empty" unless enum.is_a?(Array) && enum.any?

  enum = enum.map(&:to_s)

  attribute name, :string, default: default

  pluralized_name = name.to_s.pluralize
  generated_class_methods.synchronize do
    generated_class_methods.module_eval "    def \#{pluralized_name}\n      %w(\#{enum.join(' ')}).freeze\n    end\n    STR\n\n    validates name, inclusion: { in: enum }, allow_nil: allow_nil\n  end\n\n  self\nend\n", __FILE__, __LINE__ + 1

#finalize!(nested = true) ⇒ Object



152
153
154
155
156
# File 'lib/options_model/concerns/attributes.rb', line 152

def finalize!(nested = true)
  nested_classes.values.each(&:finalize!) if nested

  @finalized = true
end

#finalized?Boolean

Returns:

  • (Boolean)


148
149
150
# File 'lib/options_model/concerns/attributes.rb', line 148

def finalized?
  @finalized ||= false
end

#nested_classesObject



132
133
134
# File 'lib/options_model/concerns/attributes.rb', line 132

def nested_classes
  @nested_classes ||= ActiveSupport::HashWithIndifferentAccess.new
end