fireeagle 0.0.2 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,7 @@
1
+ == 0.6.0
2
+
3
+ * Complete rewrite
4
+
1
5
  == 0.0.2
2
6
 
3
7
  * Fix callback_url
@@ -1,4 +1,4 @@
1
- Copyright (c) 2007 Jesse Newland
1
+ Copyright (c) 2008 Jesse Newland
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -1,19 +1,21 @@
1
+ config/hoe.rb
2
+ config/requirements.rb
1
3
  History.txt
2
4
  License.txt
3
5
  Manifest.txt
4
6
  README.txt
5
7
  Rakefile
6
8
  lib/fireeagle.rb
7
- lib/fireeagle/application.rb
8
- lib/fireeagle/apibase.rb
9
- lib/fireeagle/base.rb
9
+ lib/fireeagle/client.rb
10
10
  lib/fireeagle/location.rb
11
+ lib/fireeagle/response.rb
11
12
  lib/fireeagle/user.rb
12
13
  lib/fireeagle/version.rb
13
- scripts/txt2html
14
14
  setup.rb
15
- test/test_fireeagle.rb
16
- test/test_fireeagle_application.rb
17
- test/test_fireeagle_location.rb
18
- test/test_fireeagle_user.rb
19
- test/test_helper.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 CHANGED
@@ -1,4 +1,8 @@
1
- FireEagle[http://fireeagle.research.yahoo.com/] (FE) is a system providing centralized management of user location information. FE allows 3rd party developers to update and/or access user's location data.
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
2
6
 
3
7
  == Installation
4
8
 
@@ -6,15 +10,39 @@ FireEagle[http://fireeagle.research.yahoo.com/] (FE) is a system providing centr
6
10
 
7
11
  == Usage
8
12
 
9
- require 'fireeagle'
10
- f = FireEagle::Base.new(:token => "foo", :secret => "bar")
11
- user = FireEagle::User.new(f,"token")
12
- l = FireEagle::Location.new(:country => "Belize")
13
- user.location = l
14
- user.location
15
- => #<FireEagle::Location:0x15ba0d0 @details={:country=>"Belize"}>
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
16
44
 
17
45
  Rubyforge Project Page:: http://rubyforge.org/projects/fireeagle
18
46
  Author:: Jesse Newland (http://soylentfoo.jnewland.com) (jnewland@gmail.com[mailto:jnewland@gmail.com])
19
- Copyright:: Copyright (c) 2007 Jesse Newland
20
- License:: Distributes under the same terms as Ruby
47
+ Copyright:: Copyright (c) 2008 Jesse Newland. Portions[http://pastie.caboo.se/private/oevvkdzl0zrdkf8s7hetg] Copyright (c) 2008 Yahoo!
48
+ License:: Distributed under the same terms as Ruby
data/Rakefile CHANGED
@@ -1,123 +1,4 @@
1
- require 'rubygems'
2
- require 'rake'
3
- require 'rake/clean'
4
- require 'rake/testtask'
5
- require 'rake/packagetask'
6
- require 'rake/gempackagetask'
7
- require 'rake/rdoctask'
8
- require 'rake/contrib/rubyforgepublisher'
9
- require 'fileutils'
10
- require 'hoe'
11
-
12
- include FileUtils
13
- require File.join(File.dirname(__FILE__), 'lib', 'fireeagle', 'version')
14
-
15
- AUTHOR = 'Jesse Newland' # can also be an array of Authors
16
- EMAIL = "jnewland@gmail.com"
17
- DESCRIPTION = "Fire Eagle is a site that keeps track of your current location and helps you share it with other sites and services safely. There are hundreds of potential applications. This gem exposes the FireEagle API as Ruby Classes"
18
- GEM_NAME = 'fireeagle' # what ppl will type to install your gem
19
-
20
- @config_file = "~/.rubyforge/user-config.yml"
21
- @config = nil
22
- def rubyforge_username
23
- unless @config
24
- begin
25
- @config = YAML.load(File.read(File.expand_path(@config_file)))
26
- rescue
27
- puts <<-EOS
28
- ERROR: No rubyforge config file found: #{@config_file}"
29
- Run 'rubyforge setup' to prepare your env for access to Rubyforge
30
- - See http://newgem.rubyforge.org/rubyforge.html for more details
31
- EOS
32
- exit
33
- end
34
- end
35
- @rubyforge_username ||= @config["username"]
36
- end
37
-
38
- RUBYFORGE_PROJECT = 'fireeagle' # The unix name for your project
39
- HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
40
- DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
41
-
42
- NAME = "fireeagle"
43
- REV = nil
44
- # UNCOMMENT IF REQUIRED:
45
- # REV = `svn info`.each {|line| if line =~ /^Revision:/ then k,v = line.split(': '); break v.chomp; else next; end} rescue nil
46
- VERS = FireEagle::VERSION::STRING + (REV ? ".#{REV}" : "")
47
- CLEAN.include ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store']
48
- RDOC_OPTS = ['--quiet', '--title', 'fireeagle documentation',
49
- "--opname", "index.html",
50
- "--line-numbers",
51
- "--main", "README",
52
- "--inline-source"]
53
-
54
- class Hoe
55
- def extra_deps
56
- @extra_deps.reject { |x| Array(x).first == 'hoe' }
57
- end
58
- end
59
-
60
- # Generate all the Rake tasks
61
- # Run 'rake -T' to see list of generated tasks (from gem root directory)
62
- hoe = Hoe.new(GEM_NAME, VERS) do |p|
63
- p.author = AUTHOR
64
- p.description = DESCRIPTION
65
- p.email = EMAIL
66
- p.summary = DESCRIPTION
67
- p.url = HOMEPATH
68
- p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
69
- p.test_globs = ["test/**/test_*.rb"]
70
- p.clean_globs |= CLEAN #An array of file patterns to delete on clean.
71
-
72
- # == Optional
73
- p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
74
- p.extra_deps = [['hpricot', '>= 0.5.145'] ] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
75
- #p.spec_extras = {} # A hash of extra values to set in the gemspec.
76
- end
77
-
78
- CHANGES = hoe.paragraphs_of('History.txt', 0..1).join("\n\n")
79
- PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
80
- hoe.remote_rdoc_dir = ''
81
-
82
- desc 'Generate website files'
83
- task :website_generate do
84
- Dir['website/**/*.txt'].each do |txt|
85
- sh %{ ruby scripts/txt2html #{txt} > #{txt.gsub(/txt$/,'html')} }
86
- end
87
- end
88
-
89
- desc 'Upload website files to rubyforge'
90
- task :website_upload do
91
- host = "#{rubyforge_username}@rubyforge.org"
92
- remote_dir = "/var/www/gforge-projects/#{PATH}/"
93
- local_dir = 'website'
94
- sh %{rsync -aCv #{local_dir}/ #{host}:#{remote_dir}}
95
- end
96
-
97
- desc 'Generate and upload website files'
98
- task :website => [:website_generate, :website_upload, :publish_docs]
99
-
100
- desc 'Release the website and new gem version'
101
- task :deploy => [:check_version, :website, :release] do
102
- puts "Remember to create SVN tag:"
103
- puts "svn copy svn+ssh://#{rubyforge_username}@rubyforge.org/var/svn/#{PATH}/trunk " +
104
- "svn+ssh://#{rubyforge_username}@rubyforge.org/var/svn/#{PATH}/tags/REL-#{VERS} "
105
- puts "Suggested comment:"
106
- puts "Tagging release #{CHANGES}"
107
- end
108
-
109
- desc 'Runs tasks website_generate and install_gem as a local deployment of the gem'
110
- task :local_deploy => [:website_generate, :install_gem]
111
-
112
- task :check_version do
113
- unless ENV['VERSION']
114
- puts 'Must pass a VERSION=x.y.z release version'
115
- exit
116
- end
117
- unless ENV['VERSION'] == VERS
118
- puts "Please update your version.rb to match the release version, currently #{VERS}"
119
- exit
120
- end
121
- end
122
-
123
-
1
+ require 'config/requirements'
2
+ require 'config/hoe' # setup Hoe + all gem configuration
3
+
4
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
@@ -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'], ['json', '>= 1.1.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,17 @@
1
+ require 'fileutils'
2
+ include FileUtils
3
+
4
+ require 'rubygems'
5
+ %w[rake hoe newgem rubigen].each do |req_gem|
6
+ begin
7
+ require req_gem
8
+ rescue LoadError
9
+ puts "This Rakefile requires the '#{req_gem}' RubyGem."
10
+ puts "Installation: gem install #{req_gem} -y"
11
+ exit
12
+ end
13
+ end
14
+
15
+ $:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
16
+
17
+ require 'fireeagle'
@@ -1,27 +1,41 @@
1
- %w[ cgi digest/sha1 net/http hpricot time ].each { |f| require f }
1
+ require 'time'
2
+ require 'net/https'
3
+ require 'rubygems'
4
+ gem 'oauth', ">= 0.2.1"
5
+ require 'oauth/client/helper'
6
+ require 'oauth/request_proxy/net_http'
7
+ require 'json'
8
+ require 'hpricot'
9
+ require 'geo_ruby'
10
+
11
+ class FireEagle
12
+ API_SERVER = "https://fireeagle.yahooapis.com"
13
+ AUTH_SERVER = "https://fireeagle.yahoo.net"
14
+ REQUEST_TOKEN_PATH = "/oauth/request_token"
15
+ ACCESS_TOKEN_PATH = "/oauth/access_token"
16
+ AUTHORIZATION_URL = "#{AUTH_SERVER}/oauth/authorize"
17
+ MOBILE_AUTH_URL = "#{AUTH_SERVER}/oauth/mobile_auth/"
18
+ USER_API_PATH = "/api/0.1/user"
19
+ LOOKUP_API_PATH = "/api/0.1/lookup"
20
+ UPDATE_API_PATH = "/api/0.1/update"
21
+ RECENT_API_PATH = "/api/0.1/recent"
22
+ WITHIN_API_PATH = "/api/0.1/within"
23
+ FORMAT_JSON = "json"
24
+ FORMAT_XML = "xml"
25
+ UPDATE_PARAMS = :lat, :lon, :woeid, :place_id, :address, :mnc, :mcc, :lac, :cid, :postal, :city, :state, :country, :q, :label
26
+ # not yet supported
27
+ #,:geom, :upcoming_venue_id, :yahoo_local_id, :plazes_id
2
28
 
3
- class FireEagle#:nodoc:
4
- API_DOMAIN = "fireeagle.research.yahoo.com"
5
- API_PATH = "/api/"
6
- DEBUG = false
7
-
8
29
  class Error < RuntimeError #:nodoc:
9
30
  end
10
31
 
11
32
  class ArgumentError < Error #:nodoc:
12
33
  end
13
-
34
+
14
35
  class FireEagleException < Error #:nodoc:
15
36
  end
16
37
  end
17
38
 
18
- require 'fireeagle/base'
19
- require 'fireeagle/apibase'
20
- require 'fireeagle/application'
21
- require 'fireeagle/user'
22
- require 'fireeagle/location'
23
- require 'fireeagle/version'
24
-
25
39
  #FireEagle additions to the <code>Hash</code> class
26
40
  class Hash
27
41
  #Returns <code>true</code> if the ALL or NONE of the given keys are present in <i>hsh</i>.
@@ -33,33 +47,9 @@ class Hash
33
47
  end
34
48
  false_count == 0 or false_count == size
35
49
  end
36
-
37
50
  end
38
51
 
39
- #FireEagle additions to the <code>Object</code> class
40
- class Object
41
- # A Ruby-ized realization of the K combinator, courtesy of Mikael Brockman.
42
- #
43
- # def foo
44
- # returning values = [] do
45
- # values << 'bar'
46
- # values << 'baz'
47
- # end
48
- # end
49
- #
50
- # foo # => ['bar', 'baz']
51
- #
52
- # def foo
53
- # returning [] do |values|
54
- # values << 'bar'
55
- # values << 'baz'
56
- # end
57
- # end
58
- #
59
- # foo # => ['bar', 'baz']
60
- #
61
- def returning(value)
62
- yield(value)
63
- value
64
- end
65
- end
52
+ require File.dirname(__FILE__) + '/fireeagle/client'
53
+ require File.dirname(__FILE__) + '/fireeagle/location'
54
+ require File.dirname(__FILE__) + '/fireeagle/user'
55
+ require File.dirname(__FILE__) + '/fireeagle/response'
@@ -0,0 +1,343 @@
1
+ class FireEagle
2
+ class Client
3
+ attr_reader :access_token, :request_token, :consumer, :format
4
+
5
+ # Initialize a FireEagle Client. Takes an options Hash.
6
+ #
7
+ # == Required keys:
8
+ #
9
+ # [<tt>:consumer_key</tt>] OAuth Consumer key representing your FireEagle Application
10
+ # [<tt>:consumer_secret</tt>] OAuth Consumer secret representing your FireEagle Application
11
+ #
12
+ # == Optional keys:
13
+ #
14
+ # [<tt>:request_token</tt>] OAuth Request Token, for use with convert_to_access_token
15
+ # [<tt>:request_token_secret</tt>] OAuth Request Token Secret, for use with convert_to_access_token
16
+ # [<tt>:access_token</tt>] OAuth Token, either User-specific or General-purpose
17
+ # [<tt>:access_token_secret</tt>] OAuth Token, either User-specific or General-purpose
18
+ # [<tt>:app_id</tt>] Your Mobile Application ID
19
+ # [<tt>:debug</tt>] Boolean
20
+ #
21
+ # User-specific OAuth tokens tie FireEagle users to your application. As such, they are intended to be
22
+ # distributed (with keys) to that user's mobile device and/or computer running your desktop or mobile client.
23
+ # For web-based applications User-specific tokens will be retrieved by your web server where they should be
24
+ # treated as private data. Take care to avoid releasing this data to the public, as the corresponding User's location
25
+ # information may be inadvertently exposed. User-specific OAuth tokens should be considered the property of
26
+ # your users.
27
+ #
28
+ # General-purpose OAuth tokens are tied to your application and allow you, as a developer, to make more
29
+ # general (often batch-style) queries against FireEagle. As a result, allowing this token/secret combination
30
+ # loose has the potential to reveal a much greater amount of personal data. In an attempt to mitigate this, we will
31
+ # only grant general-purpose tokens to web applications (contact us with details, if you seek an exception). In
32
+ # addition, we require developers to provide a restrictive IP range at registration time in order to further mitigate
33
+ # the risk of general-purpose tokens being used inappropriately.
34
+ #
35
+ # In general, OAuth tokens should be considered sacrosanct in order to help us respect our users' privacy. Please
36
+ # take this responsibility on as your own. If your Application Oauth tokens are compromised, FireEagle will
37
+ # turn off your application service until the problem is resolved.
38
+ #
39
+ # If the Client is initialized without an OAuth access token, it's assumed you're operating a non-web based application.
40
+ #
41
+ # == Non web-based applications
42
+ #
43
+ # For non web-based applications, such as a mobile client application, the authentication between the user and
44
+ # the application is slightly different. The request token is displayed to the user by the client application. The
45
+ # user then logs into the FireEagle website (using mobile_authorization_url) and enters this code to authorize the application.
46
+ # When the user finishes the authorization step the client application exchanges the request token for an access token
47
+ # (using convert_to_access_token). This is a lightweight method for non-web application users to authenticate an application
48
+ # without entering any identifying information into a potentially insecure application. Request tokens are valid for only
49
+ # 1 hour after being issued.
50
+ #
51
+ # == Example mobile-based authentication flow:
52
+ #
53
+ # Initialize a client with your consumer key, consumer secret, and your mobile application id:
54
+ #
55
+ # >> c = FireEagle::Client.new(:consumer_key => "key", :consumer_secret => "sekret", :app_id => 12345)
56
+ # => #<FireEagle::Client:0x1ce2e70 ... >
57
+ #
58
+ # Generate a request token:
59
+ #
60
+ # >> c.get_request_token
61
+ # => #<OAuth::Token:0x1cdb5bc @token="ENTER_THIS_TOKEN", @secret="sekret">
62
+ #
63
+ # Prompt your user to visit your app's mobile authorization url and enter ENTER_THIS_TOKEN:
64
+ #
65
+ # >> c.mobile_authorization_url
66
+ # => "http://fireeagle.yahoo.net/oauth/mobile_auth/12345"
67
+ #
68
+ # Once the user has indicated to you that they've done this, convert their request token to an access token:
69
+ #
70
+ # >> c.convert_to_access_token
71
+ # => #<OAuth::Token:0x1cd3bf0 @token="access_token", @secret="access_token_secret">
72
+ #
73
+ # You're done!
74
+ def initialize(options = {})
75
+ options = {
76
+ :debug => false,
77
+ :format => FireEagle::FORMAT_XML
78
+ }.merge(options)
79
+
80
+ # symbolize keys
81
+ options.map do |k,v|
82
+ options[k.to_sym] = v
83
+ end
84
+ raise FireEagle::ArgumentError, "OAuth Consumer Key and Secret required" if options[:consumer_key].nil? || options[:consumer_secret].nil?
85
+ @consumer = OAuth::Consumer.new(options[:consumer_key], options[:consumer_secret])
86
+ @debug = options[:debug]
87
+ @format = options[:format]
88
+ @app_id = options[:app_id]
89
+ if options[:access_token] && options[:access_token_secret]
90
+ @access_token = OAuth::Token.new(options[:access_token], options[:access_token_secret])
91
+ else
92
+ @access_token = nil
93
+ end
94
+ if options[:request_token] && options[:request_token_secret]
95
+ @request_token = OAuth::Token.new(options[:request_token], options[:request_token_secret])
96
+ else
97
+ @request_token = nil
98
+ end
99
+ end
100
+
101
+ # Obtain an <strong>new</strong> unauthorized OAuth Request token
102
+ def get_request_token(force_token_regeneration = false)
103
+ if force_token_regeneration || @request_token.nil?
104
+ response = get(FireEagle::REQUEST_TOKEN_PATH, :token => nil)
105
+ @request_token = create_token(response)
106
+ end
107
+ @request_token
108
+ end
109
+
110
+ # Return the Fire Eagle authorization URL for your mobile application. At this URL, the User will be prompted for their request_token.
111
+ def mobile_authorization_url
112
+ raise FireEagle::ArgumentError, ":app_id required" if @app_id.nil?
113
+ "#{FireEagle::MOBILE_AUTH_URL}#{@app_id}"
114
+ end
115
+
116
+ # 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.
117
+ def authorization_url
118
+ raise FireEagle::ArgumentError, "call #get_request_token first" if @request_token.nil?
119
+ "#{FireEagle::AUTHORIZATION_URL}?oauth_token=#{@request_token.token}"
120
+ end
121
+
122
+ #Exchange an authorized OAuth Request token for an access token. For use by desktop-based and mobile applications.
123
+ def convert_to_access_token
124
+ raise FireEagle::ArgumentError, "call #get_request_token and have user authorize the token first" if @request_token.nil?
125
+ response = get(FireEagle::ACCESS_TOKEN_PATH, :token => @request_token)
126
+ @access_token = create_token(response)
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, :cid)</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
+
158
+ response = get(FireEagle::LOOKUP_API_PATH + ".#{format}", :params => params)
159
+
160
+ if json?
161
+ JSON.parse(response.body)
162
+ else
163
+ FireEagle::Response.new(response.body).locations
164
+ end
165
+ end
166
+
167
+ # Sets a User's current Location using using a Place ID hash or a set of Location parameters. If the User
168
+ # provides a Location unconfirmed with lookup then FireEagle makes a best guess as to the User's Location.
169
+ #
170
+ # All three Location methods (lookup, update, and within) accept a Location Hash.
171
+ #
172
+ # There is a specific order for looking up locations. For example, if you provide lat, lon, and address,
173
+ # FireEagle will use the the latitude and longitude geo-coordinates and ignore the address.
174
+ #
175
+ # Location Hash keys, in order of priority:
176
+ #
177
+ # [<tt>(:lat, :lon)</tt>] both required, valid values are floats of -180 to 180 for lat and -90 to 90 for lon
178
+ # [<tt>:place_id</tt>] Place ID - valid values decrypts to an integer value
179
+ # [<tt>:address</tt>] street address (may contain a full address, but will be combined with postal, city, state, and country when available)
180
+ # [<tt>(:mnc, :mcc, :lac, :cid)</tt>] cell tower information, all required (as integers) for a valid tower location
181
+ # [<tt>:postal</tt>] a ZIP or postal code (combined with address, city, state, and country when available)
182
+ # [<tt>:city</tt>] city (combined with address, postal, state, and country when available)
183
+ # [<tt>:state</tt>] state (combined with address, postal, city, and country when available)
184
+ # [<tt>:country</tt>] country (combined with address, postal, city, and state when available)
185
+ # [<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.
186
+ #
187
+ # Not yet supported:
188
+ #
189
+ # * <tt>upcoming_venue_id</tt>
190
+ # * <tt>yahoo_local_id</tt>
191
+ # * <tt>plazes_id</tt>
192
+ def update(location = {})
193
+ raise FireEagle::ArgumentError, "OAuth Access Token Required" unless @access_token
194
+
195
+ location = sanitize_location_hash(location)
196
+
197
+ response = post(FireEagle::UPDATE_API_PATH + ".#{format}", :params => location)
198
+
199
+ if json?
200
+ JSON.parse(response.body)
201
+ else
202
+ FireEagle::Response.new(response.body)
203
+ end
204
+ end
205
+
206
+ # Returns the Location of a User.
207
+ def user
208
+ raise FireEagle::ArgumentError, "OAuth Access Token Required" unless @access_token
209
+
210
+ response = get(FireEagle::USER_API_PATH + ".#{format}")
211
+
212
+ if json?
213
+ JSON.parse(response.body)
214
+ else
215
+ FireEagle::Response.new(response.body).users.first
216
+ end
217
+ end
218
+ alias_method :location, :user
219
+
220
+ # <b>NOTE: not implemented yet</b>
221
+ #
222
+ # Query for Users of an Application who have updated their Location recently. Returns a list of
223
+ # Users for the Application with recently updated locations.
224
+ def recent(count = 10, start = 0)
225
+ raise FireEagle::ArgumentError, "OAuth Access Token Required" unless @access_token
226
+
227
+ params = { :count => count, :start => start }
228
+
229
+ response = get(FireEagle::RECENT_API_PATH + ".#{format}", :params => params)
230
+
231
+ if json?
232
+ JSON.parse(response.body)
233
+ else
234
+ FireEagle::Response.new(response.body).users
235
+ end
236
+ end
237
+
238
+ # <b>NOTE: not implemented yet</b>
239
+ # 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.
240
+ #
241
+ # Location Hash keys, in order of priority:
242
+ #
243
+ # [<tt>(:lat, :lon)</tt>] both required, valid values are floats of -180 to 180 for lat and -90 to 90 for lon
244
+ # [<tt>:woeid</tt>] Where on Earth ID
245
+ # [<tt>:place_id</tt>] Place ID
246
+ # [<tt>:address</tt>] street address (may contain a full address, but will be combined with postal, city, state, and country when available)
247
+ # [<tt>(:mnc, :mcc, :lac, :cid)</tt>] cell tower information, all required (as integers) for a valid tower location
248
+ # [<tt>:postal</tt>] a ZIP or postal code (combined with address, city, state, and country when available)
249
+ # [<tt>:city</tt>] city (combined with address, postal, state, and country when available)
250
+ # [<tt>:state</tt>] state (combined with address, postal, city, and country when available)
251
+ # [<tt>:country</tt>] country (combined with address, postal, city, and state when available)
252
+ # [<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.
253
+ #
254
+ # Not yet supported:
255
+ #
256
+ # * <tt>upcoming_venue_id</tt>
257
+ # * <tt>yahoo_local_id</tt>
258
+ # * <tt>plazes_id</tt>
259
+ def within(location = {}, count = 10, start = 0)
260
+ raise FireEagle::ArgumentError, "OAuth Access Token Required" unless @access_token
261
+
262
+ location = sanitize_location_hash(location)
263
+ params = { :count => count, :start => start }.merge(location)
264
+
265
+ response = get(FireEagle::WITHIN_API_PATH + ".#{format}", :params => params)
266
+
267
+ if json?
268
+ JSON.parse(response.body)
269
+ else
270
+ FireEagle::Response.new(response.body).users
271
+ end
272
+ end
273
+
274
+ protected
275
+
276
+ def sanitize_location_hash(location)
277
+ location.map do |k,v|
278
+ location[k.to_sym] = v
279
+ end
280
+
281
+ location = location.reject { |key, value| !FireEagle::UPDATE_PARAMS.include?(key) }
282
+ raise FireEagle::ArgumentError, "Requires all or none of :lat, :lon" unless location.has_all_or_none_keys?(:lat, :lon)
283
+ raise FireEagle::ArgumentError, "Requires all or none of :mnc, :mcc, :lac, :cellid" unless location.has_all_or_none_keys?(:mnc, :mcc, :lac, :cid)
284
+ location
285
+ end
286
+
287
+ def xml? #:nodoc:
288
+ format == FireEagle::FORMAT_XML
289
+ end
290
+
291
+ def json? #:nodoc:
292
+ format == FireEagle::FORMAT_JSON
293
+ end
294
+
295
+ def create_token(response) #:nodoc:
296
+ token = Hash[*response.body.split("&").map { |x| x.split("=") }.flatten]
297
+ OAuth::Token.new(token["oauth_token"], token["oauth_token_secret"])
298
+ end
299
+
300
+ # Is the Client in debug mode?
301
+ def debug?
302
+ @debug == true
303
+ end
304
+
305
+ def get(url, options = {}) #:nodoc:
306
+ request(:get, url, options)
307
+ end
308
+
309
+ def post(url, options = {}) #:nodoc:
310
+ request(:post, url, options)
311
+ end
312
+
313
+ def request(method, url, options) #:nodoc:
314
+ options = {
315
+ :params => {},
316
+ :token => @access_token
317
+ }.merge(options)
318
+
319
+ request_uri = URI.parse(FireEagle::API_SERVER + url)
320
+ http = Net::HTTP.new(request_uri.host, request_uri.port)
321
+ http.set_debug_output $stderr if debug?
322
+ if FireEagle::API_SERVER =~ /https:/
323
+ http.use_ssl = true
324
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
325
+ end
326
+
327
+ request = nil
328
+ if method == :post
329
+ request = Net::HTTP::Post.new(request_uri.path)
330
+ request.set_form_data(options[:params])
331
+ elsif method == :get
332
+ qs = options[:params].collect { |k,v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}" }.join("&")
333
+ request = Net::HTTP::Get.new(request_uri.path + "?" + qs)
334
+ end
335
+ request.oauth!(http, consumer, options[:token])
336
+ response = http.request(request)
337
+ raise FireEagle::FireEagleException, "Internal Server Error" if response.code == '500'
338
+ raise FireEagle::FireEagleException, "Method Not Implemented Yet" if response.code == '400'
339
+ response
340
+ end
341
+
342
+ end
343
+ end