slaskis-fleakr 0.5.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 +350 -0
- data/Rakefile +41 -0
- data/lib/fleakr.rb +164 -0
- data/lib/fleakr/api.rb +8 -0
- data/lib/fleakr/api/file_parameter.rb +47 -0
- data/lib/fleakr/api/method_request.rb +66 -0
- data/lib/fleakr/api/option.rb +175 -0
- data/lib/fleakr/api/parameter.rb +35 -0
- data/lib/fleakr/api/parameter_list.rb +97 -0
- data/lib/fleakr/api/response.rb +35 -0
- data/lib/fleakr/api/upload_request.rb +75 -0
- data/lib/fleakr/api/value_parameter.rb +36 -0
- data/lib/fleakr/core_ext.rb +3 -0
- data/lib/fleakr/core_ext/false_class.rb +7 -0
- data/lib/fleakr/core_ext/hash.rb +22 -0
- data/lib/fleakr/core_ext/true_class.rb +7 -0
- data/lib/fleakr/objects.rb +13 -0
- data/lib/fleakr/objects/authentication_token.rb +60 -0
- data/lib/fleakr/objects/comment.rb +49 -0
- data/lib/fleakr/objects/contact.rb +31 -0
- data/lib/fleakr/objects/error.rb +22 -0
- data/lib/fleakr/objects/group.rb +36 -0
- data/lib/fleakr/objects/image.rb +50 -0
- data/lib/fleakr/objects/photo.rb +147 -0
- data/lib/fleakr/objects/photo_context.rb +49 -0
- data/lib/fleakr/objects/search.rb +30 -0
- data/lib/fleakr/objects/set.rb +51 -0
- data/lib/fleakr/objects/tag.rb +56 -0
- data/lib/fleakr/objects/user.rb +95 -0
- data/lib/fleakr/support.rb +2 -0
- data/lib/fleakr/support/attribute.rb +46 -0
- data/lib/fleakr/support/object.rb +112 -0
- data/lib/fleakr/version.rb +13 -0
- data/test/fixtures/auth.checkToken.xml +8 -0
- data/test/fixtures/auth.getFullToken.xml +8 -0
- data/test/fixtures/auth.getToken.xml +8 -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.comments.getList.xml +7 -0
- data/test/fixtures/photos.getContext.xml +6 -0
- data/test/fixtures/photos.getInfo.xml +20 -0
- data/test/fixtures/photos.getSizes.xml +10 -0
- data/test/fixtures/photos.search.xml +7 -0
- data/test/fixtures/photosets.comments.getList.xml +7 -0
- data/test/fixtures/photosets.getList.xml +13 -0
- data/test/fixtures/photosets.getPhotos.xml +7 -0
- data/test/fixtures/tags.getListPhoto.xml +9 -0
- data/test/fixtures/tags.getListUser.xml +10 -0
- data/test/fixtures/tags.getRelated.xml +9 -0
- data/test/test_helper.rb +141 -0
- data/test/unit/fleakr/api/file_parameter_test.rb +63 -0
- data/test/unit/fleakr/api/method_request_test.rb +94 -0
- data/test/unit/fleakr/api/option_test.rb +179 -0
- data/test/unit/fleakr/api/parameter_list_test.rb +176 -0
- data/test/unit/fleakr/api/parameter_test.rb +34 -0
- data/test/unit/fleakr/api/response_test.rb +49 -0
- data/test/unit/fleakr/api/upload_request_test.rb +149 -0
- data/test/unit/fleakr/api/value_parameter_test.rb +41 -0
- data/test/unit/fleakr/core_ext/false_class_test.rb +13 -0
- data/test/unit/fleakr/core_ext/hash_test.rb +32 -0
- data/test/unit/fleakr/core_ext/true_class_test.rb +13 -0
- data/test/unit/fleakr/objects/authentication_token_test.rb +61 -0
- data/test/unit/fleakr/objects/comment_test.rb +66 -0
- data/test/unit/fleakr/objects/contact_test.rb +61 -0
- data/test/unit/fleakr/objects/error_test.rb +21 -0
- data/test/unit/fleakr/objects/group_test.rb +46 -0
- data/test/unit/fleakr/objects/image_test.rb +81 -0
- data/test/unit/fleakr/objects/photo_context_test.rb +80 -0
- data/test/unit/fleakr/objects/photo_test.rb +246 -0
- data/test/unit/fleakr/objects/search_test.rb +74 -0
- data/test/unit/fleakr/objects/set_test.rb +82 -0
- data/test/unit/fleakr/objects/tag_test.rb +98 -0
- data/test/unit/fleakr/objects/user_test.rb +91 -0
- data/test/unit/fleakr/support/attribute_test.rb +126 -0
- data/test/unit/fleakr/support/object_test.rb +129 -0
- data/test/unit/fleakr_test.rb +171 -0
- metadata +175 -0
@@ -0,0 +1,49 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Objects # :nodoc:
|
3
|
+
|
4
|
+
# = PhotoContext
|
5
|
+
#
|
6
|
+
# This class represents the context for a photo as retrieved from the API. It's not
|
7
|
+
# intended to be used directly, but is used in conjunction with Photo#previous and
|
8
|
+
# Photo#next
|
9
|
+
#
|
10
|
+
# == Attributes
|
11
|
+
#
|
12
|
+
# [count] The number of photos available
|
13
|
+
|
14
|
+
class PhotoContext
|
15
|
+
|
16
|
+
include Fleakr::Support::Object
|
17
|
+
|
18
|
+
flickr_attribute :count
|
19
|
+
flickr_attribute :next_id, :from => 'nextphoto@id'
|
20
|
+
flickr_attribute :previous_id, :from => 'prevphoto@id'
|
21
|
+
|
22
|
+
# Is there a previous photo available for the current photo?
|
23
|
+
#
|
24
|
+
def previous?
|
25
|
+
previous_id != '0'
|
26
|
+
end
|
27
|
+
|
28
|
+
# The previous photo if one is available
|
29
|
+
#
|
30
|
+
def previous
|
31
|
+
Photo.find_by_id(previous_id) if previous?
|
32
|
+
end
|
33
|
+
|
34
|
+
# Is there a next photo available for the current photo?
|
35
|
+
#
|
36
|
+
def next?
|
37
|
+
next_id != '0'
|
38
|
+
end
|
39
|
+
|
40
|
+
# The next photo if one is available
|
41
|
+
#
|
42
|
+
def next
|
43
|
+
Photo.find_by_id(next_id) if next?
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Objects # :nodoc:
|
3
|
+
class Search
|
4
|
+
|
5
|
+
# Create a new search
|
6
|
+
def initialize(search_options)
|
7
|
+
@search_options = search_options
|
8
|
+
end
|
9
|
+
|
10
|
+
# Retrieve search results from the API
|
11
|
+
def results
|
12
|
+
@results ||= begin
|
13
|
+
response = Fleakr::Api::MethodRequest.with_response!('photos.search', parameters)
|
14
|
+
(response.body/'rsp/photos/photo').map {|p| Photo.new(p) }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
def tag_list
|
20
|
+
Array(@search_options[:tags]).join(',')
|
21
|
+
end
|
22
|
+
|
23
|
+
def parameters
|
24
|
+
@search_options.merge!(:tags => tag_list) if tag_list.length > 0
|
25
|
+
@search_options
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Objects # :nodoc:
|
3
|
+
|
4
|
+
# = Set
|
5
|
+
#
|
6
|
+
# == Attributes
|
7
|
+
#
|
8
|
+
# [id] The ID for this photoset
|
9
|
+
# [title] The title of this photoset
|
10
|
+
# [description] The description of this set
|
11
|
+
# [count] Count of photos in this set
|
12
|
+
#
|
13
|
+
# == Associations
|
14
|
+
#
|
15
|
+
# [photos] The collection of photos for this set. See Fleakr::Objects::Photo
|
16
|
+
# [comments] All comments associated with this set. See Fleakr::Objects::Comment
|
17
|
+
#
|
18
|
+
class Set
|
19
|
+
|
20
|
+
include Fleakr::Support::Object
|
21
|
+
|
22
|
+
has_many :photos, :comments
|
23
|
+
|
24
|
+
flickr_attribute :id, :title, :description
|
25
|
+
flickr_attribute :count, :from => '@photos'
|
26
|
+
|
27
|
+
find_all :by_user_id, :call => 'photosets.getList', :path => 'photosets/photoset'
|
28
|
+
find_all :by_collection_id, :call => 'collections.getTree', :path => 'collections/collection/set'
|
29
|
+
|
30
|
+
# Save all photos in this set to the specified directory for the specified size. Allowed
|
31
|
+
# Sizes include <tt>:square</tt>, <tt>:small</tt>, <tt>:thumbnail</tt>, <tt>:medium</tt>,
|
32
|
+
# <tt>:large</tt>, and <tt>:original</tt>. When saving the set, this method will create
|
33
|
+
# a subdirectory based on the set's title.
|
34
|
+
#
|
35
|
+
def save_to(path, size)
|
36
|
+
target = "#{path}/#{self.title}"
|
37
|
+
FileUtils.mkdir(target) unless File.exist?(target)
|
38
|
+
|
39
|
+
self.photos.each_with_index do |photo, index|
|
40
|
+
image = photo.send(size)
|
41
|
+
image.save_to(target, file_prefix(index)) unless image.nil?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def file_prefix(index) # :nodoc:
|
46
|
+
sprintf("%0#{self.count.length}d_", (index + 1))
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Objects # :nodoc:
|
3
|
+
|
4
|
+
# = Tag
|
5
|
+
#
|
6
|
+
# This class represents a tag that can be associated with a photo or an individual user.
|
7
|
+
#
|
8
|
+
# == Attributes
|
9
|
+
#
|
10
|
+
# [id] The unique identifier for this tag
|
11
|
+
# [raw] The raw, user-entered value for this tag
|
12
|
+
# [value] The formatted value for this tag. Also available through <tt>to_s</tt>
|
13
|
+
#
|
14
|
+
class Tag
|
15
|
+
|
16
|
+
include Fleakr::Support::Object
|
17
|
+
|
18
|
+
flickr_attribute :id, :raw
|
19
|
+
flickr_attribute :author_id, :from => '@author'
|
20
|
+
flickr_attribute :value, :from => '.' # pull this from the current node
|
21
|
+
flickr_attribute :machine_flag, :from => '@machine_tag'
|
22
|
+
|
23
|
+
find_all :by_photo_id, :call => 'tags.getListPhoto', :path => 'photo/tags/tag'
|
24
|
+
find_all :by_user_id, :call => 'tags.getListUser', :path => 'who/tags/tag'
|
25
|
+
|
26
|
+
# The first user who created this tag. See Fleakr::Objects::User for more information
|
27
|
+
#
|
28
|
+
def author
|
29
|
+
@author ||= User.find_by_id(author_id) unless author_id.nil?
|
30
|
+
end
|
31
|
+
|
32
|
+
# A list of related tags. Each of the objects in the collection is an instance of Tag
|
33
|
+
#
|
34
|
+
def related
|
35
|
+
@related ||= begin
|
36
|
+
response = Fleakr::Api::MethodRequest.with_response!('tags.getRelated', :tag => value)
|
37
|
+
(response.body/'rsp/tags/tag').map {|e| Tag.new(e) }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Is this a machine tag?
|
42
|
+
#
|
43
|
+
def machine?
|
44
|
+
machine_flag != '0'
|
45
|
+
end
|
46
|
+
|
47
|
+
# The formatted value of the tag. Also available as <tt>value</tt>
|
48
|
+
#
|
49
|
+
def to_s
|
50
|
+
value
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Objects # :nodoc:
|
3
|
+
|
4
|
+
# = User
|
5
|
+
#
|
6
|
+
# == Accessors
|
7
|
+
#
|
8
|
+
# This class maps directly onto the flickr.people.* API methods and provides the following attributes
|
9
|
+
# for a user:
|
10
|
+
#
|
11
|
+
# [id] The ID for this user (also referred to as the NSID in the API docs)
|
12
|
+
# [username] This user's username
|
13
|
+
# [name] This user's full name (if entered)
|
14
|
+
# [location] This user's location (if entered)
|
15
|
+
# [photos_url] The direct URL to this user's photostream
|
16
|
+
# [profile_url] The direct URL to this user's profile
|
17
|
+
# [photos_count] The number of photos that this user has uploaded
|
18
|
+
# [icon_url] This user's buddy icon (or a default one if an icon wasn't uploaded)
|
19
|
+
# [pro?] Does this user have a pro account?
|
20
|
+
# [admin?] Is this user an admin?
|
21
|
+
#
|
22
|
+
# == Associations
|
23
|
+
#
|
24
|
+
# The User class is pretty central to many of the other data available across the system, so there are a
|
25
|
+
# few associations available to a user:
|
26
|
+
#
|
27
|
+
# [sets] A list of this user's public sets (newest first). See Fleakr::Objects::Set for more information.
|
28
|
+
# [groups] A list of this user's public groups. See Fleakr::Objects::Group.
|
29
|
+
# [photos] A list of this user's public photos (newest first). See Fleakr::Objects::Photo.
|
30
|
+
# [contacts] A list of this user's contacts - these are simply User objects
|
31
|
+
# [tags] The tags associated with this user
|
32
|
+
#
|
33
|
+
# == Examples
|
34
|
+
#
|
35
|
+
# Access to a specific user is typically done through the Fleakr.user method:
|
36
|
+
#
|
37
|
+
# user = Fleakr.user('brownout')
|
38
|
+
# user.id
|
39
|
+
# user.username
|
40
|
+
# user.sets
|
41
|
+
# user.contacts
|
42
|
+
#
|
43
|
+
class User
|
44
|
+
|
45
|
+
include Fleakr::Support::Object
|
46
|
+
|
47
|
+
flickr_attribute :id, :from => 'user@nsid'
|
48
|
+
flickr_attribute :username, :location
|
49
|
+
flickr_attribute :name, :from => 'person/realname'
|
50
|
+
flickr_attribute :photos_url, :from => 'person/photosurl'
|
51
|
+
flickr_attribute :profile_url, :from => 'person/profileurl'
|
52
|
+
flickr_attribute :photos_count, :from => 'person/photos/count'
|
53
|
+
flickr_attribute :icon_server, :from => 'person@iconserver'
|
54
|
+
flickr_attribute :icon_farm, :from => 'person@iconfarm'
|
55
|
+
flickr_attribute :pro, :from => 'person@ispro'
|
56
|
+
flickr_attribute :admin, :from => 'person@isadmin'
|
57
|
+
|
58
|
+
has_many :sets, :groups, :photos, :contacts, :tags, :collections
|
59
|
+
|
60
|
+
find_one :by_username, :call => 'people.findByUsername'
|
61
|
+
find_one :by_email, :using => :find_email, :call => 'people.findByEmail'
|
62
|
+
find_one :by_id, :using => :user_id, :call => 'people.getInfo'
|
63
|
+
|
64
|
+
lazily_load :name, :photos_url, :profile_url, :photos_count, :location, :with => :load_info
|
65
|
+
lazily_load :icon_server, :icon_farm, :pro, :admin, :with => :load_info
|
66
|
+
|
67
|
+
scoped_search
|
68
|
+
|
69
|
+
# Is this a pro account?
|
70
|
+
def pro?
|
71
|
+
(self.pro.to_i == 0) ? false : true
|
72
|
+
end
|
73
|
+
|
74
|
+
# Is this user an admin?
|
75
|
+
def admin?
|
76
|
+
(self.admin.to_i == 0) ? false : true
|
77
|
+
end
|
78
|
+
|
79
|
+
# This user's buddy icon
|
80
|
+
def icon_url
|
81
|
+
if self.icon_server.to_i > 0
|
82
|
+
"http://farm#{self.icon_farm}.static.flickr.com/#{self.icon_server}/buddyicons/#{self.id}.jpg"
|
83
|
+
else
|
84
|
+
'http://www.flickr.com/images/buddyicon.jpg'
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def load_info # :nodoc:
|
89
|
+
response = Fleakr::Api::MethodRequest.with_response!('people.getInfo', :user_id => self.id)
|
90
|
+
self.populate_from(response.body)
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Support # :nodoc:all
|
3
|
+
class Attribute
|
4
|
+
|
5
|
+
# TODO: Refactor the location / attribute logic into a Source class
|
6
|
+
|
7
|
+
attr_reader :name, :sources
|
8
|
+
|
9
|
+
def initialize(name, sources = nil)
|
10
|
+
@name = name.to_sym
|
11
|
+
|
12
|
+
@sources = Array(sources)
|
13
|
+
@sources << @name.to_s if @sources.empty?
|
14
|
+
end
|
15
|
+
|
16
|
+
def split(source)
|
17
|
+
location, attribute = source.split('@')
|
18
|
+
location = self.name.to_s if location.blank?
|
19
|
+
|
20
|
+
[location, attribute]
|
21
|
+
end
|
22
|
+
|
23
|
+
def node_for(document, source)
|
24
|
+
document.at(location(source)) || document.search("//[@#{attribute(source)}]").first
|
25
|
+
end
|
26
|
+
|
27
|
+
def attribute(source)
|
28
|
+
location, attribute = source.split('@')
|
29
|
+
attribute || location
|
30
|
+
end
|
31
|
+
|
32
|
+
def location(source)
|
33
|
+
split(source).first
|
34
|
+
end
|
35
|
+
|
36
|
+
def value_from(document)
|
37
|
+
values = sources.map do |source|
|
38
|
+
node = node_for(document, source)
|
39
|
+
(node.attributes[attribute(source)] || node.inner_text) unless node.nil?
|
40
|
+
end
|
41
|
+
values.compact.first
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,112 @@
|
|
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(*names_and_options)
|
12
|
+
options = names_and_options.extract_options!
|
13
|
+
|
14
|
+
names_and_options.each do |name|
|
15
|
+
self.attributes << Attribute.new(name, options[:from])
|
16
|
+
class_eval "attr_accessor :#{name}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def has_many(*attributes)
|
21
|
+
class_name = self.name
|
22
|
+
|
23
|
+
attributes.each do |attribute|
|
24
|
+
target = "Fleakr::Objects::#{attribute.to_s.classify}"
|
25
|
+
finder_attribute = "#{class_name.demodulize.underscore}_id"
|
26
|
+
|
27
|
+
class_eval <<-CODE
|
28
|
+
def #{attribute}
|
29
|
+
@#{attribute} ||= #{target}.send("find_all_by_#{finder_attribute}".to_sym, self.id)
|
30
|
+
end
|
31
|
+
CODE
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def find_all(condition, options)
|
36
|
+
attribute = options[:using].nil? ? condition.to_s.sub(/^by_/, '') : options[:using]
|
37
|
+
target_class = options[:class_name].nil? ? self.name : "Fleakr::Objects::#{options[:class_name]}"
|
38
|
+
|
39
|
+
class_eval <<-CODE
|
40
|
+
def self.find_all_#{condition}(value, options = {})
|
41
|
+
options.merge!(:#{attribute} => value)
|
42
|
+
|
43
|
+
logger.info("Ran XPath on response: #{options[:path]}")
|
44
|
+
|
45
|
+
response = Fleakr::Api::MethodRequest.with_response!('#{options[:call]}', options)
|
46
|
+
(response.body/'rsp/#{options[:path]}').map {|e| #{target_class}.new(e) }
|
47
|
+
end
|
48
|
+
CODE
|
49
|
+
end
|
50
|
+
|
51
|
+
def find_one(condition, options)
|
52
|
+
attribute = options[:using].nil? ? condition.to_s.sub(/^by_/, '') : options[:using]
|
53
|
+
|
54
|
+
class_eval <<-CODE
|
55
|
+
def self.find_#{condition}(value, options = {})
|
56
|
+
options.merge!(:#{attribute} => value)
|
57
|
+
|
58
|
+
response = Fleakr::Api::MethodRequest.with_response!('#{options[:call]}', options)
|
59
|
+
#{self.name}.new(response.body)
|
60
|
+
end
|
61
|
+
CODE
|
62
|
+
end
|
63
|
+
|
64
|
+
def scoped_search
|
65
|
+
key = "#{self.name.demodulize.underscore.downcase}_id".to_sym
|
66
|
+
|
67
|
+
class_eval <<-CODE
|
68
|
+
def search(search_text)
|
69
|
+
Fleakr::Objects::Search.new(:text => search_text, :#{key} => self.id).results
|
70
|
+
end
|
71
|
+
CODE
|
72
|
+
end
|
73
|
+
|
74
|
+
def lazily_load(*attributes)
|
75
|
+
options = attributes.extract_options!
|
76
|
+
|
77
|
+
attributes.each do |attribute|
|
78
|
+
class_eval <<-CODE
|
79
|
+
def #{attribute}_with_loading
|
80
|
+
self.send(:#{options[:with]}) if @#{attribute}.nil?
|
81
|
+
#{attribute}_without_loading
|
82
|
+
end
|
83
|
+
alias_method_chain :#{attribute}, :loading
|
84
|
+
CODE
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
module InstanceMethods
|
91
|
+
|
92
|
+
def initialize(document = nil)
|
93
|
+
self.populate_from(document) unless document.nil?
|
94
|
+
end
|
95
|
+
|
96
|
+
def populate_from(document)
|
97
|
+
self.class.attributes.each do |attribute|
|
98
|
+
value = attribute.value_from(document)
|
99
|
+
self.send("#{attribute.name}=".to_sym, value) unless value.nil?
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.included(other)
|
106
|
+
other.send(:extend, Fleakr::Support::Object::ClassMethods)
|
107
|
+
other.send(:include, Fleakr::Support::Object::InstanceMethods)
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|