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,74 +1,70 @@
1
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")
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
35
34
  end
36
35
 
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)
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
61
44
  end
62
- end
63
45
 
64
- private
65
-
66
- def verify_attribute_combination(options, *args)
67
- args.each do |a|
68
- return false unless options.include?(a)
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
69
50
  end
70
- return true
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
+
71
69
  end
72
-
73
-
74
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
@@ -1,39 +1,33 @@
1
- class FireEagle::User < FireEagle::APIBase
2
- attr_reader :token
3
-
4
- #Create a new instance of FireEagle::User. Requires a FireEagle::Base object, and the users app-specific token
5
- def initialize(fireeagle, token)
6
- super(fireeagle)
7
- @fireeagle = fireeagle
8
- @token = token.to_s
9
- end
10
-
11
- class << self
12
- #Create a new instance of FireEagle::User from the XML returned from Yahoo!
13
- def new_from_xml(fireeagle, doc)
14
- FireEagle::User.new(fireeagle, doc.at("/rsp/token").innerText)
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
15
8
  end
16
- end
17
-
18
- #Returns a FireEagle::Location instance describing the User's current location
19
- def location
20
- request("queryLoc", :userid => self.token)
21
- end
22
-
23
- #Sets the User's location to that described in an FireEagle::Location object
24
- def location=(location)
25
- raise FireEagle::ArgumentError, "FireEagle::Location required" unless location.is_a?(FireEagle::Location)
26
- returning(location) do
27
- request("updateLoc", location.details.merge(:userid => self.token))
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
28
31
  end
29
32
  end
30
-
31
- private
32
-
33
- def parse_response(doc)
34
- raise FireEagle::FireEagleException, doc.at("/resultset/errormessage").innerText if doc.at("/resultset/error").innerText.to_i != 0
35
- return if doc.at("/resultset/result").nil?
36
- FireEagle::Location.new_from_xml(doc)
37
- end
38
-
39
33
  end
@@ -1,8 +1,8 @@
1
1
  class FireEagle #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
