fireeagle 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
File without changes
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007 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.
@@ -0,0 +1,19 @@
1
+ History.txt
2
+ License.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ lib/fireeagle.rb
7
+ lib/fireeagle/application.rb
8
+ lib/fireeagle/apibase.rb
9
+ lib/fireeagle/base.rb
10
+ lib/fireeagle/location.rb
11
+ lib/fireeagle/user.rb
12
+ lib/fireeagle/version.rb
13
+ scripts/txt2html
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
@@ -0,0 +1,20 @@
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.
2
+
3
+ == Installation
4
+
5
+ gem install fireeagle
6
+
7
+ == Usage
8
+
9
+ require 'fireeagle'
10
+ f = FireEagle.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"}>
16
+
17
+ Rubyforge Project Page:: http://rubyforge.org/projects/fireeagle
18
+ 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
@@ -0,0 +1,123 @@
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 = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
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
+
@@ -0,0 +1,65 @@
1
+ %w[ cgi digest/sha1 net/http hpricot time ].each { |f| require f }
2
+
3
+ class FireEagle#:nodoc:
4
+ API_DOMAIN = "fireeagle.research.yahoo.com"
5
+ API_PATH = "/api/"
6
+ DEBUG = false
7
+
8
+ class Error < RuntimeError #:nodoc:
9
+ end
10
+
11
+ class ArgumentError < Error #:nodoc:
12
+ end
13
+
14
+ class FireEagleException < Error #:nodoc:
15
+ end
16
+ end
17
+
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
+ #FireEagle additions to the <code>Hash</code> class
26
+ class Hash
27
+ #Returns <code>true</code> if the ALL or NONE of the given keys are present in <i>hsh</i>.
28
+ def has_all_or_none_keys?(*my_keys)
29
+ size = my_keys.length
30
+ false_count = 0
31
+ my_keys.each do |k|
32
+ false_count += 1 unless keys.include?(k)
33
+ end
34
+ false_count == 0 or false_count == size
35
+ end
36
+
37
+ end
38
+
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
@@ -0,0 +1,6 @@
1
+ class FireEagle::APIBase < FireEagle::Base #:nodoc:
2
+ def initialize(fireeagle)
3
+ super(:token => fireeagle.token, :secret => fireeagle.secret)
4
+ @fireeagle = fireeagle
5
+ end
6
+ end
@@ -0,0 +1,29 @@
1
+ class FireEagle::Application < FireEagle::APIBase
2
+ #A user must authorize (or grant permissions to) an application before the application can write or read the user's location. This authorization process begins by directing a user to the authorization page, where the user logs in and chooses which permissions to grant to the requesting application. The authorization page generates a user token, specific to the requesting application, which can be returned to the requesting application in several ways:
3
+ #
4
+ #1. User token passed back to requesting application via callback URL (if the callback parameter is supplied)
5
+ #2. User token displayed onscreen (this is good for testing, but will likely be phased out or modified in the future)
6
+ #3. User token is exchanged at a later time (good for mobile devices where a user may not want to enter their Yahoo! username/password)
7
+ #
8
+ def authorize_url(callback_url = "")
9
+ "http://#{FireEagle::API_DOMAIN}/authorize.php?appid=#{@fireeagle.token}&callback=#{CGI::escape(callback_url)}"
10
+ end
11
+
12
+
13
+ #Applications which do not maintain a user repository (for example, a lightweight mobile application) may not wish to use the callback mechanism described above. These applications still must send users to authorize.php, but do not expect an immediate response (though at the moment, the authorize page will display the user token for easy testing and debugging). After the user has authorized an application, the application can later retrieve the user token in exchange for a short code obtained by the user from the Fire Eagle site. The user would retrieve a short code from the FE site and enter the code into the application which would then use the exchangeToken.php to exchange the short code for a full user token.
14
+ #
15
+ #To obtain a short code users visit http://fireeagle.research.yahoo.com/displayToken.php?appid=TOKEN, where app token is the application token.
16
+ #
17
+ #Returnes an instance of FireEagle::User
18
+ def exchange_token(shortcode = nil)
19
+ raise FireEagle::ArgumentError, "Shortcode required" if shortcode.nil?
20
+ request("exchangeToken", :shortcode => shortcode.to_s)
21
+ end
22
+
23
+ private
24
+
25
+ def parse_response(doc)
26
+ raise FireEagle::FireEagleException, doc.at("/resultset/errormessage").innerText unless doc.at("/resultset").nil?
27
+ FireEagle::User.new_from_xml(@fireeagle, doc)
28
+ end
29
+ end
@@ -0,0 +1,89 @@
1
+ #The base class for interfacing w/ FireEagle
2
+ #
3
+ # @fireeagle = Fireagle::Base.new(:token => "foo", :secret => "bar")
4
+ #
5
+ #Fire Eagle (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.
6
+ #
7
+ #There are 3 data entities at Fire Eagle's core:
8
+ #
9
+ #* User: Users can grant 3rd party applications privileges to read or write the user's location data. Users identify themselves to the FE system with their Yahoo! username/password. Applications identify a user with an application-specific user token (referred to in the API as a userid) generated by FE.
10
+ #* Application: An application is identified by system-wide application token. Applications use this token (referred to in the API as an appid) and an application-specific secret to identify themselves to FE when reading or writing user location data.
11
+ #* Location: FE stores the most recent location supplied by each application for each user. Applications can specify location in a number of ways: e.g. a latitude/longitude pair, a postal code, a street address, a GSM cell tower identifier, or an identifier from an external system (Plazes, Upcoming.org, etc.). See the Update API section for all possible location input options.
12
+ #
13
+ #Only one location per user-application pair is stored, thus FE provides access only to a user's current location, not a location history of where the user has been. When a user's location is requested FE combines location info from one or more applications into a current best guess location record which is returned in XML format -- see the Query API Response section, below for details.
14
+ class FireEagle::Base
15
+
16
+ attr_reader :token, :secret
17
+
18
+ #Create a new FireEagle::Base object.
19
+ #
20
+ # @fireeagle = Fireagle::Base.new(:token => "foo", :secret => "bar")
21
+ def initialize(options = {})
22
+ options = { :token => nil, :secret => nil }.merge(options)
23
+ raise FireEagle::ArgumentError, "Token and Secret required" if options[:token].nil? || options[:secret].nil?
24
+ @token = options[:token]
25
+ @secret = options[:secret]
26
+ @fireeagle = self
27
+ end
28
+
29
+ # Return an instance of FireEagle::Application
30
+ def application() @application ||= FireEagle::Application.new(self) end
31
+
32
+ private
33
+
34
+ # do the required api signing
35
+ def encode_and_sign(params = {})
36
+
37
+ #check for the params
38
+ raise ArgumentError, "Params required" if params == {} || !params.is_a?(Hash)
39
+
40
+ #remove any provided sig
41
+ params.delete(:sig)
42
+
43
+ #add the app token id
44
+ params[:appid] = @fireeagle.token
45
+
46
+ #sort and URL encode the params
47
+ normalized_params = {}
48
+ params.each_pair { |name, value| normalized_params[name.to_s] = value.to_s }
49
+
50
+ #build the string to sign
51
+ sig = "#{@fireeagle.secret}"
52
+ normalized_params.sort.each { |name, value| sig << "#{name}#{value}" }
53
+
54
+ #sign it
55
+ normalized_params['sig'] = Digest::SHA1.hexdigest(sig)
56
+
57
+ #create and return the request string
58
+ request_string = "?" + normalized_params.sort.collect { |name, value| "#{name}=#{CGI::escape(value)}" }.join('&')
59
+ end
60
+
61
+ # request method which is used by all public methods
62
+ def request(action = nil,params = {})
63
+ raise ArgumentError, "Action name required" if action.nil?
64
+
65
+ #reject crap
66
+ params.reject { |key, value| value.nil? or (value.is_a?(String) and value.empty?)}
67
+ #merge timestamp
68
+ params.merge!( { :timestamp => Time.now.to_i } )
69
+
70
+ path = "#{FireEagle::API_PATH}#{action}.php#{encode_and_sign(params)}"
71
+
72
+ puts path if FireEagle::DEBUG
73
+
74
+ response = Net::HTTP.get_response(FireEagle::API_DOMAIN, path)
75
+
76
+ parse_response(Hpricot(response.body))
77
+ end
78
+
79
+ def parse_response(doc)
80
+ raise FireEagleException, doc.at("/resultset/errormessage").innerText unless !doc.at("/resultset").nil? and doc.at("/resultset/error").innerText.to_i == "0"
81
+ doc.at("/result")
82
+ end
83
+
84
+ def fireeagle
85
+ fireeagle
86
+ end
87
+
88
+
89
+ end
@@ -0,0 +1,74 @@
1
+ #Describes a location
2
+ class FireEagle::Location
3
+
4
+ DETAILS = :locale, :quality, :updatetime, :latitute, :longitude, :offsetlat, :offsetlon,
5
+ :radius, :boundinglatnorth, :boundinglatsouth, :boundinglateast, :boundinglatwest,
6
+ :name, :line1, :line2, :line3, :line4, :house, :street, :xstreet, :unittype, :unit,
7
+ :neighborhood, :city, :county, :state, :country, :coutrycode, :statecode, :countrycode,
8
+ :timezone,
9
+ # attributes used only on update
10
+ :source, :addr, :mcn, :mcc, :lac, :cellid, :loc, :name, :woeid, :plazesid,
11
+ :upvenueid, :street1, :street2
12
+
13
+ attr_reader :details
14
+
15
+ #Create an instance of FireEagle::Location. There are some specific requirements for combinations of elements in the <code>options</code> Hash:
16
+ #
17
+ #* Requires all or none of :lat, :long
18
+ #* Requires all or none of :mnc, :mcc, :lac, :cellid
19
+ #* Requires all or none of :street1, :street2
20
+ #* Requires :postal or :city with :street1 and :street2
21
+ #* Requires :city or :postal with :addr
22
+ #* Requires :state, :country, or :postal with :city
23
+ #* Requires :country with :postal if not in US
24
+ def initialize(options = {}, verify_attributes = true)
25
+ option = options.reject { |key, value| DETAILS.include?(key) }
26
+
27
+ if verify_attributes
28
+ raise FireEagle::ArgumentError, "Requires all or none of :lat, :long" unless options.has_all_or_none_keys?(:lat, :long)
29
+ raise FireEagle::ArgumentError, "Requires all or none of :mnc, :mcc, :lac, :cellid" unless options.has_all_or_none_keys?(:mnc, :mcc, :lac, :cellid)
30
+ raise FireEagle::ArgumentError, "Requires all or none of :street1, :street2" unless options.has_all_or_none_keys?(:street1, :street2)
31
+ raise FireEagle::ArgumentError, "Requires :postal or :city with :street1 and :street2" if (options.has_key?(:street1) and options.has_key?(:street2)) and !(options.has_key?(:city) or options.has_key?(:postal))
32
+ raise FireEagle::ArgumentError, "Requires :city or :postal with :addr" if options.has_key?(:addr) and !(options.has_key?(:city) or options.has_key?(:postal))
33
+ raise FireEagle::ArgumentError, "Requires :state, :country, or :postal with :city" if options.has_key?(:city) and !(options.has_key?(:state) or options.has_key?(:country) or options.has_key?(:postal))
34
+ raise FireEagle::ArgumentError, "Requires :country with :postal if not in US" if (options.has_key?(:postal) and options.has_key?(:country)) and (options[:country] != "US")
35
+ end
36
+
37
+ @details = options
38
+ end
39
+
40
+ class << self
41
+ #Create a new instance of FireEagle::Location from the XML returned from Yahoo!
42
+ def new_from_xml(doc)
43
+ #build the massive hash needed for a location
44
+ options = { }
45
+ doc = doc.at("/resultset")
46
+ options[:locale] = doc.at("locale").innerText unless doc.at("locale").nil?
47
+ # quick parse of the xml
48
+ puts doc if FireEagle::DEBUG
49
+ doc.at("result").containers.each do |attribute|
50
+ options[attribute.xpath.split("/")[-1].to_sym] = attribute.inner_text unless attribute.nil?
51
+ end
52
+ #fixup types
53
+ options[:updatetime] = Time.parse(options[:updatetime])
54
+ options[:radius] = options[:radius].to_i
55
+ options[:quality] = options[:radius].to_i
56
+ %w(latitude longitude offsetlat offsetlon :boundinglatnorth boundinglatsouth boundinglateast boundinglatwest).each do |attribute|
57
+ options[attribute.to_sym].nil? || options[attribute.to_sym].empty? ? (options[attribute.to_sym] = '') : (options[attribute.to_sym] = options[attribute.to_sym].to_f)
58
+ end
59
+
60
+ FireEagle::Location.new(options, false)
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ def verify_attribute_combination(options, *args)
67
+ args.each do |a|
68
+ return false unless options.include?(a)
69
+ end
70
+ return true
71
+ end
72
+
73
+
74
+ end