atdis 0.3.11 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +46 -0
- data/.ruby-version +1 -1
- data/.travis.yml +0 -4
- 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 +32 -24
- data/lib/atdis/model.rb +108 -95
- 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 +36 -21
- 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 +128 -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 +197 -113
- 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 +244 -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 +62 -33
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 569aa3967302c8529a9c74433f54fd105ebe45d7
|
4
|
+
data.tar.gz: 0eb50df65474e2d5bc5787994d58c8a282e87e4a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b6aa7bae02ff0fa4a4a2776ef4bbb16f7438f2332f5b3e419f2281abebe8f25e8894a2beb50b325ca3ca24dce598b58f9c0580b5de408f1e4fe69f712dd4bd57
|
7
|
+
data.tar.gz: 029202949e54506691b3e5c6bb36f2bb73aafd14e707e4ff328b5f739c0a55762a6b35a2f801a5ccbe5d63c8233d23db6c575d40230bf453c2afc4760268f5e7
|
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/.travis.yml
CHANGED
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", "~> 3"
|
28
33
|
end
|
data/lib/atdis.rb
CHANGED
data/lib/atdis/feed.rb
CHANGED
@@ -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 = [
|
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, 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
|
-
|
19
|
-
|
20
|
-
|
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
|
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
|
-
[
|
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.
|
54
|
-
|
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
|
-
|
75
|
-
|
76
|
-
|
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(
|
85
|
-
CGI
|
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
|
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
|
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,54 @@ module ATDIS
|
|
69
72
|
[used, unused]
|
70
73
|
end
|
71
74
|
|
72
|
-
def self.
|
73
|
-
|
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
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
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(
|
90
|
-
used, unused = partition_by_used(
|
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
|
-
|
96
|
-
|
97
|
-
|
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
|
109
|
-
|
110
|
-
|
111
|
-
|
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
|
-
|
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
|
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?(
|
137
|
-
!attributes_before_type_cast[
|
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
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
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
|
-
@
|
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
|
-
|
151
|
-
end
|
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
|
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.
|
182
|
+
if value.nil? || value.is_a?(type)
|
166
183
|
value
|
167
|
-
# Special handling for arrays. When we typecast arrays we actually
|
168
|
-
|
169
|
-
|
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 ==
|
177
|
-
|
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
|
-
|
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
|
-
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
|
-
|
218
|
-
|
219
|
-
|
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.
|
230
|
-
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
|