atdis 0.3.13 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +46 -0
- data/.ruby-version +1 -1
- data/Gemfile +9 -7
- data/Guardfile +4 -3
- data/Rakefile +4 -2
- data/atdis.gemspec +10 -5
- data/lib/atdis.rb +2 -0
- data/lib/atdis/feed.rb +31 -24
- data/lib/atdis/model.rb +101 -99
- data/lib/atdis/models/address.rb +10 -4
- data/lib/atdis/models/application.rb +12 -9
- data/lib/atdis/models/authority.rb +11 -6
- data/lib/atdis/models/document.rb +8 -6
- data/lib/atdis/models/event.rb +10 -8
- data/lib/atdis/models/info.rb +73 -49
- data/lib/atdis/models/land_title_ref.rb +15 -7
- data/lib/atdis/models/location.rb +9 -7
- data/lib/atdis/models/page.rb +34 -19
- data/lib/atdis/models/pagination.rb +91 -32
- data/lib/atdis/models/person.rb +7 -5
- data/lib/atdis/models/reference.rb +7 -5
- data/lib/atdis/models/response.rb +5 -3
- data/lib/atdis/models/torrens_title.rb +9 -7
- data/lib/atdis/separated_url.rb +17 -15
- data/lib/atdis/validators.rb +46 -39
- data/lib/atdis/version.rb +3 -1
- data/spec/atdis/feed_spec.rb +126 -34
- data/spec/atdis/model_spec.rb +124 -51
- data/spec/atdis/models/address_spec.rb +18 -9
- data/spec/atdis/models/application_spec.rb +222 -155
- data/spec/atdis/models/authority_spec.rb +45 -15
- data/spec/atdis/models/document_spec.rb +10 -4
- data/spec/atdis/models/event_spec.rb +23 -11
- data/spec/atdis/models/info_spec.rb +191 -116
- data/spec/atdis/models/land_title_ref_spec.rb +32 -16
- data/spec/atdis/models/location_spec.rb +75 -60
- data/spec/atdis/models/page_spec.rb +241 -135
- data/spec/atdis/models/pagination_spec.rb +177 -77
- data/spec/atdis/models/person_spec.rb +8 -4
- data/spec/atdis/models/reference_spec.rb +29 -16
- data/spec/atdis/models/response_spec.rb +2 -1
- data/spec/atdis/models/torrens_title_spec.rb +24 -18
- data/spec/atdis/separated_url_spec.rb +14 -15
- data/spec/spec_helper.rb +14 -10
- metadata +56 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0c42c8984e3515ca6f1a98e02e774cfbf30b28e9
|
4
|
+
data.tar.gz: e3fd6b0fa426195259d1b392dda98f55d5f9c9b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6ebeaa70b6b9eb739cd47aa8c8b9bec5b8d15b71efb99b6263c1327cab6a2b84866a2b304959d65542b9a1a94580b55445bd417f8fe302ae2708c2c45d5d27f6
|
7
|
+
data.tar.gz: 4d72b3a0621f1d40fbaf0cf517cf35b7f1d17817422ede3273d22d0f5de2ec008456612337131b649916b45872fe3afb0b97970eae2e72a9870ed17b86fb3b6f
|
data/.rubocop.yml
ADDED
@@ -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
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
ruby-2.
|
1
|
+
ruby-2.3.1
|
data/Gemfile
CHANGED
@@ -1,14 +1,16 @@
|
|
1
|
-
|
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
|
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
|
4
|
-
watch(%r{^lib/(.+)\.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
|
5
|
+
require "rspec/core/rake_task"
|
4
6
|
|
5
|
-
RSpec::Core::RakeTask.new(
|
7
|
+
RSpec::Core::RakeTask.new("spec")
|
6
8
|
|
7
9
|
# If you want to make this the default task
|
8
10
|
task default: :spec
|
data/atdis.gemspec
CHANGED
@@ -1,14 +1,16 @@
|
|
1
|
-
#
|
2
|
-
|
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
|
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 =
|
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"
|
28
33
|
end
|
data/lib/atdis.rb
CHANGED
data/lib/atdis/feed.rb
CHANGED
@@ -1,23 +1,33 @@
|
|
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
|
6
8
|
|
7
|
-
VALID_OPTIONS = [
|
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
|
-
|
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)
|
13
22
|
@base_url = base_url
|
23
|
+
@timezone = timezone
|
14
24
|
end
|
15
25
|
|
16
26
|
def applications_url(options = {})
|
17
27
|
invalid_options = options.keys - VALID_OPTIONS
|
18
|
-
|
19
|
-
|
20
|
-
|
28
|
+
|
29
|
+
raise "Unexpected options used: #{invalid_options.join(',')}" unless invalid_options.empty?
|
30
|
+
|
21
31
|
options[:street] = options[:street].join(",") if options[:street].respond_to?(:join)
|
22
32
|
options[:suburb] = options[:suburb].join(",") if options[:suburb].respond_to?(:join)
|
23
33
|
options[:postcode] = options[:postcode].join(",") if options[:postcode].respond_to?(:join)
|
@@ -27,7 +37,7 @@ module ATDIS
|
|
27
37
|
end
|
28
38
|
|
29
39
|
def application_url(id)
|
30
|
-
"#{base_url}/#{CGI
|
40
|
+
"#{base_url}/#{CGI.escape(id)}.json"
|
31
41
|
end
|
32
42
|
|
33
43
|
def self.base_url_from_url(url)
|
@@ -45,44 +55,39 @@ module ATDIS
|
|
45
55
|
def self.options_from_url(url)
|
46
56
|
u = URI.parse(url)
|
47
57
|
options = query_to_options(u.query)
|
48
|
-
[
|
58
|
+
%i[lodgement_date_start lodgement_date_end last_modified_date_start
|
59
|
+
last_modified_date_end].each do |k|
|
49
60
|
options[k] = Date.parse(options[k]) if options[k]
|
50
61
|
end
|
51
62
|
options[:page] = options[:page].to_i if options[:page]
|
52
63
|
# Remove invalid options
|
53
|
-
options.
|
54
|
-
|
55
|
-
options.delete(key)
|
56
|
-
end
|
64
|
+
options.each_key do |key|
|
65
|
+
options.delete(key) unless VALID_OPTIONS.include?(key)
|
57
66
|
end
|
58
67
|
options
|
59
68
|
end
|
60
69
|
|
61
70
|
def applications(options = {})
|
62
|
-
Models::Page.read_url(applications_url(options))
|
71
|
+
Models::Page.read_url(applications_url(options), timezone)
|
63
72
|
end
|
64
73
|
|
65
74
|
def application(id)
|
66
|
-
Models::Application.read_url(application_url(id))
|
75
|
+
Models::Application.read_url(application_url(id), timezone)
|
67
76
|
end
|
68
77
|
|
69
|
-
private
|
70
|
-
|
71
78
|
# Turn a query string of the form "foo=bar&hello=sir" to {foo: "bar", hello: sir"}
|
72
79
|
def self.query_to_options(query)
|
73
80
|
options = {}
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
options[key.to_sym] = (CGI::unescape(value) if value)
|
78
|
-
end
|
81
|
+
(query || "").split("&").each do |t|
|
82
|
+
key, value = t.split("=")
|
83
|
+
options[key.to_sym] = (CGI.unescape(value) if value)
|
79
84
|
end
|
80
85
|
options
|
81
86
|
end
|
82
87
|
|
83
88
|
# Escape but leave commas unchanged (which are valid in query strings)
|
84
|
-
def self.escape(
|
85
|
-
CGI
|
89
|
+
def self.escape(value)
|
90
|
+
CGI.escape(value.to_s).gsub("%2C", ",")
|
86
91
|
end
|
87
92
|
|
88
93
|
# Turn an options hash of the form {foo: "bar", hello: "sir"} into a query
|
@@ -91,7 +96,9 @@ module ATDIS
|
|
91
96
|
if options.empty?
|
92
97
|
nil
|
93
98
|
else
|
94
|
-
options.sort{|a,b| a.first.to_s <=> b.first.to_s
|
99
|
+
options.sort { |a, b| a.first.to_s <=> b.first.to_s }
|
100
|
+
.map { |k, v| "#{k}=#{escape(v)}" }
|
101
|
+
.join("&")
|
95
102
|
end
|
96
103
|
end
|
97
104
|
end
|
data/lib/atdis/model.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
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:
|
15
|
-
def
|
16
|
-
define_attribute_methods(
|
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
|
-
|
20
|
-
v = [v] unless v.
|
21
|
-
|
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
|
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
|
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,47 @@ 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(url, timezone)
|
76
|
+
r = read_json(RestClient.get(url.to_s).to_str, timezone)
|
74
77
|
r.url = url.to_s
|
75
78
|
r
|
76
79
|
end
|
77
80
|
|
78
|
-
def self.read_json(text)
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
a
|
86
|
-
end
|
81
|
+
def self.read_json(text, timezone)
|
82
|
+
data = MultiJson.load(text, symbolize_keys: true)
|
83
|
+
interpret(data, timezone)
|
84
|
+
rescue MultiJson::LoadError => e
|
85
|
+
a = interpret({ response: [] }, timezone)
|
86
|
+
a.json_load_error = e.to_s
|
87
|
+
a
|
87
88
|
end
|
88
89
|
|
89
|
-
def self.interpret(
|
90
|
-
used, unused = partition_by_used(
|
91
|
-
new(used.merge(json_left_overs: unused))
|
90
|
+
def self.interpret(data, timezone)
|
91
|
+
used, unused = partition_by_used(data)
|
92
|
+
new(used.merge(json_left_overs: unused), timezone)
|
92
93
|
end
|
93
94
|
|
94
95
|
def json_loaded_correctly!
|
95
|
-
|
96
|
-
|
97
|
-
|
96
|
+
return unless json_load_error
|
97
|
+
|
98
|
+
errors.add(:json, ErrorMessage["Invalid JSON: #{json_load_error}", nil])
|
98
99
|
end
|
99
100
|
|
100
101
|
def json_errors_local
|
101
102
|
r = []
|
102
103
|
# First show special json error
|
103
|
-
if !errors[:json].empty?
|
104
|
-
r << [nil, errors[:json]]
|
105
|
-
end
|
106
104
|
errors.keys.each do |attribute|
|
105
|
+
r << [nil, errors[:json]] unless errors[:json].empty?
|
107
106
|
# The :json attribute is special
|
108
|
-
if attribute
|
109
|
-
|
110
|
-
|
111
|
-
|
107
|
+
next if attribute == :json
|
108
|
+
|
109
|
+
e = errors[attribute]
|
110
|
+
next if e.empty?
|
111
|
+
|
112
|
+
r << [
|
113
|
+
{ attribute => attributes_before_type_cast[attribute.to_s] },
|
114
|
+
e.map { |m| ErrorMessage["#{attribute} #{m}", m.spec_section] }
|
115
|
+
]
|
112
116
|
end
|
113
117
|
r
|
114
118
|
end
|
@@ -117,12 +121,11 @@ module ATDIS
|
|
117
121
|
r = []
|
118
122
|
attributes.each do |attribute_as_string, value|
|
119
123
|
attribute = attribute_as_string.to_sym
|
120
|
-
e = errors[attribute]
|
121
124
|
if value.respond_to?(:json_errors)
|
122
|
-
|
123
|
-
elsif value.
|
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
|
125
|
+
r += value.json_errors.map { |a, b| [{ attribute => a }, b] }
|
126
|
+
elsif value.is_a?(Array)
|
127
|
+
f = value.find { |v| v.respond_to?(:json_errors) && !v.json_errors.empty? }
|
128
|
+
r += f.json_errors.map { |a, b| [{ attribute => [a] }, b] } if f
|
126
129
|
end
|
127
130
|
end
|
128
131
|
r
|
@@ -133,22 +136,29 @@ module ATDIS
|
|
133
136
|
end
|
134
137
|
|
135
138
|
# Have we tried to use this attribute?
|
136
|
-
def used_attribute?(
|
137
|
-
!attributes_before_type_cast[
|
139
|
+
def used_attribute?(attribute)
|
140
|
+
!attributes_before_type_cast[attribute].nil?
|
138
141
|
end
|
139
142
|
|
140
143
|
def json_left_overs_is_empty
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
144
|
+
return unless json_left_overs && !json_left_overs.empty?
|
145
|
+
|
146
|
+
# We have extra parameters that shouldn't be there
|
147
|
+
errors.add(
|
148
|
+
:json,
|
149
|
+
ErrorMessage["Unexpected parameters in json data: #{MultiJson.dump(json_left_overs)}", "4"]
|
150
|
+
)
|
145
151
|
end
|
146
152
|
|
147
|
-
def initialize(params
|
148
|
-
@
|
153
|
+
def initialize(params, timezone)
|
154
|
+
@timezone = timezone
|
155
|
+
@attributes = {}
|
156
|
+
@attributes_before_type_cast = {}
|
157
|
+
return unless params
|
158
|
+
|
149
159
|
params.each do |attr, value|
|
150
|
-
|
151
|
-
end
|
160
|
+
send("#{attr}=", value)
|
161
|
+
end
|
152
162
|
end
|
153
163
|
|
154
164
|
def self.attribute_keys
|
@@ -157,72 +167,49 @@ module ATDIS
|
|
157
167
|
|
158
168
|
# Does what the equivalent on Activerecord does
|
159
169
|
def self.attribute_names
|
160
|
-
attribute_types.keys.map
|
170
|
+
attribute_types.keys.map(&:to_s)
|
161
171
|
end
|
162
172
|
|
163
|
-
def self.cast(value, type)
|
173
|
+
def self.cast(value, type, timezone)
|
164
174
|
# If it's already the correct type (or nil) then we don't need to do anything
|
165
|
-
if value.nil? || value.
|
175
|
+
if value.nil? || value.is_a?(type)
|
166
176
|
value
|
167
|
-
# Special handling for arrays. When we typecast arrays we actually
|
168
|
-
|
169
|
-
|
177
|
+
# Special handling for arrays. When we typecast arrays we actually
|
178
|
+
# typecast each member of the array
|
179
|
+
elsif value.is_a?(Array)
|
180
|
+
value.map { |v| cast(v, type, timezone) }
|
170
181
|
elsif type == DateTime
|
171
|
-
cast_datetime(value)
|
182
|
+
cast_datetime(value, timezone)
|
172
183
|
elsif type == URI
|
173
184
|
cast_uri(value)
|
174
185
|
elsif type == String
|
175
186
|
cast_string(value)
|
176
|
-
elsif type ==
|
177
|
-
|
187
|
+
elsif type == Integer
|
188
|
+
cast_integer(value)
|
178
189
|
elsif type == RGeo::GeoJSON
|
179
190
|
cast_geojson(value)
|
180
191
|
# Otherwise try to use Type.interpret to do the typecasting
|
181
192
|
elsif type.respond_to?(:interpret)
|
182
|
-
type.interpret(value) if value
|
193
|
+
type.interpret(value, timezone) if value
|
183
194
|
else
|
184
195
|
raise
|
185
196
|
end
|
186
197
|
end
|
187
198
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
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
|
-
begin
|
213
|
-
DateTime.parse(value)
|
214
|
-
rescue ArgumentError
|
215
|
-
nil
|
216
|
-
end
|
217
|
-
end
|
199
|
+
# If timezone is given in the string then the datetime is read in using
|
200
|
+
# the timezone in the string and then converted to the timezone "zone"
|
201
|
+
# If the timezone isn't given in the string then the datetime is read
|
202
|
+
# in using the timezone in "zone"
|
203
|
+
def self.cast_datetime(value, timezone)
|
204
|
+
ActiveSupport::TimeZone.new(timezone).iso8601(value).to_datetime
|
205
|
+
rescue ArgumentError, KeyError
|
206
|
+
nil
|
218
207
|
end
|
219
208
|
|
220
209
|
def self.cast_uri(value)
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
nil
|
225
|
-
end
|
210
|
+
URI.parse(value)
|
211
|
+
rescue URI::InvalidURIError
|
212
|
+
nil
|
226
213
|
end
|
227
214
|
|
228
215
|
def self.cast_string(value)
|
@@ -230,8 +217,8 @@ module ATDIS
|
|
230
217
|
end
|
231
218
|
|
232
219
|
# This casting allows nil values
|
233
|
-
def self.
|
234
|
-
value
|
220
|
+
def self.cast_integer(value)
|
221
|
+
value&.to_i
|
235
222
|
end
|
236
223
|
|
237
224
|
def self.cast_geojson(value)
|
@@ -250,5 +237,20 @@ module ATDIS
|
|
250
237
|
hash
|
251
238
|
end
|
252
239
|
end
|
240
|
+
|
241
|
+
private
|
242
|
+
|
243
|
+
def attribute(attr)
|
244
|
+
@attributes[attr]
|
245
|
+
end
|
246
|
+
|
247
|
+
def attribute_before_type_cast(attr)
|
248
|
+
@attributes_before_type_cast[attr]
|
249
|
+
end
|
250
|
+
|
251
|
+
def attribute=(attr, value)
|
252
|
+
@attributes_before_type_cast[attr] = value
|
253
|
+
@attributes[attr] = Model.cast(value, attribute_types[attr.to_sym][0], timezone)
|
254
|
+
end
|
253
255
|
end
|
254
256
|
end
|