atdis 0.1 → 0.2

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.
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # Atdis
2
2
 
3
- [![Build Status](https://travis-ci.org/openaustralia/atdis.png?branch=master)](https://travis-ci.org/openaustralia/atdis) [![Coverage Status](https://coveralls.io/repos/openaustralia/atdis/badge.png?branch=master)](https://coveralls.io/r/openaustralia/atdis?branch=master) [![Code Climate](https://codeclimate.com/github/openaustralia/atdis.png)](https://codeclimate.com/github/openaustralia/atdis)
3
+ [![Build Status](https://travis-ci.org/openaustralia/atdis.png?branch=master)](https://travis-ci.org/openaustralia/atdis) [![Coverage Status](https://coveralls.io/repos/openaustralia/atdis/badge.png?branch=master)](https://coveralls.io/r/openaustralia/atdis?branch=master) [![Code Climate](https://codeclimate.com/github/openaustralia/atdis.png)](https://codeclimate.com/github/openaustralia/atdis) [![Gem Version](https://badge.fury.io/rb/atdis.png)](http://badge.fury.io/rb/atdis)
4
4
 
5
5
  A ruby interface to the application tracking data interchange specification (ATDIS) API
6
6
 
7
- We're developing this against version ATDIS 1.0.4.
7
+ We're developing this against version ATDIS 1.0.7.
8
8
 
9
9
  This is **highly alpha** software that probably doesn't yet do what it says on the tin. It is very much a work in progress.
10
10
 
@@ -2,41 +2,72 @@ require 'multi_json'
2
2
 
3
3
  module ATDIS
4
4
  class Application < Model
5
- field_mappings :application => {
6
- :info => {
7
- :dat_id => [:dat_id, String],
8
- :last_modified_date => [:last_modified_date, DateTime],
9
- :description => [:description, String],
10
- :authority => [:authority, String],
11
- :lodgement_date => [:lodgement_date, DateTime],
12
- :determination_date => [:determination_date, DateTime],
13
- :status => [:status, String],
14
- :notification_start_date => [:notification_start_date, DateTime],
15
- :notification_end_date => [:notification_end_date, DateTime],
16
- :officer => [:officer, String],
17
- :estimated_cost => [:estimated_cost, String]
18
- },
19
- :reference => {
20
- :more_info_url => [:more_info_url, URI],
21
- :comments_url => [:comments_url, URI]
22
- },
23
- :location => [:location, Location],
24
- :events => [:events, Event],
25
- :documents => [:documents, Document],
26
- :people => [:people, Person],
27
- :extended => [:extended, Object]
28
- }
29
-
5
+ # TODO When we remove support for Ruby 1.8 we can convert field_mappings back to a hash
6
+ # which is much more readable
7
+ set_field_mappings [
8
+ [:application, [
9
+ [:info, [
10
+ [:dat_id, [:dat_id, String, {:level => 1}]],
11
+ [:last_modified_date, [:last_modified_date, DateTime, {:level => 1}]],
12
+ [:description, [:description, String, {:level => 1}]],
13
+ [:authority, [:authority, String, {:level => 1}]],
14
+ [:lodgement_date, [:lodgement_date, DateTime, {:level => 1}]],
15
+ [:determination_date, [:determination_date, DateTime, {:level => 1}]],
16
+ [:status, [:status, String, {:level => 1}]],
17
+ [:notification_start_date, [:notification_start_date, DateTime, {:level => 1}]],
18
+ [:notification_end_date, [:notification_end_date, DateTime, {:level => 1}]],
19
+ [:officer, [:officer, String, {:level => 1}]],
20
+ [:estimated_cost, [:estimated_cost, String, {:level => 1}]]
21
+ ]],
22
+ [:reference, [
23
+ [:more_info_url, [:more_info_url, URI, {:level => 1}]],
24
+ [:comments_url, [:comments_url, URI, {:level => 1}]]
25
+ ]],
26
+ [:location, [:location, Location, {:level => 1}]],
27
+ [:events, [:events, Event, {:level => 1}]],
28
+ [:documents, [:documents, Document, {:level => 1}]],
29
+ [:people, [:people, Person, {:level => 2}]],
30
+ [:extended, [:extended, Object, {:level => 3}]]
31
+ ]]
32
+ ]
33
+
30
34
  # Mandatory parameters
31
- validates :dat_id, :last_modified_date, :description, :authority, :lodgement_date, :determination_date, :status,
32
- :more_info_url, :location, :presence_before_type_cast => true
35
+ validates :dat_id, :last_modified_date, :description, :authority, :lodgement_date, :determination_date, :status,
36
+ :presence_before_type_cast => {:spec_section => "4.3.1"}
37
+ validates :more_info_url, :presence_before_type_cast => {:spec_section => "4.3.2"}
38
+ validates :location, :presence_before_type_cast => {:spec_section => "4.3.3"}
39
+ validates :events, :presence_before_type_cast => {:spec_section => "4.3.4"}
40
+ validates :documents, :presence_before_type_cast => {:spec_section => "4.3.5"}
33
41
 
34
42
  # Other validations
35
- validates :notification_start_date, :notification_end_date, :last_modified_date, :lodgement_date, :determination_date,
36
- :date_time => true
37
- validates :more_info_url, :http_url => true
38
- validates :location, :valid => true
43
+ validates :last_modified_date, :lodgement_date, :date_time => {:spec_section => "4.3.8"}
44
+ validates :determination_date, :notification_start_date, :notification_end_date, :date_time_or_none => {:spec_section => "4.3.1"}
45
+ validates :more_info_url, :http_url => {:spec_section => "4.3.2"}
46
+ validates :location, :events, :documents, :people, :valid => true
47
+ validates :events, :documents, :array => {:spec_section => "4.3.4"}
48
+ # TODO people should be an array if it's included
49
+
50
+ validate :notification_dates_consistent!
51
+
52
+ def notification_dates_consistent!
53
+ if notification_start_date_before_type_cast == "none" && notification_end_date_before_type_cast != "none"
54
+ errors.add(:notification_start_date, ErrorMessage["can't be none unless notification_end_date is none as well", "4.3.1"])
55
+ end
56
+ if notification_start_date_before_type_cast != "none" && notification_end_date_before_type_cast == "none"
57
+ errors.add(:notification_end_date, ErrorMessage["can't be none unless notification_start_date is none as well", "4.3.1"])
58
+ end
59
+ if notification_start_date_before_type_cast && notification_end_date_before_type_cast.blank?
60
+ errors.add(:notification_end_date, ErrorMessage["can not be blank if notification_start_date is set", "4.3.1"])
61
+ end
62
+ if notification_start_date_before_type_cast.blank? && notification_end_date_before_type_cast
63
+ errors.add(:notification_start_date, ErrorMessage["can not be blank if notification_end_date is set", "4.3.1"])
64
+ end
65
+ if notification_start_date && notification_end_date && notification_start_date > notification_end_date
66
+ errors.add(:notification_end_date, ErrorMessage["can not be earlier than notification_start_date", "4.3.1"])
67
+ end
68
+ end
39
69
 
70
+ # TODO Validate contents of estimated_cost
40
71
  # TODO Validate associated like locations, events, documents, people
41
72
  # TODO Do we need to do extra checking to ensure that events, documents and people are arrays?
42
73
  # TODO Separate validation for L2 and L3 compliance?
@@ -1,12 +1,14 @@
1
1
  module ATDIS
2
2
  class Document < Model
3
- field_mappings :ref => [:ref, String],
4
- :title => [:title, String],
5
- :document_url => [:document_url, URI]
3
+ set_field_mappings [
4
+ [:ref, [:ref, String, {:level => 1}]],
5
+ [:title, [:title, String, {:level => 1}]],
6
+ [:document_url, [:document_url, URI, {:level => 1}]]
7
+ ]
6
8
 
7
9
  # Mandatory parameters
8
- validates :ref, :title, :document_url, :presence_before_type_cast => true
10
+ validates :ref, :title, :document_url, :presence_before_type_cast => {:spec_section => "4.3.5"}
9
11
  # Other validations
10
- validates :document_url, :http_url => true
12
+ validates :document_url, :http_url => {:spec_section => "4.3.5"}
11
13
  end
12
14
  end
data/lib/atdis/event.rb CHANGED
@@ -1,12 +1,17 @@
1
1
  module ATDIS
2
2
  class Event < Model
3
- field_mappings :id => [:id, String],
4
- :date => [:date, DateTime],
5
- :description => [:description, String],
6
- :event_type => [:event_type, String],
7
- :status => [:status, String]
8
-
3
+ set_field_mappings [
4
+ [:id, [:id, String, {:level => 1}]],
5
+ [:date, [:date, DateTime, {:level => 1}]],
6
+ [:description, [:description, String, {:level => 1}]],
7
+ [:event_type, [:event_type, String, {:level => 1}]],
8
+ [:status, [:status, String, {:level => 1}]]
9
+ ]
10
+
9
11
  # Mandatory parameters
10
- validates :id, :date, :description, :presence_before_type_cast => true
12
+ validates :id, :date, :description, :presence_before_type_cast => {:spec_section => "4.3.4"}
11
13
  end
14
+
15
+ # TODO Check that :id is unique within an authority
16
+
12
17
  end
@@ -2,17 +2,20 @@ require "rgeo/geo_json"
2
2
 
3
3
  module ATDIS
4
4
  class Location < Model
5
- field_mappings :address => [:address, String],
6
- :land_title_ref => {
7
- :lot => [:lot, String],
8
- :section => [:section, String],
9
- :dpsp_id => [:dpsp_id, String]
10
- },
11
- :geometry => [:geometry, RGeo::GeoJSON]
12
-
5
+ set_field_mappings [
6
+ [:address, [:address, String, {:level => 1}]],
7
+ [:land_title_ref, [
8
+ [:lot, [:lot, String, {:level => 1}]],
9
+ [:section, [:section, String, {:none_is_nil => true, :level => 1}]],
10
+ [:dpsp_id, [:dpsp_id, String, {:level => 1}]]
11
+ ]],
12
+ [:geometry, [:geometry, RGeo::GeoJSON, {:level => 1}]]
13
+ ]
13
14
  # Mandatory parameters
14
- validates :address, :lot, :section, :dpsp_id, :presence_before_type_cast => true
15
+ validates :address, :lot, :section, :dpsp_id, :presence_before_type_cast => {:spec_section => "4.3.3"}
16
+
17
+ validates :geometry, :geo_json => {:spec_section => "4.3.3"}
15
18
 
16
- validates :geometry, :geo_json => true
19
+ # TODO: Provide warning if dpsp_id doesn't start with "DP" or "SP"
17
20
  end
18
21
  end
data/lib/atdis/model.rb CHANGED
@@ -7,30 +7,39 @@ module ATDIS
7
7
 
8
8
  included do
9
9
  class_attribute :attribute_types
10
- class_attribute :valid_fields
10
+ class_attribute :field_mappings
11
11
  end
12
12
 
13
13
  module ClassMethods
14
+ # of the form {:section=>[String, {:none_is_nil=>true}], :address=>[String]}
14
15
  def casting_attributes(p)
15
16
  define_attribute_methods(p.keys.map{|k| k.to_s})
16
17
  self.attribute_types = p
17
18
  end
18
19
 
19
- def field_mappings(p)
20
+ def set_field_mappings(p)
20
21
  a, b = translate_field_mappings(p)
21
- self.valid_fields = a
22
+ # field_mappings is of the form {:pagination=>{:previous=>:previous_page_no, :pages=>:total_no_pages}}
23
+ self.field_mappings = a
22
24
  casting_attributes(b)
23
25
  end
24
26
 
25
27
  private
28
+
29
+ def leaf_array?(v)
30
+ if !v.kind_of?(Array)
31
+ return false
32
+ end
33
+ v.all?{|a| !a.kind_of?(Array)}
34
+ end
26
35
 
27
36
  def translate_field_mappings(p)
28
- f = {}
29
- ca = {}
37
+ f = ActiveSupport::OrderedHash.new
38
+ ca = ActiveSupport::OrderedHash.new
30
39
  p.each do |k,v|
31
- if v.kind_of?(Array)
40
+ if leaf_array?(v)
32
41
  f[k] = v[0]
33
- ca[v.first] = v[1]
42
+ ca[v.first] = v[1..-1]
34
43
  else
35
44
  f2, ca2 = translate_field_mappings(v)
36
45
  f[k] = f2
@@ -42,6 +51,17 @@ module ATDIS
42
51
  end
43
52
  end
44
53
 
54
+ ErrorMessage = Struct.new :message, :spec_section do
55
+ def empty?
56
+ message.empty?
57
+ end
58
+
59
+ # Make this behave pretty much like a string
60
+ def to_s
61
+ message
62
+ end
63
+ end
64
+
45
65
  class Model
46
66
  include ActiveModel::Validations
47
67
  include Validators
@@ -50,17 +70,128 @@ module ATDIS
50
70
  attribute_method_suffix '_before_type_cast'
51
71
  attribute_method_suffix '='
52
72
 
53
- attr_reader :attributes
73
+ attr_reader :attributes, :attributes_before_type_cast
54
74
  # Stores any part of the json that could not be interpreted. Usually
55
75
  # signals an error if it isn't empty.
56
- attr_accessor :json_left_overs
76
+ attr_accessor :json_left_overs, :json_load_error
57
77
 
58
78
  validate :json_left_overs_is_empty
59
79
 
80
+ def self.level_attribute_names(level)
81
+ attribute_types.find_all{|k,v| (v[1] || {})[:level] == level }.map{|k,v| k.to_s}
82
+ end
83
+
84
+ def json_attribute(a, new_value, mappings = field_mappings)
85
+ mappings.each do |attribute, v|
86
+ if v == a
87
+ return {attribute => new_value}
88
+ end
89
+ if v.kind_of?(Hash)
90
+ r = json_attribute(a, new_value, v)
91
+ if r
92
+ return {attribute => r}
93
+ end
94
+ end
95
+ end
96
+ nil
97
+ end
98
+
99
+ def self.map_field(key, data, mappings)
100
+ mappings.each do |k, v|
101
+ if v == key
102
+ return data[k]
103
+ elsif v.kind_of?(Hash) && data.has_key?(k)
104
+ r = map_field(key, data[k], mappings[k])
105
+ if r
106
+ return r
107
+ end
108
+ end
109
+ end
110
+ nil
111
+ end
112
+
113
+ def self.unused_data(data, mappings = field_mappings)
114
+ json_left_overs = {}
115
+ data.each_key do |key|
116
+ if mappings[key]
117
+ if mappings[key].kind_of?(Hash)
118
+ l2 = unused_data(data[key], mappings[key])
119
+ json_left_overs[key] = l2 unless l2.empty?
120
+ end
121
+ else
122
+ json_left_overs[key] = data[key]
123
+ end
124
+ end
125
+ json_left_overs
126
+ end
127
+
128
+ def self.attribute_names_from_mappings(mappings)
129
+ result = []
130
+ mappings.each do |k, v|
131
+ if v.kind_of?(Hash)
132
+ result += attribute_names_from_mappings(v)
133
+ else
134
+ result << v
135
+ end
136
+ end
137
+ result
138
+ end
139
+
140
+ # Map json structure to our values
141
+ def self.map_fields(data, mappings = field_mappings)
142
+ values = {}
143
+ attribute_names_from_mappings(mappings).each do |attribute|
144
+ values[attribute] = map_field(attribute, data, mappings)
145
+ end
146
+ values
147
+ end
148
+
149
+ def json_errors
150
+ r = []
151
+ errors.messages.each do |attribute, e|
152
+ value = attributes[attribute.to_s]
153
+ if (value.respond_to?(:valid?) && !value.valid?)
154
+ r += value.json_errors.map{|a, b| [json_attribute(attribute, a), b]}
155
+ elsif (value && !value.respond_to?(:valid?) && value.respond_to?(:all?) && !value.all?{|v| v.valid?})
156
+ f = value.find{|v| !v.valid?}
157
+ r += f.json_errors.map{|a, b| [json_attribute(attribute, a), b]}
158
+ else
159
+ r << [json_attribute(attribute, attributes_before_type_cast[attribute.to_s]), e]
160
+ end
161
+ end
162
+ r
163
+ end
164
+
165
+ # TODO This is doing a similar stepping down into the children that json_errors is doing. Would be nice
166
+ # to extract the commond code to make this less horrible and arbitrary
167
+ def level_used_in_children?(level)
168
+ attributes.each_value do |a|
169
+ if a.respond_to?(:level_used?) && a.level_used?(level)
170
+ return true
171
+ elsif a.kind_of?(Array) && a.any?{|b| b.level_used?(level)}
172
+ return true
173
+ end
174
+ end
175
+ false
176
+ end
177
+
178
+ # Have we tried to use this attribute?
179
+ def used_attribute?(a)
180
+ !attributes_before_type_cast[a].nil?
181
+ end
182
+
183
+ def level_used_locally?(level)
184
+ self.class.level_attribute_names(level).any?{|a| used_attribute?(a)}
185
+ end
186
+
187
+ def level_used?(level)
188
+ level_used_locally?(level) || level_used_in_children?(level)
189
+ end
190
+
60
191
  def json_left_overs_is_empty
61
192
  if json_left_overs && !json_left_overs.empty?
62
193
  # We have extra parameters that shouldn't be there
63
- errors.add(:json, "Unexpected parameters in json data: #{MultiJson.dump(json_left_overs)}")
194
+ errors.add(:json, ErrorMessage["Unexpected parameters in json data: #{MultiJson.dump(json_left_overs)}", "4"])
64
195
  end
65
196
  end
66
197
 
@@ -77,36 +208,18 @@ module ATDIS
77
208
  end
78
209
 
79
210
  def self.interpret(*params)
80
- new(map_fields(valid_fields, *params))
81
- end
82
-
83
- # Map json structure to our values
84
- def self.map_fields(valid_fields, data)
85
- values = {:json_left_overs => {}}
86
- data.each_key do |key|
87
- if valid_fields[key]
88
- if valid_fields[key].kind_of?(Hash)
89
- v2 = map_fields(valid_fields[key], data[key])
90
- l2 = v2.delete(:json_left_overs)
91
- values = values.merge(v2)
92
- values[:json_left_overs][key] = l2 unless l2.empty?
93
- else
94
- values[valid_fields[key]] = data[key]
95
- end
96
- else
97
- values[:json_left_overs][key] = data[key]
98
- end
99
- end
100
- values
211
+ new(map_fields(*params).merge(:json_left_overs => unused_data(*params)))
101
212
  end
102
213
 
103
- def self.cast(value, type)
214
+ def self.cast(value, type, options = {})
215
+ if options[:none_is_nil] && value == "none"
216
+ nil
104
217
  # If it's already the correct type then we don't need to do anything
105
- if value.kind_of?(type)
218
+ elsif value.kind_of?(type)
106
219
  value
107
220
  # Special handling for arrays. When we typecast arrays we actually typecast each member of the array
108
221
  elsif value.kind_of?(Array)
109
- value.map {|v| cast(v, type)}
222
+ value.map {|v| cast(v, type, options)}
110
223
  elsif type == DateTime
111
224
  cast_datetime(value)
112
225
  elsif type == URI
@@ -137,7 +250,7 @@ module ATDIS
137
250
 
138
251
  def attribute=(attr, value)
139
252
  @attributes_before_type_cast[attr] = value
140
- @attributes[attr] = Model.cast(value, attribute_types[attr.to_sym])
253
+ @attributes[attr] = Model.cast(value, attribute_types[attr.to_sym][0], attribute_types[attr.to_sym][1] || {})
141
254
  end
142
255
 
143
256
  def self.cast_datetime(value)
data/lib/atdis/page.rb CHANGED
@@ -2,39 +2,48 @@ module ATDIS
2
2
  class Page < Model
3
3
  attr_accessor :url
4
4
 
5
- field_mappings :response => [:results, Application],
6
- :count => [:count, Fixnum],
7
- :pagination => {
8
- :previous => [:previous_page_no, Fixnum],
9
- :next => [:next_page_no, Fixnum],
10
- :current => [:current_page_no, Fixnum],
11
- :per_page => [:no_results_per_page, Fixnum],
12
- :count => [:total_no_results, Fixnum],
13
- :pages => [:total_no_pages, Fixnum]
14
- }
5
+ set_field_mappings [
6
+ [:response, [:results, Application, {:level => 1}]],
7
+ [:count, [:count, Fixnum, {:level => 2}]],
8
+ [:pagination, [
9
+ [:previous, [:previous_page_no, Fixnum, {:level => 2}]],
10
+ [:next, [:next_page_no, Fixnum, {:level => 2}]],
11
+ [:current, [:current_page_no, Fixnum, {:level => 2}]],
12
+ [:per_page, [:no_results_per_page, Fixnum, {:level => 2}]],
13
+ [:count, [:total_no_results, Fixnum, {:level => 2}]],
14
+ [:pages, [:total_no_pages, Fixnum, {:level => 2}]]
15
+ ]]
16
+ ]
15
17
 
16
18
  # Mandatory parameters
17
- validates :results, :presence_before_type_cast => true
19
+ validates :results, :presence_before_type_cast => {:spec_section => "4.3"}
18
20
  validates :results, :valid => true
19
21
  validate :count_is_consistent, :all_pagination_is_present, :previous_page_no_is_consistent, :next_page_no_is_consistent
20
22
  validate :current_page_no_is_consistent, :total_no_results_is_consistent
23
+ validate :json_loaded_correctly!
24
+
25
+ def json_loaded_correctly!
26
+ if json_load_error
27
+ errors.add(:json, ErrorMessage["Invalid JSON: #{json_load_error}", nil])
28
+ end
29
+ end
21
30
 
22
31
  # If some of the pagination fields are present all of the required ones should be present
23
32
  def all_pagination_is_present
24
33
  if count || previous_page_no || next_page_no || current_page_no || no_results_per_page ||
25
34
  total_no_results || total_no_pages
26
- errors.add(:count, "should be present if pagination is being used") if count.nil?
27
- errors.add(:current_page_no, "should be present if pagination is being used") if current_page_no.nil?
28
- errors.add(:no_results_per_page, "should be present if pagination is being used") if no_results_per_page.nil?
29
- errors.add(:total_no_results, "should be present if pagination is being used") if total_no_results.nil?
30
- errors.add(:total_no_pages, "should be present if pagination is being used") if total_no_pages.nil?
35
+ errors.add(:count, ErrorMessage["should be present if pagination is being used", "6.5"]) if count.nil?
36
+ errors.add(:current_page_no, ErrorMessage["should be present if pagination is being used", "6.5"]) if current_page_no.nil?
37
+ errors.add(:no_results_per_page, ErrorMessage["should be present if pagination is being used", "6.5"]) if no_results_per_page.nil?
38
+ errors.add(:total_no_results, ErrorMessage["should be present if pagination is being used", "6.5"]) if total_no_results.nil?
39
+ errors.add(:total_no_pages, ErrorMessage["should be present if pagination is being used", "6.5"]) if total_no_pages.nil?
31
40
  end
32
41
  end
33
42
 
34
43
  def count_is_consistent
35
44
  if count
36
- errors.add(:count, "is not the same as the number of applications returned") if count != results.count
37
- errors.add(:count, "should not be larger than the number of results per page") if count > no_results_per_page
45
+ errors.add(:count, ErrorMessage["is not the same as the number of applications returned", "6.5"]) if count != results.count
46
+ errors.add(:count, ErrorMessage["should not be larger than the number of results per page", "6.5"]) if count > no_results_per_page
38
47
  end
39
48
  end
40
49
 
@@ -42,14 +51,14 @@ module ATDIS
42
51
  if current_page_no
43
52
  if previous_page_no
44
53
  if previous_page_no != current_page_no - 1
45
- errors.add(:previous_page_no, "should be one less than current page number or null if first page")
54
+ errors.add(:previous_page_no, ErrorMessage["should be one less than current page number or null if first page", "6.5"])
46
55
  end
47
56
  if current_page_no == 1
48
- errors.add(:previous_page_no, "should be null if on the first page")
57
+ errors.add(:previous_page_no, ErrorMessage["should be null if on the first page", "6.5"])
49
58
  end
50
59
  else
51
60
  if current_page_no > 1
52
- errors.add(:previous_page_no, "can't be null if not on the first page")
61
+ errors.add(:previous_page_no, ErrorMessage["can't be null if not on the first page", "6.5"])
53
62
  end
54
63
  end
55
64
  end
@@ -57,29 +66,29 @@ module ATDIS
57
66
 
58
67
  def next_page_no_is_consistent
59
68
  if next_page_no && next_page_no != current_page_no + 1
60
- errors.add(:next_page_no, "should be one greater than current page number or null if last page")
69
+ errors.add(:next_page_no, ErrorMessage["should be one greater than current page number or null if last page", "6.5"])
61
70
  end
62
71
  if next_page_no.nil? && current_page_no != total_no_pages
63
- errors.add(:next_page_no, "can't be null if not on the last page")
72
+ errors.add(:next_page_no, ErrorMessage["can't be null if not on the last page", "6.5"])
64
73
  end
65
74
  if next_page_no && current_page_no == total_no_pages
66
- errors.add(:next_page_no, "should be null if on the last page")
75
+ errors.add(:next_page_no, ErrorMessage["should be null if on the last page", "6.5"])
67
76
  end
68
77
  end
69
78
 
70
79
  def current_page_no_is_consistent
71
80
  if current_page_no
72
- errors.add(:current_page_no, "is larger than the number of pages") if current_page_no > total_no_pages
73
- errors.add(:current_page_no, "can not be less than 1") if current_page_no < 1
81
+ errors.add(:current_page_no, ErrorMessage["is larger than the number of pages", "6.5"]) if current_page_no > total_no_pages
82
+ errors.add(:current_page_no, ErrorMessage["can not be less than 1", "6.5"]) if current_page_no < 1
74
83
  end
75
84
  end
76
85
 
77
86
  def total_no_results_is_consistent
78
87
  if total_no_pages && total_no_results > total_no_pages * no_results_per_page
79
- errors.add(:total_no_results, "is larger than can be retrieved through paging")
88
+ errors.add(:total_no_results, ErrorMessage["is larger than can be retrieved through paging", "6.5"])
80
89
  end
81
90
  if total_no_pages && total_no_results <= (total_no_pages - 1) * no_results_per_page
82
- errors.add(:total_no_results, "could fit into a smaller number of pages")
91
+ errors.add(:total_no_results, ErrorMessage["could fit into a smaller number of pages", "6.5"])
83
92
  end
84
93
  end
85
94
 
@@ -90,7 +99,14 @@ module ATDIS
90
99
  end
91
100
 
92
101
  def self.read_json(text)
93
- interpret(MultiJson.load(text, :symbolize_keys => true))
102
+ begin
103
+ data = MultiJson.load(text, :symbolize_keys => true)
104
+ interpret(data)
105
+ rescue MultiJson::LoadError => e
106
+ a = interpret({:response => []})
107
+ a.json_load_error = e.to_s
108
+ a
109
+ end
94
110
  end
95
111
 
96
112
  def previous_url