api_object 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -129,7 +129,16 @@ The function takes parameters to define what fields from the location object are
129
129
  data = Weather.get_results(:weather => '99.156.82.20')
130
130
  ```
131
131
 
132
- 7) Testing
132
+ 7) Error handling
133
+
134
+ In case data cannot be retrived (possible causes might be a wrong url or service downtime), the object returned is empty. Error messages could be checked using functions errors and has_errors?
135
+
136
+ ```
137
+ station.has_errors?
138
+ errors = station.errors
139
+ ```
140
+
141
+ 8) Testing
133
142
 
134
143
  The gem has been tested on BART, Google Weather and NextBus APIs.
135
144
 
@@ -143,9 +152,10 @@ API_KEY='<your key>' rake test
143
152
 
144
153
  There is no existing api key provided with this gem as per the Terms and Conditions of the ipinfodb service.
145
154
 
146
- 8) Limitations
155
+ 9) Limitations
147
156
 
148
157
  * Api data must be presented either in XML or in JSON format. The distinction between XML and JSON is determinted automatically.
158
+ * Location by ip service uses a free database which is not always reliable.
149
159
  * When using this gem with external APIs, check Terms and Conditions of the API usage.
150
160
  * If something is not working, feel free to submit bugs and or/contribute.
151
161
 
@@ -1,3 +1,3 @@
1
1
  module ApiObject
2
- VERSION = "0.5.1"
2
+ VERSION = "0.6.0"
3
3
  end
data/lib/api_object.rb CHANGED
@@ -4,17 +4,19 @@ require "active_support/all"
4
4
  require "geo_ip"
5
5
 
6
6
  module ActiveApi
7
-
7
+
8
8
  module ClassMethods
9
-
9
+
10
10
  def self.extended(base)
11
11
  base.send(:extend, Query)
12
12
  end
13
13
 
14
14
  def initialize_from_api options = {}
15
+
15
16
  class_attribute :url, :action, :key, :mode, :url_options, :data_tags, :object_name
16
17
  self.url, self.action, self.key, self.mode, self.url_options, self.data_tags, self.object_name = [options[:url], options[:action], options[:key], options[:mode], (options[:url_options] || {}), ([*options[:data_tags]] || []), (options[:object_name] || self.to_s.downcase.gsub(/^(.+::)(.+)$/, '\2'))]
17
18
  instance_eval do
19
+
18
20
 
19
21
  def get_results_by_ip ip, arguments = {}
20
22
  self.api_key = arguments.delete(:key) if arguments[:key]
@@ -22,8 +24,8 @@ module ActiveApi
22
24
  raise unless location[:status_code] == "OK"
23
25
  get_results [*arguments.keys].inject({}) { |opts, a| opts.merge(a.to_sym => location[arguments[a.to_sym]]) }
24
26
  rescue
25
- puts "WARNING: Cannot get results or location by ip. Verify that you have a valid key for the ipinfodb.com service"
26
- return {}
27
+ puts "ERROR: Cannot get results or location by ip. Verify that you have a valid key for the ipinfodb.com service"
28
+ return ApiObjectError.new(:class => self, :errors => invalid_loc_msg)
27
29
  end
28
30
 
29
31
  def get_results options = {}
@@ -32,8 +34,8 @@ module ActiveApi
32
34
  result = query_api(self.url, self.action, self.mode, self.url_options.merge(options))
33
35
  process_result result
34
36
  rescue
35
- puts "WARNING: The request returned no valid data. #{warning_invalid_url result[:url]}"
36
- return {}
37
+ puts "ERROR: The request returned no valid data. #{error_invalid_url result[:url]}"
38
+ return ApiObjectError.new(:class => self, :errors => invalid_url_msg)
37
39
  end
38
40
 
39
41
  def api_key=(key)
@@ -44,9 +46,21 @@ module ActiveApi
44
46
  GeoIp.api_key
45
47
  end
46
48
 
47
- def warning_invalid_url url
49
+ def error_invalid_url url
48
50
  "The request url is #{url}, please, check if it's invalid of there is no connectivity." unless url.nil?
49
51
  end
52
+
53
+ def invalid_url_msg
54
+ "Cannot get results from the url"
55
+ end
56
+
57
+ def invalid_loc_msg
58
+ "Cannot obtain results by ip, check the location"
59
+ end
60
+
61
+ def invalid_data_msg
62
+ "Cannot initialize the object"
63
+ end
50
64
 
51
65
  private
52
66
  def process_result result
@@ -74,9 +88,10 @@ module ActiveApi
74
88
  end
75
89
 
76
90
  module InstanceMethods
77
-
91
+
78
92
  def initialize(*args)
79
93
  url = args.first.delete(:url)
94
+ errors = args.first.delete(:errors)
80
95
  tags = respond_to?('data_tags') ? self.data_tags : args.last[:tags]
81
96
  args.first.each do |k, v|
82
97
  k = k.gsub(/@/, '')
@@ -87,11 +102,18 @@ module ActiveApi
87
102
  instance_variable_set("@#{k.to_s}", result)
88
103
  end
89
104
  end if args.first.is_a?(Hash)
90
- puts "WARNING: data passed for #{self.class} initialization was invalid. #{self.class.warning_invalid_url url}" if self.empty?
105
+ if self.empty?
106
+ puts "ERROR: data passed for #{self.class} initialization was invalid. #{self.class.error_invalid_url url}"
107
+ @errors = errors || self.class.invalid_data_msg
108
+ end
91
109
  end
92
110
 
93
111
  def empty?
94
- self.instance_variables.empty?
112
+ (self.instance_variables - [:@errors]).empty?
113
+ end
114
+
115
+ def has_errors?
116
+ self.instance_variables.include?(:@errors)
95
117
  end
96
118
 
97
119
 
@@ -142,8 +164,41 @@ module ActiveApi
142
164
  class_attribute :columns, :assoc
143
165
  self.columns, self.assoc = [{}, {}]
144
166
 
145
- end
167
+ attr_reader :errors
168
+
169
+ end
146
170
 
147
-
171
+ class ApiObjectError < Hash
172
+ attr_reader :errors, :klass
173
+
174
+ def initialize *args
175
+ options = args.extract_options!
176
+ @errors = options.delete(:errors)
177
+ @klass = options.delete(:class)
178
+ end
179
+
180
+ def keys
181
+ [:errors]
182
+ end
183
+
184
+ def [](key)
185
+ eval("self.#{key.to_s}") if keys.include?(key)
186
+ end
187
+
188
+ def map &block
189
+ klass.new({:errors => @errors})
190
+ end
191
+
192
+ alias_method :collect, :map
193
+
194
+ def inspect
195
+ "ApiObjectError: #{@errors}"
196
+ end
197
+
198
+ def to_s
199
+ inspect
200
+ end
201
+ end
202
+
148
203
  end
149
-
204
+
@@ -8,11 +8,15 @@ class ApiObjectTest < MiniTest::Unit::TestCase
8
8
  include ActiveApi
9
9
  include TestObjects
10
10
 
11
+ IP = '99.156.82.20'
12
+ GeoIp.api_key = nil
13
+
11
14
  @@data_directory = File.expand_path('../../data', __FILE__)
12
15
  @@estimate_directory = @@data_directory + "/estimate"
13
16
  @@weather_directory = @@data_directory + "/weather"
14
17
  @@bus_directory = @@data_directory + "/bus"
15
18
  @@key_directory = @@data_directory +"/keys"
19
+
16
20
 
17
21
  @@glen_estimate = Station.load_from_xml(File.read(@@estimate_directory + '/glen.xml'))
18
22
  @@sixteenth_estimate = Station.load_from_xml(File.read(@@estimate_directory + '/sixteenth.xml'))
@@ -21,8 +25,6 @@ class ApiObjectTest < MiniTest::Unit::TestCase
21
25
  @@weather_mv = Weather.load_from_xml(File.read(@@weather_directory + '/mountain_view.xml'))
22
26
  @@weather_au = Weather.load_from_xml(File.read(@@weather_directory + '/austin.xml'))
23
27
  @@ip_key = File.read(@@key_directory + "/ipinfodb_key.txt") rescue ENV['API_KEY']
24
- IP = '99.156.82.20'
25
- GeoIp.api_key = nil
26
28
 
27
29
  @@muni_routes = Route.load_from_xml(File.read(@@bus_directory + "/muni_routes.xml"))
28
30
  @@muni_F = Route.load_from_xml(File.read(@@bus_directory + "/muni_F.xml"))
@@ -64,6 +66,7 @@ class ApiObjectTest < MiniTest::Unit::TestCase
64
66
  unless @@ip_key.nil?
65
67
  weather_au = Weather.new(Weather.get_results_by_ip(IP, :key => @@ip_key, :weather => :zip_code))
66
68
  assert_equal(weather_au, @@weather_au)
69
+ refute_has_errors(weather_au)
67
70
  end
68
71
  end
69
72
 
@@ -71,33 +74,44 @@ class ApiObjectTest < MiniTest::Unit::TestCase
71
74
  GeoIp.api_key = @@ip_key
72
75
  weather_au = Weather.new(Weather.get_results_by_ip(IP, :weather => :zip_code))
73
76
  assert_equal(weather_au, @@weather_au)
77
+ refute_has_errors(weather_au)
74
78
  end
75
-
79
+
76
80
  def test_should_not_get_correct_weather_with_no_key
77
81
  GeoIp.api_key = nil
78
82
  weather_au = Weather.new(Weather.get_results_by_ip(IP, :weather => :zip_code))
79
83
  assert_empty(weather_au)
84
+ assert_has_errors(weather_au)
80
85
  end
81
86
 
82
87
  def test_should_get_correct_bus_routes
83
88
  routes = Route.get_results(:a => 'sf-muni', :command => 'routeList').map {|r| Route.new(r)}
84
89
  assert_equal_route_list(routes, @@muni_routes)
90
+ routes.map {|r| refute_has_errors(r)}
85
91
  end
86
-
92
+
87
93
  def test_should_get_correct_route_information
88
94
  route_F = Route.new(Route.get_results(:a => 'sf-muni', :command => 'routeConfig', :r => 'F', :mode => 'terse'))
89
95
  assert_equal(route_F, @@muni_F)
96
+ refute_has_errors(route_F)
90
97
  end
91
-
98
+
92
99
  def test_should_handle_invalid_url
93
- estimate = Station.new(Station.get_results(:orig => 'YES'))
100
+ estimate = Station.new(Station.get_results(:orig => 'YES'))
94
101
  assert(estimate.empty?, "Estimate should be an empty object")
102
+ assert_has_errors(estimate)
103
+ assert(estimate.errors, Station.invalid_url_msg)
95
104
  weather = Weather.new(Weather.get_results(:weather => "Here"))
96
105
  assert(weather.empty?, "Weather should be an empty object")
97
- routes = Route.get_results(:a => 'sffg').map {|r| Route.new(r)}
106
+ assert_has_errors(weather)
107
+ assert(weather.errors, Weather.invalid_data_msg)
108
+ routes = Route.get_results(:a => 'sffg').collect {|r| Route.new(r)}
98
109
  assert(routes.empty?, "Routes should be an empty object")
110
+ assert_has_errors(routes)
111
+ assert(routes.errors, Route.invalid_url_msg)
99
112
  end
100
113
 
114
+
101
115
  private
102
116
 
103
117
  #ensure that the estimates of the first station include the sample
@@ -119,6 +133,14 @@ private
119
133
  return assert(route_list.include_all?(sample_route_list), "Route list should include all the sample routes")
120
134
  end
121
135
 
136
+ def assert_has_errors obj
137
+ return assert(obj.has_errors?, "Object should have errors")
138
+ end
139
+
140
+ def refute_has_errors obj
141
+ return refute(obj.has_errors?, "Object shouldn't have errors")
142
+ end
143
+
122
144
 
123
145
  def business_time? now
124
146
  unless now.to_date.sunday?
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: api_object
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.6.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-13 00:00:00.000000000 Z
12
+ date: 2012-06-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activemodel
16
- requirement: &74735810 !ruby/object:Gem::Requirement
16
+ requirement: &76922910 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *74735810
24
+ version_requirements: *76922910
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec
27
- requirement: &74735560 !ruby/object:Gem::Requirement
27
+ requirement: &76922660 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *74735560
35
+ version_requirements: *76922660
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: minitest
38
- requirement: &74735340 !ruby/object:Gem::Requirement
38
+ requirement: &77001800 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *74735340
46
+ version_requirements: *77001800
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: activesupport
49
- requirement: &74735110 !ruby/object:Gem::Requirement
49
+ requirement: &77001550 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :runtime
56
56
  prerelease: false
57
- version_requirements: *74735110
57
+ version_requirements: *77001550
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: nori
60
- requirement: &74734810 !ruby/object:Gem::Requirement
60
+ requirement: &77001240 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '1.1'
66
66
  type: :runtime
67
67
  prerelease: false
68
- version_requirements: *74734810
68
+ version_requirements: *77001240
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rest-client
71
- requirement: &74734530 !ruby/object:Gem::Requirement
71
+ requirement: &77000930 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '1.6'
77
77
  type: :runtime
78
78
  prerelease: false
79
- version_requirements: *74734530
79
+ version_requirements: *77000930
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: geo_ip
82
- requirement: &74734330 !ruby/object:Gem::Requirement
82
+ requirement: &77000700 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ! '>='
@@ -87,7 +87,7 @@ dependencies:
87
87
  version: '0'
88
88
  type: :runtime
89
89
  prerelease: false
90
- version_requirements: *74734330
90
+ version_requirements: *77000700
91
91
  description: An interface to load objects from external APIs provided in XML and JSON
92
92
  formats
93
93
  email: