Class: ATDIS::Model

Inherits:
Object
  • Object
show all
Includes:
TypeCastAttributes, Validators, ActiveModel::AttributeMethods, ActiveModel::Validations
Defined in:
lib/atdis/model.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(params, timezone) ⇒ Model

Returns a new instance of Model.



153
154
155
156
157
158
159
160
161
162
# File 'lib/atdis/model.rb', line 153

def initialize(params, timezone)
  @timezone = timezone
  @attributes = {}
  @attributes_before_type_cast = {}
  return unless params

  params.each do |attr, value|
    send("#{attr}=", value)
  end
end

Instance Attribute Details

#attributesObject (readonly)

Returns the value of attribute attributes.



48
49
50
# File 'lib/atdis/model.rb', line 48

def attributes
  @attributes
end

#attributes_before_type_castObject (readonly)

Returns the value of attribute attributes_before_type_cast.



48
49
50
# File 'lib/atdis/model.rb', line 48

def attributes_before_type_cast
  @attributes_before_type_cast
end

#json_left_oversObject

Stores any part of the json that could not be interpreted. Usually signals an error if it isn’t empty.



51
52
53
# File 'lib/atdis/model.rb', line 51

def json_left_overs
  @json_left_overs
end

#json_load_errorObject

Stores any part of the json that could not be interpreted. Usually signals an error if it isn’t empty.



51
52
53
# File 'lib/atdis/model.rb', line 51

def json_load_error
  @json_load_error
end

#timezoneObject (readonly)

Returns the value of attribute timezone.



48
49
50
# File 'lib/atdis/model.rb', line 48

def timezone
  @timezone
end

#urlObject

Returns the value of attribute url.



52
53
54
# File 'lib/atdis/model.rb', line 52

def url
  @url
end

Class Method Details

.attribute_keysObject



164
165
166
# File 'lib/atdis/model.rb', line 164

def self.attribute_keys
  attribute_types.keys
end

.attribute_namesObject

Does what the equivalent on Activerecord does



169
170
171
# File 'lib/atdis/model.rb', line 169

def self.attribute_names
  attribute_types.keys.map(&:to_s)
end

.cast(value, type, timezone) ⇒ Object



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
# File 'lib/atdis/model.rb', line 173

def self.cast(value, type, timezone)
  # If it's already the correct type (or nil) then we don't need to do anything
  if value.nil? || value.is_a?(type)
    value
  # Special handling for arrays. When we typecast arrays we actually
  # typecast each member of the array
  elsif value.is_a?(Array)
    value.map { |v| cast(v, type, timezone) }
  elsif type == DateTime
    cast_datetime(value, timezone)
  elsif type == URI
    cast_uri(value)
  elsif type == String
    cast_string(value)
  elsif type == Integer
    cast_integer(value)
  elsif type == RGeo::GeoJSON
    cast_geojson(value)
  # Otherwise try to use Type.interpret to do the typecasting
  elsif type.respond_to?(:interpret)
    type.interpret(value, timezone) if value
  else
    raise
  end
end

.cast_datetime(value, timezone) ⇒ Object

If timezone is given in the string then the datetime is read in using the timezone in the string and then converted to the timezone “zone” If the timezone isn’t given in the string then the datetime is read in using the timezone in “zone”



203
204
205
206
207
# File 'lib/atdis/model.rb', line 203

def self.cast_datetime(value, timezone)
  ActiveSupport::TimeZone.new(timezone).iso8601(value).to_datetime
rescue ArgumentError, KeyError
  nil
end

.cast_geojson(value) ⇒ Object



224
225
226
# File 'lib/atdis/model.rb', line 224

def self.cast_geojson(value)
  RGeo::GeoJSON.decode(hash_symbols_to_string(value))
end

.cast_integer(value) ⇒ Object

This casting allows nil values



220
221
222
# File 'lib/atdis/model.rb', line 220

def self.cast_integer(value)
  value&.to_i
end

.cast_string(value) ⇒ Object



215
216
217
# File 'lib/atdis/model.rb', line 215

def self.cast_string(value)
  value.to_s
end

.cast_uri(value) ⇒ Object



209
210
211
212
213
# File 'lib/atdis/model.rb', line 209

