fireeagle 0.0.2 → 0.6.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.
@@ -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