Module: Jsapi::Meta::Model::Attributes

Included in:
Base
Defined in:
lib/jsapi/meta/model/attributes.rb

Constant Summary collapse

DEFAULT_ARRAY =
[].freeze
DEFAULT_HASH =
{}.freeze

Instance Method Summary collapse

Instance Method Details

#attribute(name, type = Object, accessors: %i[add reader writer],, default: nil, default_key: nil, keys: nil, values: nil) ⇒ Object

Defines an attribute.



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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
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
# File 'lib/jsapi/meta/model/attributes.rb', line 11

def attribute(name, type = Object,
              accessors: i[add reader writer],
              default: nil,
              default_key: nil,
              keys: nil,
              values: nil)

  (@attribute_names ||= []) << name.to_sym

  instance_variable_name = "@#{name}"

  case type
  when Array
    # General default
    default ||= DEFAULT_ARRAY

    if accessors.include?(:add) || accessors.include?(:writer)
      singular_name = name.to_s.singularize
      add_method = "add_#{singular_name}"
      type_caster = TypeCaster.new(type.first, values: values, name: singular_name)

      # Attribute writer
      define_method("#{name}=") do |argument|
        instance_variable_set(instance_variable_name, []).tap do
          Array.wrap(argument).each { |element| send(add_method, element) }
        end
      end if accessors.include?(:writer)

      # Add method
      define_method(add_method) do |argument = nil|
        type_caster.cast(argument).tap do |casted_argument|
          if instance_variable_defined?(instance_variable_name)
            instance_variable_get(instance_variable_name)
          else
            instance_variable_set(instance_variable_name, [])
          end << casted_argument
          attribute_changed(name)
        end
      end if accessors.include?(:add)
    end
  when Hash
    # General default
    default ||= DEFAULT_HASH

    singular_name = name.to_s.singularize
    key_type, value_type = type.first
    key_type_caster = TypeCaster.new(key_type, values: keys, name: 'key')

    # Lookup method
    define_method(singular_name) do |key = nil|
      key = default_key if key.to_s.empty?
      send(name)[key_type_caster.cast(key)]
    end if accessors.include?(:reader)

    if accessors.include?(:add) || accessors.include?(:writer)
      add_method = "add_#{singular_name}"
      value_type_caster = TypeCaster.new(value_type, values: values)

      # Attribute writer
      define_method("#{name}=") do |argument|
        instance_variable_set(instance_variable_name, {}).tap do
          Hash(argument).each { |key, value| send(add_method, key, value) }
        end
      end if accessors.include?(:writer)

      # Add method
      define_method(add_method) do |key_or_value, value = nil|
        if value.nil? && default_key
          key = default_key
          value = key_or_value
        else
          key = key_or_value
          key = default_key if key.to_s.empty?
        end
        raise ArgumentError, "key can't be blank" if key.to_s.empty?

        casted_key = key_type_caster.cast(key)
        casted_value = value_type_caster.cast(value)

        if instance_variable_defined?(instance_variable_name)
          instance_variable_get(instance_variable_name)
        else
          instance_variable_set(instance_variable_name, {})
        end[casted_key] = casted_value

        attribute_changed(name)
        casted_value
      end if accessors.include?(:add)
    end
  else
    # Predicate method
    define_method("#{name}?") do
      value = instance_variable_get(instance_variable_name)
      value.nil? ? default || false : value
    end if values == [true, false] && accessors.include?(:reader)

    if accessors.include?(:writer)
      type_caster = TypeCaster.new(type, values: values, name: name)

      # Attribute writer
      define_method("#{name}=") do |argument = nil|
        type_caster.cast(argument).tap do |casted_value|
          instance_variable_set(instance_variable_name, casted_value)
          attribute_changed(name)
        end
      end
    end
  end

  # Attribute reader
  define_method(name) do
    value = instance_variable_get(instance_variable_name)
    value.nil? ? default : value
  end if accessors.include?(:reader)
end

#attribute_namesObject



127
128
129
130
131
132
# File 'lib/jsapi/meta/model/attributes.rb', line 127

def attribute_names
  names = @attribute_names || []
  return names unless superclass.respond_to?(:attribute_names)

  superclass.attribute_names + names
end