def self.cast_uri(value)
  URI.parse(value)
rescue URI::InvalidURIError
  nil
end

.hash_symbols_to_string(hash) ⇒ Object

Converts {bar: “yes”} to => {“bar” => “yes”}



229
230
231
232
233
234
235
236
237
238
239
# File 'lib/atdis/model.rb', line 229

def self.hash_symbols_to_string(hash)
  if hash.respond_to?(:each_pair)
    result = {}
    hash.each_pair do |key, value|
      result[key.to_s] = hash_symbols_to_string(value)
    end
    result
  else
    hash
  end
end

.interpret(data, timezone) ⇒ Object



90
91
92
93
# File 'lib/atdis/model.rb', line 90

def self.interpret(data, timezone)
  used, unused = partition_by_used(data)
  new(used.merge(json_left_overs: unused), timezone)
end

.partition_by_used(data) ⇒ Object

Partition the data into used and unused by returning [used, unused]



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/atdis/model.rb', line 58

def self.partition_by_used(data)
  used = {}
  unused = {}
  if data.respond_to?(:each)
    data.each do |key, value|
      if attribute_keys.include?(key)
        used[key] = value
      else
        unused[key] = value
      end
    end
  else
    unused = data
  end
  [used, unused]
end

.read_json(text, timezone) ⇒ Object



81
82
83
84
85
86
87
88
# File 'lib/atdis/model.rb', line 81

def self.read_json(text, timezone)
  data = MultiJson.load(text, symbolize_keys: true)
  interpret(data, timezone)
rescue MultiJson::LoadError => e
  a = interpret({ response: [] }, timezone)
  a.json_load_error = e.to_s
  a
end

.read_url(url, timezone) ⇒ Object



75
76
77
78
79
# File 'lib/atdis/model.rb', line 75

def self.read_url(url, timezone)
  r = read_json(RestClient.get(url.to_s).to_str, timezone)
  r.url = url.to_s
  r
end

Instance Method Details

#json_errorsObject



134
135
136
# File 'lib/atdis/model.rb', line 134

def json_errors
  json_errors_local + json_errors_in_children
end

#json_errors_in_childrenObject



120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/atdis/model.rb', line 120

def json_errors_in_children
  r = []
  attributes.each do |attribute_as_string, value|
    attribute = attribute_as_string.to_sym
    if value.respond_to?(:json_errors)
      r += value.json_errors.map { |a, b| [{ attribute => a }, b] }
    elsif value.is_a?(Array)
      f = value.find { |v| v.respond_to?(:json_errors) && !v.json_errors.empty? }
      r += f.json_errors.map { |a, b| [{ attribute => [a] }, b] } if f
    end
  end
  r
end

#json_errors_localObject



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/atdis/model.rb', line 101

def json_errors_local
  r = []
  # First show special json error
  errors.keys.each do |attribute|
    r << [nil, errors[:json]] unless errors[:json].empty?
    # The :json attribute is special
    next if attribute == :json

    e = errors[attribute]
    next if e.empty?

    r << [
      { attribute => attributes_before_type_cast[attribute.to_s] },
      e.map { |m| ErrorMessage["#{attribute} #{m}", m.spec_section] }
    ]
  end
  r
end

#json_left_overs_is_emptyObject



143
144
145
146
147
148
149
150
151
# File 'lib/atdis/model.rb', line 143

def json_left_overs_is_empty
  return unless json_left_overs && !json_left_overs.empty?

  # We have extra parameters that shouldn't be there
  errors.add(
    :json,
    ErrorMessage["Unexpected parameters in json data: #{MultiJson.dump(json_left_overs)}", "4"]
  )
end

#json_loaded_correctly!Object



95
96
97
98
99
# File 'lib/atdis/model.rb', line 95

def json_loaded_correctly!
  return unless json_load_error

  errors.add(:json, ErrorMessage["Invalid JSON: #{json_load_error}", nil])
end

#used_attribute?(attribute) ⇒ Boolean

Have we tried to use this attribute?

Returns:

  • (Boolean)


139
140
141
# File 'lib/atdis/model.rb', line 139

def used_attribute?(attribute)
  !attributes_before_type_cast[attribute].nil?
end