jnewland-fireeagle 0.7.1.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.
data/History.txt ADDED
@@ -0,0 +1,33 @@
1
+ == 0.7.1.0
2
+
3
+ * Fix OAuth Dependency Issue
4
+
5
+ == 0.6.3
6
+
7
+ * Bugfix for Ruby 1.9
8
+
9
+ == 0.6.2
10
+
11
+ * Offload heavy lifting to OAuth for tokens and signing requests
12
+ * Add time parameter to Client#recent
13
+ * Change lookup params from :cid to :cellid to follow Fire Eagle
14
+ * Cleaned up specs
15
+
16
+ == 0.6.1
17
+
18
+ * Remove 'not implemented' message
19
+ * Remove JSON requirement
20
+
21
+ == 0.6.0
22
+
23
+ * Complete rewrite
24
+
25
+ == 0.0.2
26
+
27
+ * Fix callback_url
28
+ * Add token_url
29
+
30
+ == 0.0.1
31
+
32
+ * Initial version
33
+ * Works
data/License.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Jesse Newland
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest.txt ADDED
@@ -0,0 +1,21 @@
1
+ config/hoe.rb
2
+ config/requirements.rb
3
+ History.txt
4
+ License.txt
5
+ Manifest.txt
6
+ README.txt
7
+ Rakefile
8
+ lib/fireeagle.rb
9
+ lib/fireeagle/client.rb
10
+ lib/fireeagle/location.rb
11
+ lib/fireeagle/response.rb
12
+ lib/fireeagle/user.rb
13
+ lib/fireeagle/version.rb
14
+ setup.rb
15
+ spec/fireeagle_location_spec.rb
16
+ spec/fireeagle_response_spec.rb
17
+ spec/fireeagle_spec.rb
18
+ spec/spec.opts
19
+ spec/spec_helper.rb
20
+ tasks/environment.rake
21
+ tasks/rspec.rake
data/README.txt ADDED
@@ -0,0 +1,52 @@
1
+ FireEagle[http://fireeagle.yahoo.net] (FE) is a system providing centralized
2
+ management of user location information. FE allows 3rd party developers to
3
+ pdate and/or access user's location data.
4
+
5
+ http://fireeagle.yahoo.net/developer/documentation
6
+
7
+ == Installation
8
+
9
+ gem install fireeagle
10
+
11
+ == Usage
12
+
13
+ >> require 'fireeagle'
14
+ >> client = FireEagle::Client.new(
15
+ :consumer_key => "<consumer key>",
16
+ :consumer_secret => "<consumer secret>",
17
+ :access_token => "[access token]",
18
+ :access_token_secret => "[access token secret]")
19
+
20
+ ==== With a User-specific OAuth Access Token
21
+
22
+ # update your location
23
+ >> client.update(:q => "punta del diablo, uruguay") # I wish
24
+ # query your location
25
+ >> user = client.user
26
+ => #<FireEagle::User:0x1ca5e08 ... >
27
+ >> user.locations
28
+ => [#<FireEagle::Location:0x1cdd9e8 ...>, #<FireEagle::Location:0x1cc8ffc ...>, ... ]
29
+ >> user.best_guess
30
+ => #<FireEagle::Location:0x1cdd9e8 ...>
31
+ >> user.best_guess.name
32
+ => "Punta del Diablo, Uruguay"
33
+ # lookup a location
34
+ >> locations = client.lookup(:q => "30022")
35
+ => [#<FireEagle::Location:0x1cdd9e8 ...>, #<FireEagle::Location:0x1cc8ffc ...>, ...]
36
+ >> locations.first.name => "Alpharetta, GA 30022"
37
+ >> locations.first.place_id => "IrhZMHuYA5s1fFi4Qw"
38
+
39
+ == Authorization
40
+
41
+ Authorization is handled by OAuth. For more details about the OAuth
42
+ authorization flow and how it differs based on your application type, please
43
+ see http://fireeagle.yahoo.net/developer/documentation/authorizing
44
+
45
+ == Contributing
46
+
47
+ http://github.com/jnewland/fireeagle/tree/master. You know what to do.
48
+
49
+ Rubyforge Project Page:: http://rubyforge.org/projects/fireeagle
50
+ Author:: Jesse Newland (http://soylentfoo.jnewland.com) (jnewland@gmail.com[mailto:jnewland@gmail.com])
51
+ Copyright:: Copyright (c) 2008 Jesse Newland. Portions[http://pastie.caboo.se/private/oevvkdzl0zrdkf8s7hetg] Copyright (c) 2008 Yahoo!
52
+ License:: Distributed under the same terms as Ruby
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require 'config/requirements'
2
+ require 'config/hoe' # setup Hoe + all gem configuration
3
+
4
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
data/config/hoe.rb ADDED
@@ -0,0 +1,70 @@
1
+ require 'fireeagle/version'
2
+
3
+ AUTHOR = 'Jesse Newland' # can also be an array of Authors
4
+ EMAIL = "jnewland@gmail.com"
5
+ DESCRIPTION = "Ruby wrapper for Yahoo!'s FireEagle"
6
+ GEM_NAME = 'fireeagle' # what ppl will type to install your gem
7
+ RUBYFORGE_PROJECT = 'fireeagle' # The unix name for your project
8
+ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
9
+ DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
10
+
11
+ @config_file = "~/.rubyforge/user-config.yml"
12
+ @config = nil
13
+ RUBYFORGE_USERNAME = "unknown"
14
+ def rubyforge_username
15
+ unless @config
16
+ begin
17
+ @config = YAML.load(File.read(File.expand_path(@config_file)))
18
+ rescue
19
+ puts <<-EOS
20
+ ERROR: No rubyforge config file found: #{@config_file}
21
+ Run 'rubyforge setup' to prepare your env for access to Rubyforge
22
+ - See http://newgem.rubyforge.org/rubyforge.html for more details
23
+ EOS
24
+ exit
25
+ end
26
+ end
27
+ RUBYFORGE_USERNAME.replace @config["username"]
28
+ end
29
+
30
+
31
+ REV = nil
32
+ # UNCOMMENT IF REQUIRED:
33
+ # REV = `svn info`.each {|line| if line =~ /^Revision:/ then k,v = line.split(': '); break v.chomp; else next; end} rescue nil
34
+ VERS = FireEagle::VERSION::STRING + (REV ? ".#{REV}" : "")
35
+ RDOC_OPTS = ['--quiet', '--title', 'fireeagle rdoc',
36
+ "--opname", "index.html",
37
+ "--line-numbers",
38
+ "--main", "README",
39
+ "--inline-source"]
40
+
41
+ class Hoe
42
+ def extra_deps
43
+ @extra_deps.reject! { |x| Array(x).first == 'hoe' }
44
+ @extra_deps
45
+ end
46
+ end
47
+
48
+ # Generate all the Rake tasks
49
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
50
+ hoe = Hoe.new(GEM_NAME, VERS) do |p|
51
+ p.developer(AUTHOR, EMAIL)
52
+ p.description = DESCRIPTION
53
+ p.summary = DESCRIPTION
54
+ p.url = HOMEPATH
55
+ p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
56
+ p.test_globs = ["test/**/test_*.rb"]
57
+ p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
58
+
59
+ # == Optional
60
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
61
+ p.extra_deps = [ ['oauth', '>= 0.2.1'], ['hpricot', '>= 0.6'], ['GeoRuby', '>= 1.3.2'] ] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
62
+
63
+ #p.spec_extras = {} # A hash of extra values to set in the gemspec.
64
+
65
+ end
66
+
67
+ CHANGES = hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
68
+ PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
69
+ hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
70
+ hoe.rsync_args = '-av --delete --ignore-errors'
@@ -0,0 +1,18 @@
1
+ require 'fileutils'
2
+ include FileUtils
3
+
4
+ require 'rubygems'
5
+ %w[rake hoe newgem rubigen oauth geo_ruby].each do |req_gem|
6
+ begin
7
+ require req_gem
8
+ rescue LoadError
9
+ req_gem = 'GeoRuby' if req_gem == 'geo_ruby' # gem not the same name
10
+ puts "This Rakefile requires the '#{req_gem}' RubyGem."
11
+ puts "Installation: gem install #{req_gem} -y"
12
+ exit
13
+ end
14
+ end
15
+
16
+ $:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
17
+
18
+ require 'fireeagle'
@@ -0,0 +1,283 @@
1
+ class FireEagle
2
+ class Client
3
+ # TODO add access_token=() and request_token=() methods that check whether the tokens are usable
4
+
5
+ attr_reader :access_token, :request_token, :consumer, :format
6
+
7
+ # Initialize a FireEagle Client. Takes an options Hash.
8
+ #
9
+ # == Required keys:
10
+ #
11
+ # [<tt>:consumer_key</tt>] OAuth Consumer key representing your FireEagle Application
12
+ # [<tt>:consumer_secret</tt>] OAuth Consumer secret representing your FireEagle Application
13
+ #
14
+ # == Optional keys:
15
+ #
16
+ # [<tt>:request_token</tt>] OAuth Request Token, for use with convert_to_access_token
17
+ # [<tt>:request_token_secret</tt>] OAuth Request Token Secret, for use with convert_to_access_token
18
+ # [<tt>:access_token</tt>] OAuth Token, either User-specific or General-purpose
19
+ # [<tt>:access_token_secret</tt>] OAuth Token, either User-specific or General-purpose
20
+ # [<tt>:app_id</tt>] Your Mobile Application ID
21
+ # [<tt>:debug</tt>] Boolean
22
+ #
23
+ # User-specific OAuth tokens tie FireEagle users to your application. As such, they are intended to be
24
+ # distributed (with keys) to that user's mobile device and/or computer running your desktop or mobile client.
25
+ # For web-based applications User-specific tokens will be retrieved by your web server where they should be
26
+ # treated as private data. Take care to avoid releasing this data to the public, as the corresponding User's location
27
+ # information may be inadvertently exposed. User-specific OAuth tokens should be considered the property of
28
+ # your users.
29
+ #
30
+ # General-purpose OAuth tokens are tied to your application and allow you, as a developer, to make more
31
+ # general (often batch-style) queries against FireEagle. As a result, allowing this token/secret combination
32
+ # loose has the potential to reveal a much greater amount of personal data. In an attempt to mitigate this, we will
33
+ # only grant general-purpose tokens to web applications (contact us with details, if you seek an exception). In
34
+ # addition, we require developers to provide a restrictive IP range at registration time in order to further mitigate
35
+ # the risk of general-purpose tokens being used inappropriately.
36
+ #
37
+ # In general, OAuth tokens should be considered sacrosanct in order to help us respect our users' privacy. Please
38
+ # take this responsibility on as your own. If your Application Oauth tokens are compromised, FireEagle will
39
+ # turn off your application service until the problem is resolved.
40
+ #
41
+ # If the Client is initialized without an OAuth access token, it's assumed you're operating a non-web based application.
42
+ #
43
+ # == Non web-based applications
44
+ #
45
+ # For non web-based applications, such as a mobile client application, the authentication between the user and
46
+ # the application is slightly different. The request token is displayed to the user by the client application. The
47
+ # user then logs into the FireEagle website (using mobile_authorization_url) and enters this code to authorize the application.
48
+ # When the user finishes the authorization step the client application exchanges the request token for an access token
49
+ # (using convert_to_access_token). This is a lightweight method for non-web application users to authenticate an application
50
+ # without entering any identifying information into a potentially insecure application. Request tokens are valid for only
51
+ # 1 hour after being issued.
52
+ #
53
+ # == Example mobile-based authentication flow:
54
+ #
55
+ # Initialize a client with your consumer key, consumer secret, and your mobile application id:
56
+ #
57
+ # >> c = FireEagle::Client.new(:consumer_key => "key", :consumer_secret => "sekret", :app_id => 12345)
58
+ # => #<FireEagle::Client:0x1ce2e70 ... >
59
+ #
60
+ # Generate a request token:
61
+ #
62
+ # >> c.get_request_token
63
+ # => #<OAuth::Token:0x1cdb5bc @token="ENTER_THIS_TOKEN", @secret="sekret">
64
+ #
65
+ # Prompt your user to visit your app's mobile authorization url and enter ENTER_THIS_TOKEN:
66
+ #
67
+ # >> c.mobile_authorization_url
68
+ # => "http://fireeagle.yahoo.net/oauth/mobile_auth/12345"
69
+ #
70
+ # Once the user has indicated to you that they've done this, convert their request token to an access token:
71
+ #
72
+ # >> c.convert_to_access_token
73
+ # => #<OAuth::Token:0x1cd3bf0 @token="access_token", @secret="access_token_secret">
74
+ #
75
+ # You're done!
76
+ def initialize(options = {})
77
+ options = {
78
+ :debug => false,
79
+ :format => FireEagle::FORMAT_XML
80
+ }.merge(options)
81
+
82
+ # symbolize keys
83
+ options.map do |k,v|
84
+ options[k.to_sym] = v
85
+ end
86
+ raise FireEagle::ArgumentError, "OAuth Consumer Key and Secret required" if options[:consumer_key].nil? || options[:consumer_secret].nil?
87
+ @consumer = OAuth::Consumer.new(options[:consumer_key], options[:consumer_secret], :site => FireEagle::API_SERVER, :authorize_url => FireEagle::AUTHORIZATION_URL)
88
+ @debug = options[:debug]
89
+ @format = options[:format]
90
+ @app_id = options[:app_id]
91
+ if options[:access_token] && options[:access_token_secret]
92
+ @access_token = OAuth::AccessToken.new(@consumer, options[:access_token], options[:access_token_secret])
93
+ else
94
+ @access_token = nil
95
+ end
96
+ if options[:request_token] && options[:request_token_secret]
97
+ @request_token = OAuth::RequestToken.new(@consumer, options[:request_token], options[:request_token_secret])
98
+ else
99
+ @request_token = nil
100
+ end
101
+ end
102
+
103
+ # Obtain an <strong>new</strong> unauthorized OAuth Request token
104
+ def get_request_token(force_token_regeneration = false)
105
+ if force_token_regeneration || @request_token.nil?
106
+ @request_token = consumer.get_request_token
107
+ end
108
+ @request_token
109
+ end
110
+
111
+ # Return the Fire Eagle authorization URL for your mobile application. At this URL, the User will be prompted for their request_token.
112
+ def mobile_authorization_url
113
+ raise FireEagle::ArgumentError, ":app_id required" if @app_id.nil?
114
+ "#{FireEagle::MOBILE_AUTH_URL}#{@app_id}"
115
+ end
116
+
117
+ # The URL the user must access to authorize this token. request_token must be called first. For use by web-based and desktop-based applications.
118
+ def authorization_url
119
+ raise FireEagle::ArgumentError, "call #get_request_token first" if @request_token.nil?
120
+ request_token.authorize_url
121
+ end
122
+
123
+ #Exchange an authorized OAuth Request token for an access token. For use by desktop-based and mobile applications.
124
+ def convert_to_access_token
125
+ raise FireEagle::ArgumentError, "call #get_request_token and have user authorize the token first" if @request_token.nil?
126
+ @access_token = request_token.get_access_token
127
+ end
128
+
129
+ # Disambiguates potential values for update query. Results from lookup can be passed to
130
+ # update to ensure that FireEagle will understand how to parse the Location Hash.
131
+ #
132
+ # All three Location methods (lookup, update, and within) accept a Location Hash.
133
+ #
134
+ # There is a specific order for looking up locations. For example, if you provide lat, lon, and address,
135
+ # FireEagle will use the the latitude and longitude geo-coordinates and ignore the address.
136
+ #
137
+ # Location Hash keys, in order of priority:
138
+ #
139
+ # [<tt>(:lat, :lon)</tt>] both required, valid values are floats of -180 to 180 for lat and -90 to 90 for lon
140
+ # [<tt>(:woeid)</tt>] Where on Earth ID
141
+ # [<tt>:place_id</tt>] Place ID (via Flickr/Upcomoing); deprecated in favor of WOE IDs when possible
142
+ # [<tt>:address</tt>] street address (may contain a full address, but will be combined with postal, city, state, and country when available)
143
+ # [<tt>(:mnc, :mcc, :lac, :cellid)</tt>] cell tower information, all required (as integers) for a valid tower location
144
+ # [<tt>:postal</tt>] a ZIP or postal code (combined with address, city, state, and country when available)
145
+ # [<tt>:city</tt>] city (combined with address, postal, state, and country when available)
146
+ # [<tt>:state</tt>] state (combined with address, postal, city, and country when available)
147
+ # [<tt>:country</tt>] country (combined with address, postal, city, and state when available)
148
+ # [<tt>:q</tt>] Free-text fallback containing user input. Lat/lon pairs and geometries will be extracted if possible, otherwise this string will be geocoded as-is.
149
+ #
150
+ # Not yet supported:
151
+ #
152
+ # * <tt>upcoming_venue_id</tt>
153
+ # * <tt>yahoo_local_id</tt>
154
+ # * <tt>plazes_id</tt>
155
+ def lookup(params)
156
+ raise FireEagle::ArgumentError, "OAuth Access Token Required" unless @access_token
157
+ response = get(FireEagle::LOOKUP_API_PATH + ".#{format}", :params => params)
158
+ FireEagle::Response.new(response.body).locations
159
+ end
160
+
161
+ # Sets a User's current Location using using a Place ID hash or a set of Location parameters. If the User
162
+ # provides a Location unconfirmed with lookup then FireEagle makes a best guess as to the User's Location.
163
+ #
164
+ # All three Location methods (lookup, update, and within) accept a Location Hash.
165
+ #
166
+ # There is a specific order for looking up locations. For example, if you provide lat, lon, and address,
167
+ # FireEagle will use the the latitude and longitude geo-coordinates and ignore the address.
168
+ #
169
+ # Location Hash keys, in order of priority:
170
+ #
171
+ # [<tt>(:lat, :lon)</tt>] both required, valid values are floats of -180 to 180 for lat and -90 to 90 for lon
172
+ # [<tt>:place_id</tt>] Place ID - valid values decrypts to an integer value
173
+ # [<tt>:address</tt>] street address (may contain a full address, but will be combined with postal, city, state, and country when available)
174
+ # [<tt>(:mnc, :mcc, :lac, :cellid)</tt>] cell tower information, all required (as integers) for a valid tower location
175
+ # [<tt>:postal</tt>] a ZIP or postal code (combined with address, city, state, and country when available)
176
+ # [<tt>:city</tt>] city (combined with address, postal, state, and country when available)
177
+ # [<tt>:state</tt>] state (combined with address, postal, city, and country when available)
178
+ # [<tt>:country</tt>] country (combined with address, postal, city, and state when available)
179
+ # [<tt>:q</tt>] Free-text fallback containing user input. Lat/lon pairs and geometries will be extracted if possible, otherwise this string will be geocoded as-is.
180
+ #
181
+ # Not yet supported:
182
+ #
183
+ # * <tt>upcoming_venue_id</tt>
184
+ # * <tt>yahoo_local_id</tt>
185
+ # * <tt>plazes_id</tt>
186
+ def update(location = {})
187
+ raise FireEagle::ArgumentError, "OAuth Access Token Required" unless @access_token
188
+ location = sanitize_location_hash(location)
189
+ response = post(FireEagle::UPDATE_API_PATH + ".#{format}", :params => location)
190
+ FireEagle::Response.new(response.body)
191
+ end
192
+
193
+ # Returns the Location of a User.
194
+ def user
195
+ raise FireEagle::ArgumentError, "OAuth Access Token Required" unless @access_token
196
+ response = get(FireEagle::USER_API_PATH + ".#{format}")
197
+ FireEagle::Response.new(response.body).users.first
198
+ end
199
+ alias_method :location, :user
200
+
201
+ # Query for Users of an Application who have updated their Location recently. Returns a list of
202
+ # Users for the Application with recently updated locations.
203
+ #
204
+ # == Optional parameters:
205
+ #
206
+ # <tt>count</tt> Number of users to return per page. (default: 10)
207
+ # <tt>start</tt> The page number at which to start returning the list of users. Pages are 0-indexed, each page contains the per_page number of users. (default: 0)
208
+ # <tt>time</tt> The time to start looking at recent updates from. Value is flexible, supported forms are 'now', 'yesterday', '12:00', '13:00', '1:00pm' and '2008-03-12 12:34:56'. (default: 'now')
209
+ def recent(count = 10, start = 0, time = 'now')
210
+ raise FireEagle::ArgumentError, "OAuth Access Token Required" unless @access_token
211
+ params = { :count => count, :start => start, :time => time }
212
+ response = get(FireEagle::RECENT_API_PATH + ".#{format}", :params => params)
213
+ FireEagle::Response.new(response.body).users
214
+ end
215
+
216
+ # Takes a Place ID or a Location and returns a list of users of your application who are within the bounding box of that Location.
217
+ #
218
+ # Location Hash keys, in order of priority:
219
+ #
220
+ # [<tt>(:lat, :lon)</tt>] both required, valid values are floats of -180 to 180 for lat and -90 to 90 for lon
221
+ # [<tt>:woeid</tt>] Where on Earth ID
222
+ # [<tt>:place_id</tt>] Place ID
223
+ # [<tt>:address</tt>] street address (may contain a full address, but will be combined with postal, city, state, and country when available)
224
+ # [<tt>(:mnc, :mcc, :lac, :cellid)</tt>] cell tower information, all required (as integers) for a valid tower location
225
+ # [<tt>:postal</tt>] a ZIP or postal code (combined with address, city, state, and country when available)
226
+ # [<tt>:city</tt>] city (combined with address, postal, state, and country when available)
227
+ # [<tt>:state</tt>] state (combined with address, postal, city, and country when available)
228
+ # [<tt>:country</tt>] country (combined with address, postal, city, and state when available)
229
+ # [<tt>:q</tt>] Free-text fallback containing user input. Lat/lon pairs and geometries will be extracted if possible, otherwise this string will be geocoded as-is.
230
+ #
231
+ # Not yet supported:
232
+ #
233
+ # * <tt>upcoming_venue_id</tt>
234
+ # * <tt>yahoo_local_id</tt>
235
+ # * <tt>plazes_id</tt>
236
+ def within(location = {}, count = 10, start = 0)
237
+ raise FireEagle::ArgumentError, "OAuth Access Token Required" unless @access_token
238
+ location = sanitize_location_hash(location)
239
+ params = { :count => count, :start => start }.merge(location)
240
+ response = get(FireEagle::WITHIN_API_PATH + ".#{format}", :params => params)
241
+ FireEagle::Response.new(response.body).users
242
+ end
243
+
244
+ protected
245
+
246
+ def sanitize_location_hash(location)
247
+ location.map do |k,v|
248
+ location[k.to_sym] = v
249
+ end
250
+
251
+ location = location.reject { |key, value| !FireEagle::UPDATE_PARAMS.include?(key) }
252
+ raise FireEagle::ArgumentError, "Requires all or none of :lat, :lon" unless location.has_all_or_none_keys?(:lat, :lon)
253
+ raise FireEagle::ArgumentError, "Requires all or none of :mnc, :mcc, :lac, :cellid" unless location.has_all_or_none_keys?(:mnc, :mcc, :lac, :cellid)
254
+ location
255
+ end
256
+
257
+ def get(url, options = {}) #:nodoc:
258
+ request(:get, url, options)
259
+ end
260
+
261
+ def post(url, options = {}) #:nodoc:
262
+ request(:post, url, options)
263
+ end
264
+
265
+ def request(method, url, options) #:nodoc:
266
+ response = case method
267
+ when :post
268
+ access_token.request(:post, url, options[:params])
269
+ when :get
270
+ qs = options[:params].collect { |k,v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}" }.join("&") if options[:params]
271
+ access_token.request(:get, "#{url}?#{qs}")
272
+ else
273
+ raise ArgumentError, "method #{method} not supported"
274
+ end
275
+
276
+ case response.code
277
+ when '500'; then raise FireEagle::FireEagleException, "Internal Server Error"
278
+ when '400'; then raise FireEagle::FireEagleException, "Method Not Implemented Yet"
279
+ else response
280
+ end
281
+ end
282
+ end
283
+ end
@@ -0,0 +1,70 @@
1
+ #Describes a location
2
+ class FireEagle
3
+ class Location
4
+
5
+ #Initialize a Location from an XML response
6
+ def initialize(doc)
7
+ doc = Hpricot(doc) unless doc.is_a?(Hpricot::Doc || Hpricot::Elem)
8
+ @doc = doc
9
+ end
10
+
11
+ def label
12
+ @label ||= @doc.at("/location/label").innerText rescue nil
13
+ end
14
+
15
+ #Level of granularity for this Location
16
+ def level
17
+ @level ||= @doc.at("/location/level").innerText.to_i rescue nil
18
+ end
19
+
20
+ #Name of the level of granularity for this Location
21
+ def level_name
22
+ @level_name ||= @doc.at("/location/level-name").innerText rescue nil
23
+ end
24
+
25
+ #Human Name for this Location
26
+ def name
27
+ @name ||= @doc.at("/location/name").innerText rescue nil
28
+ end
29
+ alias_method :to_s, :name
30
+
31
+ #Unique identifier for this place. Pro-tip: This is the same place_id used by Flickr.
32
+ def place_id
33
+ @place_id ||= @doc.at("/location/place-id").innerText rescue nil
34
+ end
35
+
36
+ #Numeric unique identifier for this place.
37
+ def woeid
38
+ @woeid ||= @doc.at("/location/woeid").innerText rescue nil
39
+ end
40
+
41
+ #The Time at which the User last updated in this Location
42
+ def located_at
43
+ @located_at ||= Time.parse(@doc.at("/location/located-at").innerText) rescue nil
44
+ end
45
+
46
+ #The coordinates of the lower corner of a bounding box surrounding this Location
47
+ def lower_corner
48
+ @georss ||= @doc.at("/location//georss:box").innerText.split.map{ |l| l.to_f } rescue nil
49
+ @lower_corner ||= @georss[0..1] rescue nil
50
+ end
51
+
52
+ # The GeoRuby[http://georuby.rubyforge.org/] representation of this location
53
+ def geom
54
+ if @doc.at("/location//georss:box")
55
+ @geo ||= GeoRuby::SimpleFeatures::Geometry.from_georss(@doc.at("/location//georss:box").to_s)
56
+ elsif @doc.at("/location//georss:point")
57
+ @geo ||= GeoRuby::SimpleFeatures::Geometry.from_georss(@doc.at("/location//georss:point").to_s)
58
+ else
59
+ return nil
60
+ end
61
+ end
62
+ alias_method :geo, :geom
63
+
64
+ #Is this Location FireEagle's best guess?
65
+ def best_guess?
66
+ @best_guess ||= @doc.at("/location").attributes["best-guess"] == "true" rescue false
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,31 @@
1
+ class FireEagle
2
+ class Response
3
+
4
+ #Parses the XML response from FireEagle
5
+ def initialize(doc)
6
+ doc = Hpricot(doc) unless doc.is_a?(Hpricot::Doc || Hpricot::Elem)
7
+ @doc = doc
8
+ raise FireEagle::FireEagleException, @doc.at("/rsp/err").attributes["msg"] if !success?
9
+ end
10
+
11
+ #does the response indicate success?
12
+ def success?
13
+ @doc.at("/rsp").attributes["stat"] == "ok" rescue false
14
+ end
15
+
16
+ #An array of of User-specific tokens and their Location at all levels the Client can see and larger.
17
+ def users
18
+ @users ||= @doc.search("/rsp//user").map do |user|
19
+ FireEagle::User.new(user.to_s)
20
+ end
21
+ end
22
+
23
+ #A Location array.
24
+ def locations
25
+ @locations ||= @doc.search("/rsp/locations/location").map do |location|
26
+ FireEagle::Location.new(location.to_s)
27
+ end
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,33 @@
1
+ class FireEagle
2
+ class User
3
+
4
+ #Parses the XML response from FireEagle.
5
+ def initialize(doc)
6
+ doc = Hpricot(doc) unless doc.is_a?(Hpricot::Doc || Hpricot::Elem)
7
+ @doc = doc
8
+ end
9
+
10
+ #The User-specific token for this Client.
11
+ def token
12
+ @token ||= @doc.at("/user").attributes["token"] rescue nil
13
+ end
14
+
15
+ #FireEagle's "best guess" form this User's Location. This best guess is derived as the most accurate
16
+ #level of the hierarchy with a timestamp in the last half an hour <b>or</b> as the most accurate
17
+ #level of the hierarchy with the most recent timestamp.
18
+ def best_guess
19
+ @best_guess ||= locations.select { |location| location.best_guess? }.first rescue nil
20
+ end
21
+
22
+ #An Array containing all Location granularity that the Client has been allowed to
23
+ #see for the User. The Location elements returned are arranged in hierarchy order consisting of:
24
+ #Country, State, County, Large Cities, Neighbourhoods/Local Area, Postal Code and exact location.
25
+ #The Application should therefore be prepared to receive a response that may consist of (1) only
26
+ #country, or (2) country & state or (3) country, state & county and so forth.
27
+ def locations
28
+ @locations ||= @doc.search("/user/location-hierarchy/location").map do |location|
29
+ FireEagle::Location.new(location.to_s)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,10 @@
1
+ class FireEagle #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 7
5
+ TINY = 1
6
+ TEENY = 0
7
+
8
+ STRING = [MAJOR, MINOR, TINY, TEENY].join('.')
9
+ end
10
+ end