reagent-fleakr 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/{README.markdown → README.rdoc} +53 -26
- data/Rakefile +3 -3
- 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 +3 -3
- data/lib/fleakr.rb +66 -11
- data/test/fixtures/contacts.getPublicList.xml +7 -0
- data/test/fixtures/groups.pools.getPhotos.xml +7 -0
- data/test/fixtures/people.getInfo.xml +1 -1
- data/test/fixtures/photos.getSizes.xml +10 -0
- data/test/test_helper.rb +21 -6
- data/test/unit/fleakr/api/request_test.rb +93 -0
- data/test/{fleakr → unit/fleakr/api}/response_test.rb +2 -2
- data/test/unit/fleakr/objects/contact_test.rb +58 -0
- data/test/{fleakr → unit/fleakr/objects}/error_test.rb +2 -2
- data/test/{fleakr → unit/fleakr/objects}/group_test.rb +6 -2
- data/test/unit/fleakr/objects/image_test.rb +76 -0
- data/test/unit/fleakr/objects/photo_test.rb +101 -0
- data/test/{fleakr → unit/fleakr/objects}/search_test.rb +21 -16
- data/test/{fleakr → unit/fleakr/objects}/set_test.rb +17 -12
- data/test/{fleakr → unit/fleakr/objects}/user_test.rb +44 -3
- data/test/{fleakr → unit/fleakr/support}/attribute_test.rb +2 -2
- data/test/{fleakr → unit/fleakr/support}/object_test.rb +3 -3
- data/test/unit/fleakr_test.rb +44 -0
- metadata +43 -29
- data/lib/fleakr/attribute.rb +0 -26
- data/lib/fleakr/error.rb +0 -10
- data/lib/fleakr/group.rb +0 -12
- data/lib/fleakr/image.rb +0 -35
- data/lib/fleakr/object.rb +0 -75
- data/lib/fleakr/photo.rb +0 -26
- data/lib/fleakr/request.rb +0 -45
- data/lib/fleakr/response.rb +0 -21
- data/lib/fleakr/search.rb +0 -31
- data/lib/fleakr/set.rb +0 -22
- data/lib/fleakr/user.rb +0 -33
- data/test/fleakr/image_test.rb +0 -82
- data/test/fleakr/photo_test.rb +0 -64
- data/test/fleakr/request_test.rb +0 -99
@@ -1,10 +1,16 @@
|
|
1
|
-
|
1
|
+
= Fleakr
|
2
2
|
|
3
|
-
|
3
|
+
== Description
|
4
4
|
|
5
5
|
A teeny tiny gem to interface with Flickr photostreams
|
6
6
|
|
7
|
-
|
7
|
+
== Installation
|
8
|
+
|
9
|
+
=== Stable
|
10
|
+
|
11
|
+
sudo gem install fleakr
|
12
|
+
|
13
|
+
=== Bleeding Edge
|
8
14
|
|
9
15
|
sudo gem install reagent-fleakr --source=http://gems.github.com
|
10
16
|
|
@@ -14,7 +20,7 @@ Or ...
|
|
14
20
|
$ cd fleakr
|
15
21
|
$ rake gem && sudo gem install pkg/fleakr-<version>.gem
|
16
22
|
|
17
|
-
|
23
|
+
== Usage
|
18
24
|
|
19
25
|
Before doing anything, require the library:
|
20
26
|
|
@@ -23,40 +29,51 @@ Before doing anything, require the library:
|
|
23
29
|
|
24
30
|
Then, set your API key (only need to do this once per session):
|
25
31
|
|
26
|
-
>> Fleakr
|
32
|
+
>> Fleakr.api_key = '<your api key here>'
|
27
33
|
|
28
34
|
Find a user by username:
|
29
35
|
|
30
|
-
>> user = Fleakr
|
31
|
-
=> #<Fleakr::User:0x692648 @username="the decapitator", @id="21775151@N06">
|
36
|
+
>> user = Fleakr.user('the decapitator')
|
37
|
+
=> #<Fleakr::Objects::User:0x692648 @username="the decapitator", @id="21775151@N06">
|
32
38
|
|
33
39
|
Or by email:
|
34
40
|
|
35
|
-
>> user = Fleakr
|
36
|
-
=> #<Fleakr::User:0x11f484c @username="bckspcr", @id="84481630@N00">
|
41
|
+
>> user = Fleakr.user('user@host.com')
|
42
|
+
=> #<Fleakr::Objects::User:0x11f484c @username="bckspcr", @id="84481630@N00">
|
37
43
|
|
38
44
|
Once you have a user, you can find his associated sets:
|
39
45
|
|
40
46
|
>> user.sets
|
41
|
-
=> [#<Fleakr::Set:0x671358 @title="The Decapitator", @description="">,
|
42
|
-
#<Fleakr::Set:0x66d898 @title="londonpaper hijack", ...
|
47
|
+
=> [#<Fleakr::Objects::Set:0x671358 @title="The Decapitator", @description="">,
|
48
|
+
#<Fleakr::Objects::Set:0x66d898 @title="londonpaper hijack", ...
|
49
|
+
|
50
|
+
Or contacts:
|
51
|
+
|
52
|
+
>> user.contacts.first
|
53
|
+
=> #<Fleakr::Objects::User:0x19039bc @username=".schill",
|
54
|
+
@id="12289718@N00", @icon_farm="1", @icon_server="4">
|
43
55
|
|
44
56
|
Or groups if you would like:
|
45
57
|
|
46
58
|
>> user.groups
|
47
|
-
=> [#<Fleakr::Group:0x11f2330 ...,
|
48
|
-
#<Fleakr::Group:0x11f2308 ...
|
59
|
+
=> [#<Fleakr::Objects::Group:0x11f2330 ...,
|
60
|
+
#<Fleakr::Objects::Group:0x11f2308 ...
|
49
61
|
>> user.groups.first.name
|
50
62
|
=> "Rural Decay"
|
51
63
|
>> user.groups.first.id
|
52
64
|
=> "14581414@N00"
|
53
65
|
|
66
|
+
Groups also contain photos:
|
67
|
+
|
68
|
+
>> u.groups.last.photos.first.title
|
69
|
+
=> "Welcome To The Machine"
|
70
|
+
|
54
71
|
When accessing a set, you can also grab all the photos that are in that set:
|
55
72
|
|
56
73
|
>> user.sets.first
|
57
|
-
=> #<Fleakr::Set:0x1195bbc @title="The Decapitator", @id="72157603480986566", @description="">
|
74
|
+
=> #<Fleakr::Objects::Set:0x1195bbc @title="The Decapitator", @id="72157603480986566", @description="">
|
58
75
|
>> user.sets.first.photos.first
|
59
|
-
=> #<Fleakr::Photo:0x1140108 ... >
|
76
|
+
=> #<Fleakr::Objects::Photo:0x1140108 ... >
|
60
77
|
>> user.sets.first.photos.first.title
|
61
78
|
=> "Untitled1"
|
62
79
|
|
@@ -69,7 +86,7 @@ If you can't decide on a photo and would rather just save the whole set, specify
|
|
69
86
|
and the size of the images you're interested in:
|
70
87
|
|
71
88
|
>> user.sets.first.save_to('/tmp', :square)
|
72
|
-
=> [#<Fleakr::Photo:0x1187a1c @secret="715587b2cb" ...
|
89
|
+
=> [#<Fleakr::Objects::Photo:0x1187a1c @secret="715587b2cb" ...
|
73
90
|
>> Dir["/tmp/#{user.sets.first.title}/*.jpg"].map
|
74
91
|
=> ["/tmp/The Decapitator/2117919621_8b2d601bff_s.jpg",
|
75
92
|
"/tmp/The Decapitator/2117921045_5fb15eff90_s.jpg",
|
@@ -77,26 +94,36 @@ and the size of the images you're interested in:
|
|
77
94
|
|
78
95
|
If you would prefer to just search photos, you can do that with search text:
|
79
96
|
|
80
|
-
>>
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
>> search.results.first.title
|
97
|
+
>> photos = Fleakr.search('ponies!!')
|
98
|
+
=> [#<Fleakr::Objects::Photo:0x11f4e64 @title="hiroshima atomic garden", @id="3078234390">,
|
99
|
+
#<Fleakr::Objects::Photo:0x11f4928 @title="PONYLOV", @id="3077360853">, ...
|
100
|
+
>> photos.first.title
|
85
101
|
=> "hiroshima atomic garden"
|
86
102
|
|
87
103
|
You can also search based on tags:
|
88
104
|
|
89
|
-
>>
|
90
|
-
>>
|
105
|
+
>> photos = Fleakr.search(:tags => 'macro')
|
106
|
+
>> photos.first.title
|
91
107
|
=> "Demure"
|
92
|
-
>>
|
108
|
+
>> photos.first.id
|
93
109
|
=> "3076049945"
|
94
110
|
|
95
|
-
|
111
|
+
Searches can also be scoped to other entities in the system (namely Users and Groups):
|
112
|
+
|
113
|
+
>> user.groups.first.search('awesome')
|
114
|
+
=> [#<Fleakr::Objects::Photo:0x18cb4cc @server_id="2012", @id="2181921273",
|
115
|
+
@farm_id="3", @title="", @secret="634eda7521">, ...
|
116
|
+
>> user.search('serpent')
|
117
|
+
=> [#<Fleakr::Objects::Photo:0x18a6960 @server_id="41", @id="81370156",
|
118
|
+
@farm_id="1", @title="Clear and Serpent Danger", @secret="013091582a">]
|
119
|
+
|
120
|
+
== TODO
|
96
121
|
|
97
122
|
* Implement remaining bits of person, photoset, and photo-releated APIs
|
123
|
+
* Provide a better searching interface
|
124
|
+
* Lazily load attributes for objects that need to be accessed via secondary API call
|
98
125
|
|
99
|
-
|
126
|
+
== License
|
100
127
|
|
101
128
|
Copyright (c) 2008 Patrick Reagan (reaganpr@gmail.com)
|
102
129
|
|
data/Rakefile
CHANGED
@@ -10,13 +10,13 @@ spec = Gem::Specification.new do |s|
|
|
10
10
|
s.name = 'fleakr'
|
11
11
|
s.version = Fleakr::Version.to_s
|
12
12
|
s.has_rdoc = true
|
13
|
-
s.extra_rdoc_files = %w(README.
|
13
|
+
s.extra_rdoc_files = %w(README.rdoc)
|
14
|
+
s.rdoc_options = %w(--main README.rdoc)
|
14
15
|
s.summary = "A teeny tiny gem to interface with Flickr photostreams"
|
15
16
|
s.author = 'Patrick Reagan'
|
16
17
|
s.email = 'reaganpr@gmail.com'
|
17
18
|
s.homepage = 'http://sneaq.net'
|
18
|
-
s.files = %w(README.
|
19
|
-
# s.executables = ['fleakr']
|
19
|
+
s.files = %w(README.rdoc Rakefile) + Dir.glob("{lib,test}/**/*")
|
20
20
|
|
21
21
|
s.add_dependency('hpricot', '~> 0.6.0')
|
22
22
|
s.add_dependency('activesupport', '~> 2.2.0')
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Api # :nodoc:
|
3
|
+
|
4
|
+
# = Request
|
5
|
+
#
|
6
|
+
# Performs requests against the Flickr API and returns response objects (Flickr::Api::Response)
|
7
|
+
# that contain Hpricot documents for further parsing and inspection. This class is used internally
|
8
|
+
# in all the defined model objects.
|
9
|
+
#
|
10
|
+
class Request
|
11
|
+
|
12
|
+
# Generic catch-all exception for any API errors
|
13
|
+
class ApiError < StandardError; end
|
14
|
+
|
15
|
+
# Makes a request to the Flickr API and returns a valid Response object. If
|
16
|
+
# there are errors on the response it will rais an ApiError exception
|
17
|
+
def self.with_response!(method, additional_parameters = {})
|
18
|
+
request = Request.new(method, additional_parameters)
|
19
|
+
response = request.send
|
20
|
+
|
21
|
+
raise(ApiError, "Code: #{response.error.code} - #{response.error.message}") if response.error?
|
22
|
+
|
23
|
+
response
|
24
|
+
end
|
25
|
+
|
26
|
+
# Create a new request for the specified API method and pass along any additional
|
27
|
+
# parameters. The Flickr API uses namespacing for its methods - this is optional
|
28
|
+
# when calling this method.
|
29
|
+
#
|
30
|
+
# This must be called after initializing the library with the required API key
|
31
|
+
# see (#Fleakr.api_key=)
|
32
|
+
def initialize(method, additional_parameters = {})
|
33
|
+
method = method.sub(/^(flickr\.)?/, 'flickr.')
|
34
|
+
|
35
|
+
default_parameters = {:api_key => Fleakr.api_key, :method => method}
|
36
|
+
@parameters = default_parameters.merge(additional_parameters)
|
37
|
+
end
|
38
|
+
|
39
|
+
def send # :nodoc:
|
40
|
+
Response.new(Net::HTTP.get(endpoint_uri))
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def endpoint_uri
|
45
|
+
uri = URI.parse('http://api.flickr.com/services/rest/')
|
46
|
+
uri.query = query_parameters
|
47
|
+
uri
|
48
|
+
end
|
49
|
+
|
50
|
+
def query_parameters
|
51
|
+
@parameters.map {|key,value| "#{key}=#{CGI.escape(value)}" }.join('&')
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Api
|
3
|
+
|
4
|
+
# = Response
|
5
|
+
#
|
6
|
+
# Response objects contain Hpricot documents that are traversed and parsed by
|
7
|
+
# the model objects. This class is never called directly but is instantiated
|
8
|
+
# during the request cycle (see: Fleakr::Api::Request.with_response!)
|
9
|
+
#
|
10
|
+
class Response
|
11
|
+
|
12
|
+
# Creates a new response from a raw XML string returned from a Request
|
13
|
+
def initialize(response_xml)
|
14
|
+
@response_xml = response_xml
|
15
|
+
end
|
16
|
+
|
17
|
+
# Return a document-based representation of the XML contained in the
|
18
|
+
# API response. This is an Hpricot document object
|
19
|
+
def body
|
20
|
+
@body ||= Hpricot.XML(@response_xml)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Did the response from the API contain errors?
|
24
|
+
def error?
|
25
|
+
(self.body/'rsp').attr('stat') != 'ok'
|
26
|
+
end
|
27
|
+
|
28
|
+
# Access the API error if one exists
|
29
|
+
def error
|
30
|
+
Fleakr::Objects::Error.new(self.body) if self.error?
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Objects # :nodoc:
|
3
|
+
class Contact # :nodoc:
|
4
|
+
|
5
|
+
include Fleakr::Support::Object
|
6
|
+
|
7
|
+
flickr_attribute :id, :attribute => 'nsid'
|
8
|
+
flickr_attribute :username, :attribute => 'username'
|
9
|
+
flickr_attribute :icon_server, :attribute => 'iconserver'
|
10
|
+
flickr_attribute :icon_farm, :attribute => 'iconfarm'
|
11
|
+
|
12
|
+
# Retrieve a list of contacts for the specified user ID and return an initialized
|
13
|
+
# collection of #User objects
|
14
|
+
def self.find_all_by_user_id(user_id)
|
15
|
+
response = Fleakr::Api::Request.with_response!('contacts.getPublicList', :user_id => user_id)
|
16
|
+
(response.body/'contacts/contact').map {|c| Contact.new(c).to_user }
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_user
|
20
|
+
user = User.new
|
21
|
+
self.class.attributes.each do |attribute|
|
22
|
+
attribute_name = attribute.name
|
23
|
+
user.send("#{attribute.name}=".to_sym, self.send(attribute.name))
|
24
|
+
end
|
25
|
+
|
26
|
+
user
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Objects # :nodoc:
|
3
|
+
# = Error
|
4
|
+
#
|
5
|
+
# == Accessors
|
6
|
+
#
|
7
|
+
# This class is a simple wrapper for the error response that the API returns. There are
|
8
|
+
# a couple of attributes:
|
9
|
+
#
|
10
|
+
# [code] The error code as described in the documentation
|
11
|
+
# [message] The associated error message
|
12
|
+
#
|
13
|
+
class Error
|
14
|
+
|
15
|
+
include Fleakr::Support::Object
|
16
|
+
|
17
|
+
flickr_attribute :code, :xpath => 'rsp/err', :attribute => 'code'
|
18
|
+
flickr_attribute :message, :xpath => 'rsp/err', :attribute => 'msg'
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Objects # :nodoc:
|
3
|
+
|
4
|
+
# = Group
|
5
|
+
#
|
6
|
+
# == Accessors
|
7
|
+
#
|
8
|
+
# [id] This group's ID
|
9
|
+
# [name] The name of the group
|
10
|
+
#
|
11
|
+
class Group
|
12
|
+
|
13
|
+
include Fleakr::Support::Object
|
14
|
+
|
15
|
+
flickr_attribute :id, :attribute => 'nsid'
|
16
|
+
flickr_attribute :name, :attribute => 'name'
|
17
|
+
|
18
|
+
find_all :by_user_id, :call => 'people.getPublicGroups', :path => 'groups/group'
|
19
|
+
|
20
|
+
has_many :photos
|
21
|
+
|
22
|
+
scoped_search
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Objects
|
3
|
+
|
4
|
+
# = Image
|
5
|
+
#
|
6
|
+
# This class wraps the functionality for saving remote images to disk. It's called
|
7
|
+
# by the Fleakr::Objects::Photo class to save an image with a specific size and would
|
8
|
+
# typically never be called directly.
|
9
|
+
#
|
10
|
+
# Example:
|
11
|
+
#
|
12
|
+
# user = Fleakr.user('brownout')
|
13
|
+
# user.photos.first.small.save_to('/tmp')
|
14
|
+
#
|
15
|
+
# == Attributes
|
16
|
+
#
|
17
|
+
# [size] The name of this image's size (e.g. Square, Large, etc...)
|
18
|
+
# [width] The width of this image
|
19
|
+
# [height] The height of this image
|
20
|
+
# [url] The direct URL for this image
|
21
|
+
# [page] The page on Flickr that represents this photo
|
22
|
+
#
|
23
|
+
class Image
|
24
|
+
|
25
|
+
include Fleakr::Support::Object
|
26
|
+
|
27
|
+
flickr_attribute :size, :attribute => :label
|
28
|
+
flickr_attribute :width, :attribute => :width
|
29
|
+
flickr_attribute :height, :attribute => :height
|
30
|
+
flickr_attribute :url, :attribute => :source
|
31
|
+
flickr_attribute :page, :attribute => :url
|
32
|
+
|
33
|
+
find_all :by_photo_id, :call => 'photos.getSizes', :path => 'sizes/size'
|
34
|
+
|
35
|
+
# The filename portion of the image (without the full URL)
|
36
|
+
def filename
|
37
|
+
self.url.match(/([^\/]+)$/)[1]
|
38
|
+
end
|
39
|
+
|
40
|
+
# Save this image to the specified directory or file. If the target is a
|
41
|
+
# directory, the file will be created with the original filename from Flickr.
|
42
|
+
# If the target is a file, it will be saved with the specified name. In the
|
43
|
+
# case that the target file already exists, this method will overwrite it.
|
44
|
+
def save_to(target)
|
45
|
+
destination = File.directory?(target) ? "#{target}/#{self.filename}" : "#{target}"
|
46
|
+
File.open(destination, 'w') {|f| f << Net::HTTP.get(URI.parse(self.url)) }
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Objects # :nodoc:
|
3
|
+
|
4
|
+
# = Photo
|
5
|
+
#
|
6
|
+
# == Attributes
|
7
|
+
#
|
8
|
+
# [id] The ID for this photo
|
9
|
+
# [title] The title of this photo
|
10
|
+
# [square] The tiny square representation of this photo
|
11
|
+
# [thumbnail] The thumbnail for this photo
|
12
|
+
# [small] The small representation of this photo
|
13
|
+
# [medium] The medium representation of this photo
|
14
|
+
# [large] The large representation of this photo
|
15
|
+
# [original] The original photo
|
16
|
+
#
|
17
|
+
# == Associations
|
18
|
+
#
|
19
|
+
# [images] The underlying images for this photo.
|
20
|
+
#
|
21
|
+
class Photo
|
22
|
+
|
23
|
+
# Available sizes for this photo
|
24
|
+
SIZES = [:square, :thumbnail, :small, :medium, :large, :original]
|
25
|
+
|
26
|
+
include Fleakr::Support::Object
|
27
|
+
|
28
|
+
flickr_attribute :title, :attribute => 'title'
|
29
|
+
flickr_attribute :id, :attribute => 'id'
|
30
|
+
flickr_attribute :farm_id, :attribute => 'farm'
|
31
|
+
flickr_attribute :server_id, :attribute => 'server'
|
32
|
+
flickr_attribute :secret, :attribute => 'secret'
|
33
|
+
|
34
|
+
find_all :by_photoset_id, :call => 'photosets.getPhotos', :path => 'photoset/photo'
|
35
|
+
find_all :by_user_id, :call => 'people.getPublicPhotos', :path => 'photos/photo'
|
36
|
+
find_all :by_group_id, :call => 'groups.pools.getPhotos', :path => 'photos/photo'
|
37
|
+
|
38
|
+
has_many :images
|
39
|
+
|
40
|
+
# Create methods to access image sizes by name
|
41
|
+
SIZES.each do |size|
|
42
|
+
define_method(size) do
|
43
|
+
images_by_size[size]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
def images_by_size
|
49
|
+
image_sizes = SIZES.inject({}) {|l,o| l.merge(o => nil)}
|
50
|
+
self.images.inject(image_sizes) {|l,o| l.merge!(o.size.downcase.to_sym => o) }
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|