- MINOR = 0
5
- TINY = 2
4
+ MINOR = 6
5
+ TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -0,0 +1,60 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe "FireEagle Location" do
4
+
5
+ before(:each) do
6
+ location = Hpricot.XML(XML_LOCATION_CHUNK)
7
+ @location = FireEagle::Location.new(location)
8
+ end
9
+
10
+ it "should know if this is a best guess" do
11
+ @location.best_guess?.should == false
12
+ end
13
+
14
+ it "should represent the level" do
15
+ @location.level.should == 3
16
+ end
17
+
18
+ it "should represent the level name" do
19
+ @location.level_name.should == 'city'
20
+ end
21
+
22
+ it "should represent the location name" do
23
+ @location.name.should == 'Davis, CA'
24
+ end
25
+
26
+ it "should represent the location place id" do
27
+ @location.place_id.should == 'u4L9ZOObApTdx1q3'
28
+ end
29
+
30
+ it "should represent the location's timestamp" do
31
+ @location.located_at.should == Time.parse("2008-01-22T14:23:11-08:00")
32
+ end
33
+
34
+ it "should use the name for #to_s" do
35
+ @location.to_s.should == @location.name
36
+ end
37
+
38
+ describe "GeoRuby support" do
39
+
40
+ it "should represent a bounding box as a GeoRuby Envelope" do
41
+ location = Hpricot.XML(XML_LOCATION_CHUNK)
42
+ @location = FireEagle::Location.new(location)
43
+ @location.geom.class.should == GeoRuby::SimpleFeatures::Envelope
44
+ end
45
+
46
+ it "should represent an exact point as a GeoRuby Point" do
47
+ location = Hpricot.XML(XML_EXACT_LOCATION_CHUNK)
48
+ @location = FireEagle::Location.new(location)
49
+ @location.geom.class.should == GeoRuby::SimpleFeatures::Point
50
+ end
51
+
52
+ it "should be aliased as 'geo'" do
53
+ location = Hpricot.XML(XML_EXACT_LOCATION_CHUNK)
54
+ @location = FireEagle::Location.new(location)
55
+ @location.geo.class.should == GeoRuby::SimpleFeatures::Point
56
+ end
57
+
58
+ end
59
+
60
+ end
@@ -0,0 +1,65 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe "FireEagle Response" do
4
+
5
+ describe "user / location parsing" do
6
+
7
+ before(:each) do
8
+ @response = FireEagle::Response.new(XML_LOCATION_RESPONSE)
9
+ end
10
+
11
+ it "should indicate success" do
12
+ @response.success?.should == true
13
+ end
14
+
15
+ it "should have an array of users" do
16
+ @response.users.size.should == 1
17
+ end
18
+
19
+ it "should have each users' token" do
20
+ @response.users.first.token.should == "16w3z6ysudxt"
21
+ end
22
+
23
+ it "should flag the best guess" do
24
+ @response.users.first.best_guess.name.should == "Yolo County, California"
25
+ end
26
+
27
+ it "should have users' loactions" do
28
+ @response.users.first.locations.size.should == 4
29
+ end
30
+
31
+ end
32
+
33
+ describe "location parsing" do
34
+
35
+ before(:each) do
36
+ @response = FireEagle::Response.new(XML_LOOKUP_RESPONSE)
37
+ end
38
+
39
+ it "should indicate success" do
40
+ @response.success?.should == true
41
+ end
42
+
43
+ it "should have an array of locations" do
44
+ @response.locations.size.should == 9
45
+ end
46
+
47
+ it "should have each location's place_id" do
48
+ @response.locations.first.place_id.should == "IrhZMHuYA5s1fFi4Qw"
49
+ end
50
+
51
+ it "should have each location's name" do
52
+ @response.locations.first.name.should == "Alpharetta, GA 30022"
53
+ end
54
+
55
+ end
56
+
57
+ describe "error handling" do
58
+
59
+ it "should raise an exception when returned xml with a status of fail" do
60
+ lambda { FireEagle::Response.new(XML_ERROR_RESPONSE) }.should raise_error(FireEagle::FireEagleException)
61
+ end
62
+
63
+ end
64
+
65
+ end
@@ -0,0 +1,150 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe "FireEagle" do
4
+
5
+ describe "being initialized" do
6
+
7
+ it "should require OAuth Consumer Key and Secret" do
8
+ lambda do
9
+ client = FireEagle::Client.new({})
10
+ end.should raise_error(FireEagle::ArgumentError)
11
+ end
12
+
13
+ it "should initialize an OAuth::Consumer" do
14
+ @consumer = mock(OAuth::Consumer)
15
+ OAuth::Consumer.should_receive(:new).and_return(@consumer)
16
+ client = FireEagle::Client.new(:consumer_key => 'key', :consumer_secret => 'sekret')
17
+ end
18
+
19
+ end
20
+
21
+ describe "web app authentication scenario" do
22
+
23
+ it "should initialize a OAuth::Token if given it's token and secret" do
24
+ @access_token = mock(OAuth::Token)
25
+ OAuth::Token.stub!(:new).and_return(@access_token)
26
+ client = FireEagle::Client.new(:consumer_key => 'key', :consumer_secret => 'sekret', :access_token => 'toke', :access_token_secret => 'sekret')
27
+ client.access_token.should == @access_token
28
+ end
29
+
30
+ it "should initialize a request token if given one" do
31
+ @request_token = mock(OAuth::Token)
32
+ OAuth::Token.stub!(:new).and_return(@request_token)
33
+ client = FireEagle::Client.new(:consumer_key => 'key', :consumer_secret => 'sekret', :request_token => 'toke', :request_token_secret => 'sekret')
34
+ client.request_token.should == @request_token
35
+ end
36
+ end
37
+
38
+ describe "request token scenario" do
39
+ it "shouldn't initialize with a access_token" do
40
+ client = FireEagle::Client.new(:consumer_key => 'key', :consumer_secret => 'sekret')
41
+ client.access_token.should == nil
42
+ end
43
+
44
+ it "should require token exchange before calling any API methods" do
45
+ client = FireEagle::Client.new(:consumer_key => 'key', :consumer_secret => 'sekret')
46
+ lambda do
47
+ client.user
48
+ end.should raise_error(FireEagle::ArgumentError)
49
+ end
50
+
51
+ it "should generate a Request Token URL" do
52
+ client = FireEagle::Client.new(:consumer_key => 'key', :consumer_secret => 'sekret')
53
+ @token = mock("token", :token => 'foo')
54
+ client.stub!(:get).and_return('')
55
+ client.stub!(:create_token).and_return(@token)
56
+ client.get_request_token
57
+ client.authorization_url.should =~ /\?oauth_token=foo/
58
+ end
59
+
60
+ it "should require #get_request_token be called before #convert_to_access_token" do
61
+ lambda do
62
+ client = FireEagle::Client.new(:consumer_key => 'key', :consumer_secret => 'sekret')
63
+ client.convert_to_access_token
64
+ end.should raise_error(FireEagle::ArgumentError)
65
+ end
66
+
67
+ it "should require #get_request_token be called before #authorization_url" do
68
+ lambda do
69
+ client = FireEagle::Client.new(:consumer_key => 'key', :consumer_secret => 'sekret')
70
+ client.authorization_url
71
+ end.should raise_error(FireEagle::ArgumentError)
72
+ end
73
+
74
+ it "should generate a Request Token URL" do
75
+ client = FireEagle::Client.new(:consumer_key => 'key', :consumer_secret => 'sekret')
76
+ @token = mock("token", :token => 'foo')
77
+ client.stub!(:get).and_return('')
78
+ client.stub!(:create_token).and_return(@token)
79
+ client.get_request_token
80
+ client.convert_to_access_token
81
+ client.access_token.should == @token
82
+ end
83
+
84
+ end
85
+
86
+ describe "update method" do
87
+
88
+ before(:each) do
89
+ @client = FireEagle::Client.new(:consumer_key => 'key', :consumer_secret => 'sekret', :access_token => 'toke', :access_token_secret => 'sekret')
90
+ @response = mock('response', :body => XML_SUCCESS_RESPONSE)
91
+ @client.stub!(:request).and_return(@response)
92
+ end
93
+
94
+ it "requires all or none of :lat, :lon" do
95
+ lambda { @client.update(:lat => 1) }.should raise_error(FireEagle::ArgumentError)
96
+ lambda { @client.update(:lat => 1, :lon => 2) }.should_not raise_error(FireEagle::ArgumentError)
97
+ end
98
+
99
+ it "requires all or none of :mnc, :mcc, :lac, :cid" do
100
+ lambda { @client.update(:mcc => 123, :lac => "whatever", :cid => true) }.should raise_error(FireEagle::ArgumentError)
101
+ lambda { @client.update(:mcc => 123, :mnc => 123123, :lac => "whatever", :cid => true) }.should_not raise_error(FireEagle::ArgumentError)
102
+ end
103
+
104
+ it "should wrap the result" do
105
+ @client.update(:mcc => 123, :mnc => 123123, :lac => "whatever", :cid => true).users.first.token.should == "16w3z6ysudxt"
106
+ end
107
+
108
+ end
109
+
110
+ describe "user method" do
111
+
112
+ before(:each) do
113
+ @client = FireEagle::Client.new(:consumer_key => 'key', :consumer_secret => 'sekret', :access_token => 'toke', :access_token_secret => 'sekret')
114
+ @response = mock('response', :body => XML_LOCATION_RESPONSE)
115
+ @client.stub!(:request).and_return(@response)
116
+ end
117
+
118
+ it "should return a best guess" do
119
+ @client.user.best_guess.name.should == "Yolo County, California"
120
+ end
121
+
122
+ it "should return several locations" do
123
+ @client.user.locations.size.should == 4
124
+ end
125
+
126
+ end
127
+
128
+ describe "lookup method" do
129
+
130
+ before(:each) do
131
+ @client = FireEagle::Client.new(:consumer_key => 'key', :consumer_secret => 'sekret', :access_token => 'toke', :access_token_secret => 'sekret')
132
+ @response = mock('response', :body => XML_LOOKUP_RESPONSE)
133
+ @client.stub!(:request).and_return(@response)
134
+ end
135
+
136
+ it "should return an array of Locations" do
137
+ @client.lookup(:q => "30022").size.should == 9
138
+ end
139
+
140
+ it "should return a place id for each" do
141
+ @client.lookup(:q => "30022").first.place_id.should == "IrhZMHuYA5s1fFi4Qw"
142
+ end
143
+
144
+ it "should return a name for each" do
145
+ @client.lookup(:q => "30022").first.name.should == "Alpharetta, GA 30022"
146
+ end
147
+
148
+ end
149
+
150
+ end