fleakr 0.3.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/README.rdoc +149 -0
- data/Rakefile +40 -0
- data/lib/fleakr/api/request.rb +58 -0
- data/lib/fleakr/api/response.rb +35 -0
- data/lib/fleakr/objects/contact.rb +31 -0
- data/lib/fleakr/objects/error.rb +22 -0
- data/lib/fleakr/objects/group.rb +26 -0
- data/lib/fleakr/objects/image.rb +51 -0
- data/lib/fleakr/objects/photo.rb +55 -0
- data/lib/fleakr/objects/search.rb +33 -0
- data/lib/fleakr/objects/set.rb +45 -0
- data/lib/fleakr/objects/user.rb +106 -0
- data/lib/fleakr/support/attribute.rb +28 -0
- data/lib/fleakr/support/object.rb +88 -0
- data/lib/fleakr/version.rb +13 -0
- data/lib/fleakr.rb +74 -0
- data/test/fixtures/contacts.getPublicList.xml +7 -0
- data/test/fixtures/groups.pools.getPhotos.xml +7 -0
- data/test/fixtures/people.findByEmail.xml +6 -0
- data/test/fixtures/people.findByUsername.xml +6 -0
- data/test/fixtures/people.getInfo.xml +18 -0
- data/test/fixtures/people.getPublicGroups.xml +7 -0
- data/test/fixtures/people.getPublicPhotos.xml +7 -0
- data/test/fixtures/photos.getSizes.xml +10 -0
- data/test/fixtures/photos.search.xml +7 -0
- data/test/fixtures/photosets.getList.xml +13 -0
- data/test/fixtures/photosets.getPhotos.xml +7 -0
- data/test/test_helper.rb +123 -0
- data/test/unit/fleakr/api/request_test.rb +93 -0
- data/test/unit/fleakr/api/response_test.rb +49 -0
- data/test/unit/fleakr/objects/contact_test.rb +58 -0
- data/test/unit/fleakr/objects/error_test.rb +21 -0
- data/test/unit/fleakr/objects/group_test.rb +31 -0
- data/test/unit/fleakr/objects/image_test.rb +76 -0
- data/test/unit/fleakr/objects/photo_test.rb +101 -0
- data/test/unit/fleakr/objects/search_test.rb +74 -0
- data/test/unit/fleakr/objects/set_test.rb +71 -0
- data/test/unit/fleakr/objects/user_test.rb +104 -0
- data/test/unit/fleakr/support/attribute_test.rb +68 -0
- data/test/unit/fleakr/support/object_test.rb +95 -0
- data/test/unit/fleakr_test.rb +44 -0
- metadata +123 -0
@@ -0,0 +1,88 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Support # :nodoc:all
|
3
|
+
module Object
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
|
7
|
+
def attributes
|
8
|
+
@attributes ||= []
|
9
|
+
end
|
10
|
+
|
11
|
+
def flickr_attribute(name, options = {})
|
12
|
+
self.attributes << Attribute.new(name, options)
|
13
|
+
class_eval "attr_accessor :#{name}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def has_many(*attributes)
|
17
|
+
options = attributes.extract_options!
|
18
|
+
class_name = self.name
|
19
|
+
|
20
|
+
attributes.each do |attribute|
|
21
|
+
target = "Fleakr::Objects::#{attribute.to_s.classify}"
|
22
|
+
finder_attribute = options[:using].nil? ? "#{class_name.demodulize.underscore}_id": options[:using]
|
23
|
+
class_eval <<-CODE
|
24
|
+
def #{attribute}
|
25
|
+
@#{attribute} ||= #{target}.send("find_all_by_#{finder_attribute}".to_sym, self.id)
|
26
|
+
end
|
27
|
+
CODE
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def find_all(condition, options)
|
32
|
+
attribute = options[:using].nil? ? condition.to_s.sub(/^by_/, '') : options[:using]
|
33
|
+
target_class = options[:class_name].nil? ? self.name : "Fleakr::Objects::#{options[:class_name]}"
|
34
|
+
|
35
|
+
class_eval <<-CODE
|
36
|
+
def self.find_all_#{condition}(value)
|
37
|
+
response = Fleakr::Api::Request.with_response!('#{options[:call]}', :#{attribute} => value)
|
38
|
+
(response.body/'rsp/#{options[:path]}').map {|e| #{target_class}.new(e) }
|
39
|
+
end
|
40
|
+
CODE
|
41
|
+
end
|
42
|
+
|
43
|
+
def find_one(condition, options)
|
44
|
+
attribute = options[:using].nil? ? condition.to_s.sub(/^by_/, '') : options[:using]
|
45
|
+
|
46
|
+
class_eval <<-CODE
|
47
|
+
def self.find_#{condition}(value)
|
48
|
+
response = Fleakr::Api::Request.with_response!('#{options[:call]}', :#{attribute} => value)
|
49
|
+
#{self.name}.new(response.body)
|
50
|
+
end
|
51
|
+
CODE
|
52
|
+
end
|
53
|
+
|
54
|
+
def scoped_search
|
55
|
+
key = "#{self.name.demodulize.underscore.downcase}_id".to_sym
|
56
|
+
|
57
|
+
class_eval <<-CODE
|
58
|
+
def search(search_text)
|
59
|
+
Fleakr::Objects::Search.new(:text => search_text, :#{key} => self.id).results
|
60
|
+
end
|
61
|
+
CODE
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
module InstanceMethods
|
67
|
+
|
68
|
+
def initialize(document = nil)
|
69
|
+
self.populate_from(document) unless document.nil?
|
70
|
+
end
|
71
|
+
|
72
|
+
def populate_from(document)
|
73
|
+
self.class.attributes.each do |attribute|
|
74
|
+
value = attribute.value_from(document)
|
75
|
+
self.send("#{attribute.name}=".to_sym, value) unless value.nil?
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.included(other)
|
82
|
+
other.send(:extend, Fleakr::Support::Object::ClassMethods)
|
83
|
+
other.send(:include, Fleakr::Support::Object::InstanceMethods)
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
data/lib/fleakr.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
require 'uri'
|
4
|
+
require 'cgi'
|
5
|
+
require 'net/http'
|
6
|
+
require 'rubygems'
|
7
|
+
require 'hpricot'
|
8
|
+
require 'activesupport'
|
9
|
+
|
10
|
+
%w(support api objects).each do |path|
|
11
|
+
full_path = File.expand_path(File.dirname(__FILE__)) + "/fleakr/#{path}"
|
12
|
+
Dir["#{full_path}/*.rb"].each {|f| require f }
|
13
|
+
end
|
14
|
+
|
15
|
+
# = Fleakr: A teeny tiny gem to interface with Flickr
|
16
|
+
#
|
17
|
+
# Getting started is easy, just make sure you have a valid API key from Flickr and you can
|
18
|
+
# then start making any non-authenticated request to pull back data for yours and others'
|
19
|
+
# photostreams, sets, contacts, groups, etc...
|
20
|
+
#
|
21
|
+
# For now, all activity originates from a single user which you can find by username or
|
22
|
+
# email address.
|
23
|
+
#
|
24
|
+
# Example:
|
25
|
+
#
|
26
|
+
# require 'rubygems'
|
27
|
+
# require 'fleakr'
|
28
|
+
#
|
29
|
+
# # Our API key is ABC123 (http://www.flickr.com/services/api/keys/apply/)
|
30
|
+
# Fleakr.api_key = 'ABC123'
|
31
|
+
# user = Fleakr.user('bees')
|
32
|
+
# user = Fleakr.user('user@host.com')
|
33
|
+
# # Grab a list of sets
|
34
|
+
# user.sets
|
35
|
+
# # Grab a list of the user's public groups
|
36
|
+
# user.groups
|
37
|
+
#
|
38
|
+
# To see what other associations and attributes are available, see the Fleakr::Objects::User class
|
39
|
+
#
|
40
|
+
module Fleakr
|
41
|
+
|
42
|
+
mattr_accessor :api_key
|
43
|
+
|
44
|
+
# Find a user based on some unique user data. This method will try to find
|
45
|
+
# the user based on username and will fall back to email if that fails. Example:
|
46
|
+
#
|
47
|
+
# Fleakr.api_key = 'ABC123'
|
48
|
+
# Fleakr.user('the decapitator') # => #<Fleakr::Objects::User:0x692648 @username="the decapitator", @id="21775151@N06">
|
49
|
+
# Fleakr.user('user@host.com') # => #<Fleakr::Objects::User:0x11f484c @username="bckspcr", @id="84481630@N00">
|
50
|
+
#
|
51
|
+
def self.user(user_data)
|
52
|
+
begin
|
53
|
+
Objects::User.find_by_username(user_data)
|
54
|
+
rescue Api::Request::ApiError
|
55
|
+
Objects::User.find_by_email(user_data)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Search all photos on the Flickr site. By default, this searches based on text, but you can pass
|
60
|
+
# different search parameters (passed as hash keys):
|
61
|
+
#
|
62
|
+
# [tags] The list of tags to search on (either as an array or comma-separated)
|
63
|
+
# [user_id] Scope the search to this user
|
64
|
+
# [group_id] Scope the search to this group
|
65
|
+
#
|
66
|
+
# If you're interested in User- and Group-scoped searches, you may want to use User#search and Group#search
|
67
|
+
# instead.
|
68
|
+
#
|
69
|
+
def self.search(params)
|
70
|
+
params = {:text => params} unless params.is_a?(Hash)
|
71
|
+
Objects::Search.new(params).results
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8" ?>
|
2
|
+
<rsp stat="ok">
|
3
|
+
<contacts page="1" pages="1" per_page="1000" perpage="1000" total="17">
|
4
|
+
<contact nsid="9302864@N42" username="blinky" iconserver="2263" iconfarm="3" ignored="0" />
|
5
|
+
<contact nsid="63204625@N20" username="inky" iconserver="53" iconfarm="1" ignored="0" />
|
6
|
+
</contacts>
|
7
|
+
</rsp>
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8" ?>
|
2
|
+
<rsp stat="ok">
|
3
|
+
<photos page="1" pages="796" perpage="100" total="79509">
|
4
|
+
<photo id="3101830908" owner="64008250@N00" secret="e85e249e72" server="3042" farm="4" title="Canal Gate" ispublic="1" isfriend="0" isfamily="0" ownername="oakraidr" dateadded="1229049777" />
|
5
|
+
<photo id="3100780049" owner="98821864@N00" secret="f46fbe211b" server="3274" farm="4" title="2alarm" ispublic="1" isfriend="0" isfamily="0" ownername="LooknFeel" dateadded="1229042651" />
|
6
|
+
</photos>
|
7
|
+
</rsp>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8" ?>
|
2
|
+
<rsp stat="ok">
|
3
|
+
<person id="31066442@N69" nsid="31066442@N69" isadmin="0" ispro="1" iconserver="30" iconfarm="1">
|
4
|
+
<username>frootpantz</username>
|
5
|
+
<realname>Sir Froot Pantz</realname>
|
6
|
+
<mbox_sha1sum>e52ed1e5b91c763694995460e9796fc2adc02019</mbox_sha1sum>
|
7
|
+
<location />
|
8
|
+
<photosurl>http://www.flickr.com/photos/frootpantz/</photosurl>
|
9
|
+
<profileurl>http://www.flickr.com/people/frootpantz/</profileurl>
|
10
|
+
<mobileurl>http://m.flickr.com/photostream.gne?id=34225</mobileurl>
|
11
|
+
<photos>
|
12
|
+
<firstdatetaken>2006-10-11 08:19:57</firstdatetaken>
|
13
|
+
<firstdate>1160612247</firstdate>
|
14
|
+
<count>3907</count>
|
15
|
+
<views>9002</views>
|
16
|
+
</photos>
|
17
|
+
</person>
|
18
|
+
</rsp>
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8" ?>
|
2
|
+
<rsp stat="ok">
|
3
|
+
<photos page="1" pages="1" perpage="100" total="76">
|
4
|
+
<photo id="2924549350" owner="21775151@N06" secret="cbc1804258" server="3250" farm="4" title="Photo #1" ispublic="1" isfriend="0" isfamily="0" />
|
5
|
+
<photo id="2923697303" owner="21775151@N06" secret="649aa95f29" server="3208" farm="4" title="Photo #2" ispublic="1" isfriend="0" isfamily="0" />
|
6
|
+
</photos>
|
7
|
+
</rsp>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8" ?>
|
2
|
+
<rsp stat="ok">
|
3
|
+
<sizes canblog="0" canprint="0" candownload="1">
|
4
|
+
<size label="Square" width="75" height="75" source="http://farm4.static.flickr.com/3093/2409912100_71e14ed08a_s.jpg" url="http://www.flickr.com/photos/the_decapitator/2409912100/sizes/sq/" media="photo" />
|
5
|
+
<size label="Thumbnail" width="100" height="67" source="http://farm4.static.flickr.com/3093/2409912100_71e14ed08a_t.jpg" url="http://www.flickr.com/photos/the_decapitator/2409912100/sizes/t/" media="photo" />
|
6
|
+
<size label="Small" width="240" height="160" source="http://farm4.static.flickr.com/3093/2409912100_71e14ed08a_m.jpg" url="http://www.flickr.com/photos/the_decapitator/2409912100/sizes/s/" media="photo" />
|
7
|
+
<size label="Medium" width="500" height="334" source="http://farm4.static.flickr.com/3093/2409912100_71e14ed08a.jpg" url="http://www.flickr.com/photos/the_decapitator/2409912100/sizes/m/" media="photo" />
|
8
|
+
<size label="Original" width="700" height="467" source="http://farm4.static.flickr.com/3093/2409912100_3305c4a108_o.jpg" url="http://www.flickr.com/photos/the_decapitator/2409912100/sizes/o/" media="photo" />
|
9
|
+
</sizes>
|
10
|
+
</rsp>
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8" ?>
|
2
|
+
<rsp stat="ok">
|
3
|
+
<photos page="1" pages="385" perpage="100" total="38424">
|
4
|
+
<photo id="3076020861" owner="31987693@N05" secret="b956549c07" server="3278" farm="4" title="Mona and Lisa" ispublic="1" isfriend="0" isfamily="0" />
|
5
|
+
<photo id="3075386827" owner="9377349@N05" secret="4e40291b2d" server="3151" farm="4" title="IMG_0055 1.JPG" ispublic="1" isfriend="0" isfamily="0" />
|
6
|
+
</photos>
|
7
|
+
</rsp>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
2
|
+
<rsp stat="ok">
|
3
|
+
<photosets>
|
4
|
+
<photoset videos="0" primary="3044180117" farm="4" photos="138" id="72157609490909659" server="3012" secret="01cd1a741d">
|
5
|
+
<title>Second Set</title>
|
6
|
+
<description>This is the second set.</description>
|
7
|
+
</photoset>
|
8
|
+
<photoset videos="0" primary="2988511085" farm="4" photos="139" id="72157608538140671" server="3241" secret="a7b90926ba">
|
9
|
+
<title>First Set</title>
|
10
|
+
<description>This is the first set.</description>
|
11
|
+
</photoset>
|
12
|
+
</photosets>
|
13
|
+
</rsp>
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8" ?>
|
2
|
+
<rsp stat="ok">
|
3
|
+
<photoset id="72157609490909659" primary="3044180117" owner="31066442@N69" ownername="frootpantz" page="1" per_page="500" perpage="500" pages="1" total="138">
|
4
|
+
<photo id="3044163577" secret="fa27e5a824" server="3153" farm="4" title="Photo #1" isprimary="0" />
|
5
|
+
<photo id="3045001128" secret="a8c0e51b39" server="3204" farm="4" title="Photo #2" isprimary="0" />
|
6
|
+
</photoset>
|
7
|
+
</rsp>
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
$:.reject! { |e| e.include? 'TextMate' }
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'matchy'
|
5
|
+
require 'context'
|
6
|
+
require 'mocha'
|
7
|
+
|
8
|
+
require File.dirname(__FILE__) + '/../lib/fleakr'
|
9
|
+
|
10
|
+
class Test::Unit::TestCase
|
11
|
+
|
12
|
+
def self.should_have_a_value_for(attribute_test)
|
13
|
+
it "should have a value for :#{attribute_test.keys.first}" do
|
14
|
+
@object.send(attribute_test.keys.first).should == attribute_test.values.first
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.should_search_by(key)
|
19
|
+
it "should be able to perform a scoped search by :#{key}" do
|
20
|
+
photos = [stub()]
|
21
|
+
search = stub(:results => photos)
|
22
|
+
|
23
|
+
klass = self.class.name.sub(/Test$/, '').constantize
|
24
|
+
|
25
|
+
instance = klass.new
|
26
|
+
instance.stubs(:id).with().returns('1')
|
27
|
+
|
28
|
+
Fleakr::Objects::Search.expects(:new).with(:text => 'foo', key => '1').returns(search)
|
29
|
+
|
30
|
+
instance.search('foo').should == photos
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.should_have_many(*attributes)
|
35
|
+
class_name = self.name.demodulize.sub(/Test$/, '')
|
36
|
+
this_klass = "Fleakr::Objects::#{class_name}".constantize
|
37
|
+
|
38
|
+
options = attributes.extract_options!
|
39
|
+
finder_attribute = options[:using].nil? ? "#{class_name.downcase}_id" : options[:using]
|
40
|
+
|
41
|
+
attributes.each do |attribute|
|
42
|
+
target_klass = "Fleakr::Objects::#{attribute.to_s.singularize.classify}".constantize
|
43
|
+
it "should be able to retrieve the #{class_name.downcase}'s #{attribute}" do
|
44
|
+
results = [stub()]
|
45
|
+
object = this_klass.new
|
46
|
+
object.stubs(:id).with().returns('1')
|
47
|
+
|
48
|
+
target_klass.expects("find_all_by_#{finder_attribute}".to_sym).with('1').returns(results)
|
49
|
+
object.send(attribute).should == results
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should memoize the results for the #{class_name.downcase}'s #{attribute}" do
|
53
|
+
object = this_klass.new
|
54
|
+
|
55
|
+
target_klass.expects("find_all_by_#{finder_attribute}".to_sym).once.returns([])
|
56
|
+
2.times { object.send(attribute) }
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.should_find_one(thing, options)
|
63
|
+
class_name = thing.to_s.singularize.camelcase
|
64
|
+
klass = "Fleakr::Objects::#{class_name}".constantize
|
65
|
+
object_type = class_name.downcase
|
66
|
+
|
67
|
+
options[:with] = options[:by] if options[:with].nil?
|
68
|
+
|
69
|
+
it "should be able to find a #{thing} by #{options[:by]}" do
|
70
|
+
condition_value = '1'
|
71
|
+
stub = stub()
|
72
|
+
response = mock_request_cycle :for => options[:call], :with => {options[:with] => condition_value}
|
73
|
+
|
74
|
+
klass.expects(:new).with(response.body).returns(stub)
|
75
|
+
klass.send("find_by_#{options[:by]}".to_sym, condition_value).should == stub
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.should_find_all(thing, options)
|
80
|
+
class_name = thing.to_s.singularize.camelcase
|
81
|
+
klass = "Fleakr::Objects::#{class_name}".constantize
|
82
|
+
object_type = class_name.downcase
|
83
|
+
|
84
|
+
it "should be able to find all #{thing} by #{options[:by]}" do
|
85
|
+
condition_value = '1'
|
86
|
+
response = mock_request_cycle :for => options[:call], :with => {options[:by] => condition_value}
|
87
|
+
|
88
|
+
stubs = []
|
89
|
+
elements = (response.body/options[:path]).map
|
90
|
+
|
91
|
+
|
92
|
+
elements.each do |element|
|
93
|
+
stub = stub()
|
94
|
+
stubs << stub
|
95
|
+
|
96
|
+
klass.expects(:new).with(element).returns(stub)
|
97
|
+
end
|
98
|
+
|
99
|
+
klass.send("find_all_by_#{options[:by]}".to_sym, condition_value).should == stubs
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
def read_fixture(method_call)
|
105
|
+
fixture_path = File.dirname(__FILE__) + '/fixtures'
|
106
|
+
File.read("#{fixture_path}/#{method_call}.xml")
|
107
|
+
end
|
108
|
+
|
109
|
+
def mock_request_cycle(options)
|
110
|
+
response = stub(:body => Hpricot.XML(read_fixture(options[:for])))
|
111
|
+
Fleakr::Api::Request.expects(:with_response!).with(options[:for], options[:with]).returns(response)
|
112
|
+
|
113
|
+
response
|
114
|
+
end
|
115
|
+
|
116
|
+
def create_temp_directory
|
117
|
+
tmp_dir = File.dirname(__FILE__) + '/tmp'
|
118
|
+
FileUtils.mkdir(tmp_dir)
|
119
|
+
|
120
|
+
tmp_dir
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../../test_helper'
|
2
|
+
|
3
|
+
module Fleakr::Api
|
4
|
+
class RequestTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
describe "A Request instance" do
|
7
|
+
|
8
|
+
context "with an API key" do
|
9
|
+
|
10
|
+
before do
|
11
|
+
@api_key = 'f00b4r'
|
12
|
+
Fleakr.stubs(:api_key).with().returns(@api_key)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should know the full query parameters" do
|
16
|
+
request = Request.new('flickr.people.findByUsername', :username => 'foobar')
|
17
|
+
|
18
|
+
expected = [
|
19
|
+
"api_key=#{@api_key}",
|
20
|
+
"method=flickr.people.findByUsername",
|
21
|
+
"username=foobar"
|
22
|
+
]
|
23
|
+
|
24
|
+
request.query_parameters.split('&').sort.should == expected
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should escape the keys and values in the parameter list" do
|
28
|
+
request = Request.new('flickr.people.findByUsername', :username => 'the decapitator')
|
29
|
+
request.query_parameters.split('&').include?("username=#{CGI.escape('the decapitator')}").should be(true)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should translate a shorthand API call" do
|
33
|
+
request = Request.new('people.findByUsername')
|
34
|
+
request.query_parameters.split('&').include?('method=flickr.people.findByUsername').should be(true)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should know the endpoint with full parameters" do
|
38
|
+
query_parameters = 'foo=bar'
|
39
|
+
|
40
|
+
request = Request.new('people.getInfo')
|
41
|
+
request.stubs(:query_parameters).with().returns(query_parameters)
|
42
|
+
|
43
|
+
uri_mock = mock() {|m| m.expects(:query=).with(query_parameters)}
|
44
|
+
|
45
|
+
URI.expects(:parse).with("http://api.flickr.com/services/rest/").returns(uri_mock)
|
46
|
+
|
47
|
+
request.endpoint_uri.should == uri_mock
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should be able to make a request" do
|
51
|
+
endpoint_uri = stub()
|
52
|
+
|
53
|
+
request = Request.new('flickr.people.findByUsername')
|
54
|
+
|
55
|
+
request.stubs(:endpoint_uri).with().returns(endpoint_uri)
|
56
|
+
Net::HTTP.expects(:get).with(endpoint_uri).returns('<xml>')
|
57
|
+
|
58
|
+
request.send
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should create a response from the request" do
|
62
|
+
response_xml = '<xml>'
|
63
|
+
response_stub = stub()
|
64
|
+
|
65
|
+
request = Request.new('flickr.people.findByUsername')
|
66
|
+
|
67
|
+
Net::HTTP.stubs(:get).returns(response_xml)
|
68
|
+
Response.expects(:new).with(response_xml).returns(response_stub)
|
69
|
+
|
70
|
+
request.send.should == response_stub
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should be able to make a full request and response cycle" do
|
74
|
+
response = stub(:error? => false)
|
75
|
+
Response.expects(:new).with(kind_of(String)).returns(response)
|
76
|
+
|
77
|
+
Request.with_response!('flickr.people.findByUsername', :username => 'foobar').should == response
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should raise an exception when the full request / response cycle has errors" do
|
81
|
+
response = stub(:error? => true, :error => stub(:code => '1', :message => 'User not found'))
|
82
|
+
Response.stubs(:new).with(kind_of(String)).returns(response)
|
83
|
+
|
84
|
+
lambda do
|
85
|
+
Request.with_response!('flickr.people.findByUsername', :username => 'foobar')
|
86
|
+
end.should raise_error(Fleakr::Api::Request::ApiError)
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../../test_helper'
|
2
|
+
|
3
|
+
module Fleakr::Api
|
4
|
+
class ResponseTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
describe "An instance of Response" do
|
7
|
+
|
8
|
+
it "should provide the response body as an Hpricot element" do
|
9
|
+
response_xml = '<xml>'
|
10
|
+
hpricot_stub = stub()
|
11
|
+
|
12
|
+
Hpricot.expects(:XML).with(response_xml).returns(hpricot_stub)
|
13
|
+
|
14
|
+
response = Response.new(response_xml)
|
15
|
+
response.body.should == hpricot_stub
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should memoize the Hpricot document" do
|
19
|
+
response = Response.new('<xml>')
|
20
|
+
|
21
|
+
Hpricot.expects(:XML).with(kind_of(String)).once.returns(stub())
|
22
|
+
|
23
|
+
2.times { response.body }
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should know if there are errors in the response" do
|
27
|
+
response_xml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<rsp stat=\"fail\">\n\t<err code=\"1\" msg=\"User not found\" />\n</rsp>\n"
|
28
|
+
response = Response.new(response_xml)
|
29
|
+
|
30
|
+
response.error?.should be(true)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should not have an error if there are no errors in the XML" do
|
34
|
+
response = Response.new(read_fixture('people.findByUsername'))
|
35
|
+
response.error.should be(nil)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should have an error if there is an error in the response" do
|
39
|
+
response_xml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<rsp stat=\"fail\">\n\t<err code=\"1\" msg=\"User not found\" />\n</rsp>\n"
|
40
|
+
response = Response.new(response_xml)
|
41
|
+
|
42
|
+
response.error.code.should == '1'
|
43
|
+
response.error.message.should == 'User not found'
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../../test_helper'
|
2
|
+
|
3
|
+
module Fleakr::Objects
|
4
|
+
class ContactTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
describe "The Contact class" do
|
7
|
+
|
8
|
+
should "return a list of users for a specified user's contacts" do
|
9
|
+
user_1, user_2 = [stub(), stub()]
|
10
|
+
contact_1, contact_2 = [stub(:to_user => user_1), stub(:to_user => user_2)]
|
11
|
+
|
12
|
+
response = mock_request_cycle :for => 'contacts.getPublicList', :with => {:user_id => '1'}
|
13
|
+
|
14
|
+
contact_1_doc, contact_2_doc = (response.body/'rsp/contacts/contact').map
|
15
|
+
|
16
|
+
Contact.stubs(:new).with(contact_1_doc).returns(contact_1)
|
17
|
+
Contact.stubs(:new).with(contact_2_doc).returns(contact_2)
|
18
|
+
|
19
|
+
Contact.find_all_by_user_id('1').should == [user_1, user_2]
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "An instance of the Contact class" do
|
25
|
+
context "when populating from an XML document" do
|
26
|
+
before do
|
27
|
+
@object = Contact.new(Hpricot.XML(read_fixture('contacts.getPublicList')).at('contacts/contact'))
|
28
|
+
end
|
29
|
+
|
30
|
+
should_have_a_value_for :id => '9302864@N42'
|
31
|
+
should_have_a_value_for :username => 'blinky'
|
32
|
+
should_have_a_value_for :icon_server => '2263'
|
33
|
+
should_have_a_value_for :icon_farm => '3'
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
context "in general" do
|
38
|
+
|
39
|
+
should "be able to convert to a user" do
|
40
|
+
contact = Contact.new
|
41
|
+
user = mock()
|
42
|
+
|
43
|
+
User.stubs(:new).returns(user)
|
44
|
+
|
45
|
+
[:id, :username, :icon_server, :icon_farm].each do |method|
|
46
|
+
contact.stubs(method).with().returns(method.to_s)
|
47
|
+
user.expects("#{method}=".to_sym).with(method.to_s)
|
48
|
+
end
|
49
|
+
|
50
|
+
contact.to_user.should == user
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../../test_helper'
|
2
|
+
|
3
|
+
module Fleakr::Objects
|
4
|
+
class ErrorTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
describe "An instance of the Error class" do
|
7
|
+
|
8
|
+
it "should have a code and a message" do
|
9
|
+
response_xml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<rsp stat=\"fail\">\n\t<err code=\"1\" msg=\"User not found\" />\n</rsp>\n"
|
10
|
+
|
11
|
+
error = Error.new(Hpricot.XML(response_xml))
|
12
|
+
|
13
|
+
error.code.should == '1'
|
14
|
+
error.message.should == 'User not found'
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../../test_helper'
|
2
|
+
|
3
|
+
module Fleakr::Objects
|
4
|
+
class GroupTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
should_have_many :photos
|
7
|
+
|
8
|
+
should_search_by :group_id
|
9
|
+
|
10
|
+
describe "The Group class" do
|
11
|
+
|
12
|
+
should_find_all :groups, :by => :user_id, :call => 'people.getPublicGroups', :path => 'rsp/groups/group'
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "An instance of the Group class" do
|
17
|
+
context "when initializing from an Hpricot document" do
|
18
|
+
|
19
|
+
before do
|
20
|
+
doc = Hpricot.XML(read_fixture('people.getPublicGroups')).at('rsp/groups/group')
|
21
|
+
@object = Group.new(doc)
|
22
|
+
end
|
23
|
+
|
24
|
+
should_have_a_value_for :id => '13378274@N00'
|
25
|
+
should_have_a_value_for :name => 'Group #1'
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|