Module: ModelAttribute::InstanceMethods

Defined in:
lib/model_attribute.rb

Instance Method Summary collapse

Instance Method Details

#==(other) ⇒ Object Also known as: eql?



98
99
100
101
102
103
104
105
# File 'lib/model_attribute.rb', line 98

def ==(other)
  return true if equal?(other)
  if respond_to?(:id)
    other.kind_of?(self.class) && id == other.id
  else
    other.kind_of?(self.class) && attributes == other.attributes
  end
end

#attributesObject



86
87
88
89
90
# File 'lib/model_attribute.rb', line 86

def attributes
  self.class.attributes.each_with_object({}) do |name, attributes|
    attributes[name] = read_attribute(name)
  end
end

#attributes_for_jsonObject

Attributes suitable for serializing to a JSON string.

- Attribute keys are strings (for 'strict' JSON dumping).
- Attributes with a nil value are omitted to speed serialization.
- :time attributes are serialized as an Integer giving the number of
  milliseconds since the epoch.


118
119
120
121
122
123
124
125
126
# File 'lib/model_attribute.rb', line 118

def attributes_for_json
  self.class.attributes.each_with_object({}) do |name, attributes|
    value = read_attribute(name)
    unless value.nil?
      value = (value.to_f * 1000).to_i if value.is_a? Time
      attributes[name.to_s] = value
    end
  end
end

#cast(value, type) ⇒ Object



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/model_attribute.rb', line 155

def cast(value, type)
  return nil if value.nil?

  case type
  when :integer
    int = Integer(value)
    float = Float(value)
    raise "Can't cast #{value.inspect} to an integer without loss of precision" unless int == float
    int
  when :boolean
    if !!value == value
      value
    elsif value == 't'
      true
    elsif value == 'f'
      false
    else
      raise "Can't cast #{value.inspect} to boolean"
    end
  when :time
    case value
    when Time
      value
    when Date, DateTime
      value.to_time
    when Integer
      # Assume milliseconds since epoch.
      Time.at(value / 1000.0)
    when Numeric
      # Numeric, but not an integer. Assume seconds since epoch.
      Time.at(value)
    else
      Time.parse(value)
    end
  when :string
    String(value)
  when :json
    if Json.valid?(value)
      value
    else
      raise "JSON only supports nil, numeric, string, boolean and arrays and hashes of those."
    end
  else
    raise UnsupportedTypeError.new(type)
  end
end

#changesObject



108
109
110
# File 'lib/model_attribute.rb', line 108

def changes
  @changes ||= {} #HashWithIndifferentAccess.new
end

#changes_for_jsonObject

Changed attributes suitable for serializing to a JSON string. Returns a hash from attribute name (as a string) to the new value of that attribute, for attributes that have changed.

- :time attributes are serialized as an Integer giving the number of
  milliseconds since the epoch.
- Unlike attributes_for_json, attributes that have changed to a nil value
  *are* included.


136
137
138
139
140
141
142
143
144
# File 'lib/model_attribute.rb', line 136

def changes_for_json
  hash = {}
  changes.each do |attr_name, (_old_value, new_value)|
    new_value = (new_value.to_f * 1000).to_i if new_value.is_a? Time
    hash[attr_name.to_s] = new_value
  end

  hash
end

#inspectObject

Includes the class name and all the attributes and their values. e.g. “#<User id: 1, paid: true, name: "Fred", created_at: 2014-12-25 08:00:00 +0000>”



148
149
150
151
152
153
# File 'lib/model_attribute.rb', line 148

def inspect
  attribute_string = self.class.attributes.map do |key|
    "#{key}: #{read_attribute(key).inspect}"
  end.join(', ')
  "#<#{self.class} #{attribute_string}>"
end

#read_attribute(name) ⇒ Object



77
78
79
80
81
82
83
84
# File 'lib/model_attribute.rb', line 77

def read_attribute(name)
  ivar_name = "@#{name}"
  if instance_variable_defined?(ivar_name)
    instance_variable_get(ivar_name)
  elsif !self.class.attributes.include?(name.to_sym)
    raise InvalidAttributeNameError.new(name)
  end
end

#set_attributes(attributes, can_set_private_attrs = false) ⇒ Object



92
93
94
95
96
# File 'lib/model_attribute.rb', line 92

def set_attributes(attributes, can_set_private_attrs = false)
  attributes.each do |key, value|
    send("#{key}=", value) if respond_to?("#{key}=", can_set_private_attrs)
  end
end

#write_attribute(name, value, type = nil) ⇒ Object



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
# File 'lib/model_attribute.rb', line 51

def write_attribute(name, value, type = nil)
  name = name.to_sym

  # Don't want to expose attribute types as a method on the class, so access
  # via a back door.
  type ||= self.class.instance_variable_get('@attribute_types')[name]
  raise InvalidAttributeNameError.new(name) unless type

  value = cast(value, type)
  return if value == read_attribute(name)

  if changes.has_key? name
    original = changes[name].first
  else
    original = read_attribute(name)
  end

  if original == value
    changes.delete(name)
  else
    changes[name] = [original, value]
  end

  instance_variable_set("@#{name}", value)
end