atdis 0.3.11 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +46 -0
  3. data/.ruby-version +1 -1
  4. data/.travis.yml +0 -4
  5. data/Gemfile +9 -7
  6. data/Guardfile +4 -3
  7. data/Rakefile +4 -2
  8. data/atdis.gemspec +10 -5
  9. data/lib/atdis.rb +2 -0
  10. data/lib/atdis/feed.rb +32 -24
  11. data/lib/atdis/model.rb +108 -95
  12. data/lib/atdis/models/address.rb +10 -4
  13. data/lib/atdis/models/application.rb +12 -9
  14. data/lib/atdis/models/authority.rb +11 -6
  15. data/lib/atdis/models/document.rb +8 -6
  16. data/lib/atdis/models/event.rb +10 -8
  17. data/lib/atdis/models/info.rb +73 -49
  18. data/lib/atdis/models/land_title_ref.rb +15 -7
  19. data/lib/atdis/models/location.rb +9 -7
  20. data/lib/atdis/models/page.rb +36 -21
  21. data/lib/atdis/models/pagination.rb +91 -32
  22. data/lib/atdis/models/person.rb +7 -5
  23. data/lib/atdis/models/reference.rb +7 -5
  24. data/lib/atdis/models/response.rb +5 -3
  25. data/lib/atdis/models/torrens_title.rb +9 -7
  26. data/lib/atdis/separated_url.rb +17 -15
  27. data/lib/atdis/validators.rb +46 -39
  28. data/lib/atdis/version.rb +3 -1
  29. data/spec/atdis/feed_spec.rb +128 -34
  30. data/spec/atdis/model_spec.rb +124 -51
  31. data/spec/atdis/models/address_spec.rb +18 -9
  32. data/spec/atdis/models/application_spec.rb +222 -155
  33. data/spec/atdis/models/authority_spec.rb +45 -15
  34. data/spec/atdis/models/document_spec.rb +10 -4
  35. data/spec/atdis/models/event_spec.rb +23 -11
  36. data/spec/atdis/models/info_spec.rb +197 -113
  37. data/spec/atdis/models/land_title_ref_spec.rb +32 -16
  38. data/spec/atdis/models/location_spec.rb +75 -60
  39. data/spec/atdis/models/page_spec.rb +244 -135
  40. data/spec/atdis/models/pagination_spec.rb +177 -77
  41. data/spec/atdis/models/person_spec.rb +8 -4
  42. data/spec/atdis/models/reference_spec.rb +29 -16
  43. data/spec/atdis/models/response_spec.rb +2 -1
  44. data/spec/atdis/models/torrens_title_spec.rb +24 -18
  45. data/spec/atdis/separated_url_spec.rb +14 -15
  46. data/spec/spec_helper.rb +14 -10
  47. metadata +62 -33
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a12e4aefcd5295f4cc75677251c79e16d1f8b765
4
- data.tar.gz: ad62ec1348b126335f78f618e7cda5d2cc28cd79
3
+ metadata.gz: 569aa3967302c8529a9c74433f54fd105ebe45d7
4
+ data.tar.gz: 0eb50df65474e2d5bc5787994d58c8a282e87e4a
5
5
  SHA512:
