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 +12 -2
- data/lib/api_object/version.rb +1 -1
- data/lib/api_object.rb +68 -13
- data/test/unit/api_object_test.rb +29 -7
- metadata +16 -16
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)
|
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
|
-
|
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
|
|
data/lib/api_object/version.rb
CHANGED
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 "
|
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 "
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
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-
|
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: &
|
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: *
|
24
|
+
version_requirements: *76922910
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
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: *
|
35
|
+
version_requirements: *76922660
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: minitest
|
38
|
-
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: *
|
46
|
+
version_requirements: *77001800
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: activesupport
|
49
|
-
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: *
|
57
|
+
version_requirements: *77001550
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: nori
|
60
|
-
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: *
|
68
|
+
version_requirements: *77001240
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rest-client
|
71
|
-
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: *
|
79
|
+
version_requirements: *77000930
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
81
|
name: geo_ip
|
82
|
-
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: *
|
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:
|