Module: Jsapi::Meta::Base::Attributes

Included in:
Model
Defined in:
lib/jsapi/meta/base/attributes.rb

Constant Summary collapse

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

Instance Method Summary collapse

Instance Method Details

#attribute(name, type = Object, default: nil, default_key: nil, keys: nil, read_only: false, 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
126
127
128
# File 'lib/jsapi/meta/base/attributes.rb', line 11

def attribute(name, type = Object,
              default: nil,
              default_key: nil,
              keys: nil,
              read_only: false,
              values: nil)

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

  instance_variable_name = "@#{name}"

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

    unless read_only
      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

      # 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
    end
  when Hash
    singular_name = name.to_s.singularize

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

    # General default
    default ||= DEFAULT_HASH

    # 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

    unless read_only
      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

      # 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
    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]

    unless read_only
      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
end

#attribute_namesObject



130
131
132
133
134
135
# File 'lib/jsapi/meta/base/attributes.rb', line 130

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

  superclass.attribute_names + names
end