6
- metadata.gz: a873c3b343903e1b9bc4820098d43881e9f8bd473ee2ebd919c0d0e2cd8ab9e4a9bc5454daf6ffa10df8e5e578d82517659bc25ecbe446ba4ce12a756c794f88
7
- data.tar.gz: 997b41425821fb3472a618679f455a5be093955275001284f4f5aa8ede14a07251454e851a87e214b649107e66309c095e7c0cd51b8d388b61bdabc773b4ffda
6
+ metadata.gz: b6aa7bae02ff0fa4a4a2776ef4bbb16f7438f2332f5b3e419f2281abebe8f25e8894a2beb50b325ca3ca24dce598b58f9c0580b5de408f1e4fe69f712dd4bd57
7
+ data.tar.gz: 029202949e54506691b3e5c6bb36f2bb73aafd14e707e4ff328b5f739c0a55762a6b35a2f801a5ccbe5d63c8233d23db6c575d40230bf453c2afc4760268f5e7
@@ -0,0 +1,46 @@
1
+ # Bumping max line length to something a little more reasonable
2
+ Metrics/LineLength:
3
+ Max: 100
4
+
5
+ # Not so worried about top-level class documentation. So...
6
+ Style/Documentation:
7
+ Enabled: false
8
+
9
+ # We prefer double quotes here and it we're making liberal use of multi-line
10
+ # strings so it makes sense to enforce those to be consistent oo
11
+ Style/StringLiterals:
12
+ EnforcedStyle: double_quotes
13
+ ConsistentQuotesInMultiline: true
14
+
15
+ # This one I disagree with. Putting seperators in large numbers makes sense
16
+ # in some circumstances but in others (an example id in a database table)
17
+ # it's just nonsensical. Also, I think this one might also be a bit US centric.
18
+ Style/NumericLiterals:
19
+ Enabled: false
20
+
21
+ # Disable a bunch of metrics to do with code complexity. These as are all
22
+ # a bit hard-nosed. Maybe after we've done a pass with Code Climate we
23
+ # can revisit these
24
+ Metrics/AbcSize:
25
+ Enabled: false
26
+
27
+ Metrics/BlockLength:
28
+ Enabled: false
29
+
30
+ Metrics/ClassLength:
31
+ Enabled: false
32
+
33
+ Metrics/CyclomaticComplexity:
34
+ Enabled: false
35
+
36
+ Metrics/MethodLength:
37
+ Enabled: false
38
+
39
+ Metrics/ModuleLength:
40
+ Enabled: false
41
+
42
+ Metrics/ParameterLists:
43
+ Enabled: false
44
+
45
+ Metrics/PerceivedComplexity:
46
+ Enabled: false
@@ -1 +1 @@
1
- ruby-2.0.0-p353
1
+ ruby-2.3.1
@@ -1,6 +1,2 @@
1
- language: ruby
2
- rvm:
3
- - "1.9.3"
4
- - "2.0.0"
5
1
  # uncomment this line if your project needs to run something other than `rake`:
6
2
  # script: bundle exec rspec spec
data/Gemfile CHANGED
@@ -1,14 +1,16 @@
1
- source 'https://rubygems.org'
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
2
4
 
3
5
  group :development do
6
+ gem "coveralls", require: false
7
+ gem "growl"
8
+ gem "guard"
9
+ gem "guard-rspec"
10
+ gem "rb-fsevent", "~> 0.9"
4
11
  gem "rspec"
5
- gem 'guard'
6
- gem 'guard-rspec'
7
- gem 'growl'
8
- gem 'rb-fsevent', '~> 0.9'
9
12
  # Probably required on OS X. See https://github.com/guard/guard/wiki/Add-Readline-support-to-Ruby-on-Mac-OS-X
10
- gem 'rb-readline'
11
- gem 'coveralls', require: false
13
+ gem "rb-readline"
12
14
  end
13
15
 
14
16
  # Specify your gem's dependencies in atdis.gemspec
data/Guardfile CHANGED
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # More info at https://github.com/guard/guard#readme
2
4
 
3
- guard 'rspec' do
4
- watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
5
+ guard "rspec" do
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
5
7
  watch(%r{^spec/.+_spec\.rb$})
6
8
  end
7
-
data/Rakefile CHANGED
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "bundler/gem_tasks"
2
4
 
3
- require 'rspec/core/rake_task'
5
+ require "rspec/core/rake_task"
4
6
 
5
- RSpec::Core::RakeTask.new('spec')
7
+ RSpec::Core::RakeTask.new("spec")
6
8
 
7
9
  # If you want to make this the default task
8
10
  task default: :spec
