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.



160
161
162
163
164
165
166
167
168
169
# File 'lib/atdis/model.rb', line 160

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



171
172
173
# File 'lib/atdis/model.rb', line 171

def self.attribute_keys
  attribute_types.keys
end

.attribute_namesObject

Does what the equivalent on Activerecord does



176
177
178
# File 'lib/atdis/model.rb', line 176

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

.cast(value, type, timezone) ⇒ Object



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/atdis/model.rb', line 180

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”



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

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

.cast_geojson(value) ⇒ Object



231
232
233
# File 'lib/atdis/model.rb', line 231

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

.cast_integer(value) ⇒ Object

This casting allows nil values



227
228
229
# File 'lib/atdis/model.rb', line 227

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

.cast_string(value) ⇒ Object



222
223
224
# File 'lib/atdis/model.rb', line 222

def self.cast_string(value)
  value.to_s
end

.cast_uri(value) ⇒ Object



216
217
218
219
220
# File 'lib/atdis/model.rb', line 216

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”}



236
237
238
239
240
241
242
243
244
245
246
# File 'lib/atdis/model.rb', line 236

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



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

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



88
89
90
91
92
93
94
95
# File 'lib/atdis/model.rb', line 88

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, ignore_ssl_certificate = false) ⇒ Object



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

def self.read_url(url, timezone, ignore_ssl_certificate = false)
  r = read_json(read_url_raw(url, ignore_ssl_certificate), timezone)
  r.url = url.to_s
  r
end

.read_url_raw(url, ignore_ssl_certificate = false) ⇒ Object



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

def self.read_url_raw(url, ignore_ssl_certificate = false)
  RestClient::Resource.new(
    url.to_s,
    verify_ssl: (OpenSSL::SSL::VERIFY_NONE if ignore_ssl_certificate)
  ).get.to_str
end

Instance Method Details

#json_errorsObject



141
142
143
# File 'lib/atdis/model.rb', line 141

def json_errors
  json_errors_local + json_errors_in_children
end

#json_errors_in_childrenObject



127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/atdis/model.rb', line 127

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



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/atdis/model.rb', line 108

def json_errors_local
  r = []
  # First show special json error
  errors.attribute_names.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



150
151
152
153
154
155
156
157
158
# File 'lib/atdis/model.rb', line 150

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



102
103
104
105
106
# File 'lib/atdis/model.rb', line 102

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)


146
147
148
# File 'lib/atdis/model.rb', line 146

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