@@ -1,14 +1,16 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("lib", __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'atdis/version'
5
+ require "atdis/version"
5
6
 
6
7
  Gem::Specification.new do |spec|
7
8
  spec.name = "atdis"
8
9
  spec.version = Atdis::VERSION
9
10
  spec.authors = ["Matthew Landauer"]
10
11
  spec.email = ["matthew@openaustraliafoundation.org.au"]
11
- spec.description = %q{A ruby interface to the application tracking data interchange specification (ATDIS) API}
12
+ spec.description =
13
+ "A ruby interface to the application tracking data interchange specification (ATDIS) API"
12
14
  spec.summary = spec.description
13
15
  spec.homepage = "http://github.com/openaustralia/atdis"
14
16
  spec.license = "MIT"
@@ -17,12 +19,15 @@ Gem::Specification.new do |spec|
17
19
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
20
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
21
  spec.require_paths = ["lib"]
22
+ spec.required_ruby_version = ">= 2.3.1"
20
23
 
21
24
  spec.add_development_dependency "bundler", "~> 1.3"
22
25
  spec.add_development_dependency "rake"
26
+ spec.add_development_dependency "rubocop"
23
27
 
28
+ spec.add_dependency "activemodel"
29
+ spec.add_dependency "activesupport"
24
30
  spec.add_dependency "multi_json", "~> 1.7"
25
31
  spec.add_dependency "rest-client"
26
32
  spec.add_dependency "rgeo-geojson"
27
- spec.add_dependency "activemodel", "~> 3"
28
33
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "atdis/version"
2
4
 
3
5
  require "atdis/validators"
@@ -1,23 +1,34 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "rest-client"
2
4
 
3
5
  module ATDIS
4
6
  class Feed
5
- attr_reader :base_url
7
+ attr_reader :base_url, :timezone, :ignore_ssl_certificate
6
8
 
7
- VALID_OPTIONS = [:page, :street, :suburb, :postcode, :lodgement_date_start, :lodgement_date_end, :last_modified_date_start, :last_modified_date_end]
9
+ VALID_OPTIONS = %i[page street suburb postcode lodgement_date_start
10
+ lodgement_date_end last_modified_date_start last_modified_date_end].freeze
8
11
 
9
12
  # base_url - the base url from which the urls for all atdis urls are made
10
13
  # It should be of the form:
11
14
  # http://www.council.nsw.gov.au/atdis/1.0
12
- def initialize(base_url)
15
+ # timezone - a string (e.g. "Sydney") for the timezone in which times are returned
16
+ # (Note: times in the feeds that have timezones specified get converted to the
17
+ # timezone given while times in the feed which don't have a timezone specified
18
+ # get interpreted in the given timezone)
19
+ # See https://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html for the
20
+ # list of possible timezone strings
21
+ def initialize(base_url, timezone, ignore_ssl_certificate = false)
13
22
  @base_url = base_url
23
+ @timezone = timezone
24
+ @ignore_ssl_certificate = ignore_ssl_certificate
14
25
  end
15
26
 
16
27
  def applications_url(options = {})
17
28
  invalid_options = options.keys - VALID_OPTIONS
18
- if !invalid_options.empty?
19
- raise "Unexpected options used: #{invalid_options.join(',')}"
20
- end
29
+
30
+ raise "Unexpected options used: #{invalid_options.join(',')}" unless invalid_options.empty?
31
+
21
32
  options[:street] = options[:street].join(",") if options[:street].respond_to?(:join)
22
33
  options[:suburb] = options[:suburb].join(",") if options[:suburb].respond_to?(:join)
23
34
  options[:postcode] = options[:postcode].join(",") if options[:postcode].respond_to?(:join)
@@ -27,7 +38,7 @@ module ATDIS
27
38
  end
28
39
 
29
40
  def application_url(id)
30
- "#{base_url}/#{CGI::escape(id)}.json"
41
+ "#{base_url}/#{CGI.escape(id)}.json"
31
42
  end
32
43
 
33
44
  def self.base_url_from_url(url)
@@ -45,44 +56,39 @@ module ATDIS
45
56
  def self.options_from_url(url)
46
57
  u = URI.parse(url)
47
58
  options = query_to_options(u.query)
48
- [:lodgement_date_start, :lodgement_date_end, :last_modified_date_start, :last_modified_date_end].each do |k|
59
+ %i[lodgement_date_start lodgement_date_end last_modified_date_start
60
+ last_modified_date_end].each do |k|
49
61
  options[k] = Date.parse(options[k]) if options[k]
50
62
  end
51
63
  options[:page] = options[:page].to_i if options[:page]
52
64
  # Remove invalid options
53
- options.keys.each do |key|
54
- if !VALID_OPTIONS.include?(key)
55
- options.delete(key)
56
- end
65
+ options.each_key do |key|
66
+ options.delete(key) unless VALID_OPTIONS.include?(key)
57
67
  end
58
68
  options
59
69
  end
60
70
 
61
71
  def applications(options = {})
62
- Models::Page.read_url(applications_url(options))
72
+ Models::Page.read_url(applications_url(options), timezone, ignore_ssl_certificate)
63
73
  end
64
74
 
65
75
  def application(id)
66
- Models::Application.read_url(application_url(id))
76
+ Models::Application.read_url(application_url(id), timezone, ignore_ssl_certificate)
67
77
  end
68
78
 
69
- private
70
-
71
79
  # Turn a query string of the form "foo=bar&hello=sir" to {foo: "bar", hello: sir"}
72
80
  def self.query_to_options(query)
73
81
  options = {}
74
- if query
75
- query.split("&").each do |t|
76
- key, value = t.split("=")
77
- options[key.to_sym] = (CGI::unescape(value) if value)
78
- end
82
+ (query || "").split("&").each do |t|
83
+ key, value = t.split("=")
84
+ options[key.to_sym] = (CGI.unescape(value) if value)
79
85
  end
80
86
  options
81
87
  end
82
88
 
83
89
  # Escape but leave commas unchanged (which are valid in query strings)
84
- def self.escape(v)
85
- CGI::escape(v.to_s).gsub('%2C',',')
90
+ def self.escape(value)
91
+ CGI.escape(value.to_s).gsub("%2C", ",")
86
92
  end
87
93
 
88
94
  # Turn an options hash of the form {foo: "bar", hello: "sir"} into a query
@@ -91,7 +97,9 @@ module ATDIS
91
97
  if options.empty?
92
98
  nil
93
99
  else
94
- options.sort{|a,b| a.first.to_s <=> b.first.to_s}.map{|k,v| "#{k}=#{escape(v)}"}.join("&")
100
+ options.sort { |a, b| a.first.to_s <=> b.first.to_s }
101
+ .map { |k, v| "#{k}=#{escape(v)}" }
102
+ .join("&")
95
103
  end
96
104
  end
97
105
  end
@@ -1,6 +1,8 @@
1
- require 'multi_json'
2
- require 'active_model'
3
- require 'date'
1
+ # frozen_string_literal: true
2
+
3
+ require "multi_json"
4
+ require "active_model"
5
+ require "date"
4
6
 
5
7
  module ATDIS
6
8
  module TypeCastAttributes
@@ -11,14 +13,14 @@ module ATDIS
11
13
  end
12
14
 
13
15
  module ClassMethods
14
- # of the form {section: Fixnum, address: String}
15
- def set_field_mappings(p)
16
- define_attribute_methods(p.keys.map{|k| k.to_s})
16
+ # of the form {section: Integer, address: String}
17
+ def field_mappings(params)
18
+ define_attribute_methods(params.keys.map(&:to_s))
17
19
  # Convert all values to arrays. Doing this for the sake of tidier notation
18
20
  self.attribute_types = {}
19
- p.each do |k,v|
20
- v = [v] unless v.kind_of?(Array)
21
- self.attribute_types[k] = v
21
+ params.each do |k, v|
22
+ v = [v] unless v.is_a?(Array)
23
+ attribute_types[k] = v
22
24
  end
23
25
  end
24
26
  end
@@ -40,10 +42,10 @@ module ATDIS
40
42
  include Validators
41
43
  include ActiveModel::AttributeMethods
42
44
  include TypeCastAttributes
43
- attribute_method_suffix '_before_type_cast'
44
- attribute_method_suffix '='
45
+ attribute_method_suffix "_before_type_cast"
46
+ attribute_method_suffix "="
45
47
 
46
- attr_reader :attributes, :attributes_before_type_cast
48
+ attr_reader :attributes, :attributes_before_type_cast, :timezone
47
49
  # Stores any part of the json that could not be interpreted. Usually
48
50
  # signals an error if it isn't empty.
49
51
  attr_accessor :json_left_overs, :json_load_error
@@ -54,7 +56,8 @@ module ATDIS
54
56
 
55
57
  # Partition the data into used and unused by returning [used, unused]
56
58
  def self.partition_by_used(data)
57
- used, unused = {}, {}
59
+ used = {}
60
+ unused = {}
58
61
  if data.respond_to?(:each)
59
62
  data.each do |key, value|
60
63
  if attribute_keys.include?(key)
@@ -69,46 +72,54 @@ module ATDIS
69
72
  [used, unused]
70
73
  end
71
74
 
72
- def self.read_url(url)
73
- r = read_json(RestClient.get(url.to_s).to_str)
75
+ def self.read_url_raw(url, ignore_ssl_certificate = false)
76
+ RestClient::Resource.new(
77
+ url.to_s,
78
+ verify_ssl: (OpenSSL::SSL::VERIFY_NONE if ignore_ssl_certificate)
79
+ ).get.to_str
80
+ end
81
+
82
+ def self.read_url(url, timezone, ignore_ssl_certificate = false)
83
+ r = read_json(read_url_raw(url, ignore_ssl_certificate), timezone)
74
84
  r.url = url.to_s
75
85
  r
76
86
  end
77
87
 
78
- def self.read_json(text)
79
- begin
80
- data = MultiJson.load(text, symbolize_keys: true)
81
- interpret(data)
82
- rescue MultiJson::LoadError => e
83
- a = interpret({response: []})
84
- a.json_load_error = e.to_s
85
- a
86
- end
88
+ def self.read_json(text, timezone)
89
+ data = MultiJson.load(text, symbolize_keys: true)
90
+ interpret(data, timezone)
91
+ rescue MultiJson::LoadError => e
92
+ a = interpret({ response: [] }, timezone)
93
+ a.json_load_error = e.to_s
94
+ a
87
95
  end
88
96
 
89
- def self.interpret(*params)
90
- used, unused = partition_by_used(*params)
91
- new(used.merge(json_left_overs: unused))
97
+ def self.interpret(data, timezone)
98
+ used, unused = partition_by_used(data)
99
+ new(used.merge(json_left_overs: unused), timezone)
92
100
  end
93
101
 
94
102
  def json_loaded_correctly!
95
- if json_load_error
96
- errors.add(:json, ErrorMessage["Invalid JSON: #{json_load_error}", nil])
97
- end
103
+ return unless json_load_error
104
+
105
+ errors.add(:json, ErrorMessage["Invalid JSON: #{json_load_error}", nil])
98
106
  end
99
107
 
100
108
  def json_errors_local
101
109
  r = []
102
110
  # First show special json error
103
- if !errors[:json].empty?
104
- r << [nil, errors[:json]]
105
- end
106
111
  errors.keys.each do |attribute|
112
+ r << [nil, errors[:json]] unless errors[:json].empty?
107
113
  # The :json attribute is special
108
- if attribute != :json
109
- e = errors[attribute]
110
- r << [{attribute => attributes_before_type_cast[attribute.to_s]}, e.map{|m| ErrorMessage["#{attribute} #{m}", m.spec_section]}] unless e.empty?
111
- end
114
+ next if attribute == :json
115
+
116
+ e = errors[attribute]
117
+ next if e.empty?
118
+
119
+ r << [
120
+ { attribute => attributes_before_type_cast[attribute.to_s] },
121
+ e.map { |m| ErrorMessage["#{attribute} #{m}", m.spec_section] }
122
+ ]
112
123
  end
113
124
  r
114
125
  end
@@ -117,12 +128,11 @@ module ATDIS
117
128
  r = []
118
129
  attributes.each do |attribute_as_string, value|
119
130
  attribute = attribute_as_string.to_sym
120
- e = errors[attribute]
121
131
  if value.respond_to?(:json_errors)
122
- r += value.json_errors.map{|a, b| [{attribute => a}, b]}
123
- elsif value.kind_of?(Array)
124
- f = value.find{|v| v.respond_to?(:json_errors) && !v.json_errors.empty?}
125
- r += f.json_errors.map{|a, b| [{attribute => [a]}, b]} if f
132
+ r += value.json_errors.map { |a, b| [{ attribute => a }, b] }
133
+ elsif value.is_a?(Array)
134
+ f = value.find { |v| v.respond_to?(:json_errors) && !v.json_errors.empty? }
135
+ r += f.json_errors.map { |a, b| [{ attribute => [a] }, b] } if f
126
136
  end
127
137
  end
128
138
  r
@@ -133,22 +143,29 @@ module ATDIS
133
143
  end
134
144
 
135
145
  # Have we tried to use this attribute?
136
- def used_attribute?(a)
137
- !attributes_before_type_cast[a].nil?
146
+ def used_attribute?(attribute)
147
+ !attributes_before_type_cast[attribute].nil?
138
148
  end
139
149
 
140
150
  def json_left_overs_is_empty
141
- if json_left_overs && !json_left_overs.empty?
142
- # We have extra parameters that shouldn't be there
143
- errors.add(:json, ErrorMessage["Unexpected parameters in json data: #{MultiJson.dump(json_left_overs)}", "4"])
144
- end
151
+ return unless json_left_overs && !json_left_overs.empty?
152
+
153
+ # We have extra parameters that shouldn't be there
154
+ errors.add(
155
+ :json,
156
+ ErrorMessage["Unexpected parameters in json data: #{MultiJson.dump(json_left_overs)}", "4"]
157
+ )
145
158
  end
146
159
 
147
- def initialize(params={})
148
- @attributes, @attributes_before_type_cast = {}, {}
160
+ def initialize(params, timezone)
161
+ @timezone = timezone
162
+ @attributes = {}
163
+ @attributes_before_type_cast = {}
164
+ return unless params
165
+
149
166
  params.each do |attr, value|
150
- self.send("#{attr}=", value)
151
- end if params
167
+ send("#{attr}=", value)
168
+ end
152
169
  end
153
170
 
154
171
  def self.attribute_keys
@@ -157,68 +174,49 @@ module ATDIS
157
174
 
158
175
  # Does what the equivalent on Activerecord does
159
176
  def self.attribute_names
160
- attribute_types.keys.map{|k| k.to_s}
177
+ attribute_types.keys.map(&:to_s)
161
178
  end
162
179
 
163
- def self.cast(value, type)
180
+ def self.cast(value, type, timezone)
164
181
  # If it's already the correct type (or nil) then we don't need to do anything
165
- if value.nil? || value.kind_of?(type)
182
+ if value.nil? || value.is_a?(type)
166
183
  value
167
- # Special handling for arrays. When we typecast arrays we actually typecast each member of the array
168
- elsif value.kind_of?(Array)
169
- value.map {|v| cast(v, type)}
184
+ # Special handling for arrays. When we typecast arrays we actually
185
+ # typecast each member of the array
186
+ elsif value.is_a?(Array)
187
+ value.map { |v| cast(v, type, timezone) }
170
188
  elsif type == DateTime
171
- cast_datetime(value)
189
+ cast_datetime(value, timezone)
172
190
  elsif type == URI
173
191
  cast_uri(value)
174
192
  elsif type == String
175
193
  cast_string(value)
176
- elsif type == Fixnum
177
- cast_fixnum(value)
194
+ elsif type == Integer
195
+ cast_integer(value)
178
196
  elsif type == RGeo::GeoJSON
179
197
  cast_geojson(value)
180
198
  # Otherwise try to use Type.interpret to do the typecasting
181
199
  elsif type.respond_to?(:interpret)
182
- type.interpret(value) if value
200
+ type.interpret(value, timezone) if value
183
201
  else
184
202
  raise
185
203
  end
186
204
  end
187
205
 
188
- private
189
-
190
- def attribute(attr)
191
- @attributes[attr]
192
- end
193
-
194
- def attribute_before_type_cast(attr)
195
- @attributes_before_type_cast[attr]
196
- end
197
-
198
- def attribute=(attr, value)
199
- @attributes_before_type_cast[attr] = value
200
- @attributes[attr] = Model.cast(value, attribute_types[attr.to_sym][0])
201
- end
202
-
203
- def self.cast_datetime(value)
204
- # This would be much easier if we knew we only had to support Ruby 1.9 or greater because it has
205
- # an implementation built in. Because for the time being we need to support Ruby 1.8 as well
206
- # we'll build an implementation of parsing by hand. Ugh.
207
- # Referencing http://www.w3.org/TR/NOTE-datetime
208
- # In section 4.3.1 of ATDIS 1.0.4 it shows two variants of iso 8601, either the full date
209
- # or the full date with hours, seconds, minutes and timezone. We'll assume that these
210
- # are the two variants that are allowed.
211
- if value.respond_to?(:match) && value.match(/^\d\d\d\d-\d\d-\d\d(T\d\d:\d\d:\d\d(Z|(\+|-)\d\d:\d\d))?$/)
212
- DateTime.parse(value)
213
- end
206
+ # If timezone is given in the string then the datetime is read in using
207
+ # the timezone in the string and then converted to the timezone "zone"
208
+ # If the timezone isn't given in the string then the datetime is read
209
+ # in using the timezone in "zone"
210
+ def self.cast_datetime(value, timezone)
211
+ ActiveSupport::TimeZone.new(timezone).iso8601(value).to_datetime
212
+ rescue ArgumentError, KeyError
213
+ nil
214
214
  end
215
215
 
216
216
  def self.cast_uri(value)
217
- begin
218
- URI.parse(value)
219
- rescue URI::InvalidURIError
220
- nil
221
- end
217
+ URI.parse(value)
218
+ rescue URI::InvalidURIError
219
+ nil
222
220
  end
223
221
 
224
222
  def self.cast_string(value)
@@ -226,8 +224,8 @@ module ATDIS
226
224
  end
227
225
 
228
226
  # This casting allows nil values
229
- def self.cast_fixnum(value)
230
- value.to_i if value
227
+ def self.cast_integer(value)
228
+ value&.to_i
231
229
  end
232
230
 
233
231
  def self.cast_geojson(value)
@@ -246,5 +244,20 @@ module ATDIS
246
244
  hash
247
245
  end
248
246
  end
247
+
248
+ private
249
+
250
+ def attribute(attr)
251
+ @attributes[attr]
252
+ end
253
+
254
+ def attribute_before_type_cast(attr)
255
+ @attributes_before_type_cast[attr]
256
+ end
257
+
258
+ def attribute=(attr, value)
259
+ @attributes_before_type_cast[attr] = value
260
+ @attributes[attr] = Model.cast(value, attribute_types[attr.to_sym][0], timezone)
261
+ end
249
262
  end
250
263
  end