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.
Files changed (82) hide show
  1. data/README.rdoc +350 -0
  2. data/Rakefile +41 -0
  3. data/lib/fleakr.rb +164 -0
  4. data/lib/fleakr/api.rb +8 -0
  5. data/lib/fleakr/api/file_parameter.rb +47 -0
  6. data/lib/fleakr/api/method_request.rb +66 -0
  7. data/lib/fleakr/api/option.rb +175 -0
  8. data/lib/fleakr/api/parameter.rb +35 -0
  9. data/lib/fleakr/api/parameter_list.rb +97 -0
  10. data/lib/fleakr/api/response.rb +35 -0
  11. data/lib/fleakr/api/upload_request.rb +75 -0
  12. data/lib/fleakr/api/value_parameter.rb +36 -0
  13. data/lib/fleakr/core_ext.rb +3 -0
  14. data/lib/fleakr/core_ext/false_class.rb +7 -0
  15. data/lib/fleakr/core_ext/hash.rb +22 -0
  16. data/lib/fleakr/core_ext/true_class.rb +7 -0
  17. data/lib/fleakr/objects.rb +13 -0
  18. data/lib/fleakr/objects/authentication_token.rb +60 -0
  19. data/lib/fleakr/objects/comment.rb +49 -0
  20. data/lib/fleakr/objects/contact.rb +31 -0
  21. data/lib/fleakr/objects/error.rb +22 -0
  22. data/lib/fleakr/objects/group.rb +36 -0
  23. data/lib/fleakr/objects/image.rb +50 -0
  24. data/lib/fleakr/objects/photo.rb +147 -0
  25. data/lib/fleakr/objects/photo_context.rb +49 -0
  26. data/lib/fleakr/objects/search.rb +30 -0
  27. data/lib/fleakr/objects/set.rb +51 -0
  28. data/lib/fleakr/objects/tag.rb +56 -0
  29. data/lib/fleakr/objects/user.rb +95 -0
  30. data/lib/fleakr/support.rb +2 -0
  31. data/lib/fleakr/support/attribute.rb +46 -0
  32. data/lib/fleakr/support/object.rb +112 -0
  33. data/lib/fleakr/version.rb +13 -0
  34. data/test/fixtures/auth.checkToken.xml +8 -0
  35. data/test/fixtures/auth.getFullToken.xml +8 -0
  36. data/test/fixtures/auth.getToken.xml +8 -0
  37. data/test/fixtures/contacts.getPublicList.xml +7 -0
  38. data/test/fixtures/groups.pools.getPhotos.xml +7 -0
  39. data/test/fixtures/people.findByEmail.xml +6 -0
  40. data/test/fixtures/people.findByUsername.xml +6 -0
  41. data/test/fixtures/people.getInfo.xml +18 -0
  42. data/test/fixtures/people.getPublicGroups.xml +7 -0
  43. data/test/fixtures/people.getPublicPhotos.xml +7 -0
  44. data/test/fixtures/photos.comments.getList.xml +7 -0
  45. data/test/fixtures/photos.getContext.xml +6 -0
  46. data/test/fixtures/photos.getInfo.xml +20 -0
  47. data/test/fixtures/photos.getSizes.xml +10 -0
  48. data/test/fixtures/photos.search.xml +7 -0
  49. data/test/fixtures/photosets.comments.getList.xml +7 -0
  50. data/test/fixtures/photosets.getList.xml +13 -0
  51. data/test/fixtures/photosets.getPhotos.xml +7 -0
  52. data/test/fixtures/tags.getListPhoto.xml +9 -0
  53. data/test/fixtures/tags.getListUser.xml +10 -0
  54. data/test/fixtures/tags.getRelated.xml +9 -0
  55. data/test/test_helper.rb +141 -0
  56. data/test/unit/fleakr/api/file_parameter_test.rb +63 -0
  57. data/test/unit/fleakr/api/method_request_test.rb +94 -0
  58. data/test/unit/fleakr/api/option_test.rb +179 -0
  59. data/test/unit/fleakr/api/parameter_list_test.rb +176 -0
  60. data/test/unit/fleakr/api/parameter_test.rb +34 -0
  61. data/test/unit/fleakr/api/response_test.rb +49 -0
  62. data/test/unit/fleakr/api/upload_request_test.rb +149 -0
  63. data/test/unit/fleakr/api/value_parameter_test.rb +41 -0
  64. data/test/unit/fleakr/core_ext/false_class_test.rb +13 -0
  65. data/test/unit/fleakr/core_ext/hash_test.rb +32 -0
  66. data/test/unit/fleakr/core_ext/true_class_test.rb +13 -0
  67. data/test/unit/fleakr/objects/authentication_token_test.rb +61 -0
  68. data/test/unit/fleakr/objects/comment_test.rb +66 -0
  69. data/test/unit/fleakr/objects/contact_test.rb +61 -0
  70. data/test/unit/fleakr/objects/error_test.rb +21 -0
  71. data/test/unit/fleakr/objects/group_test.rb +46 -0
  72. data/test/unit/fleakr/objects/image_test.rb +81 -0
  73. data/test/unit/fleakr/objects/photo_context_test.rb +80 -0
  74. data/test/unit/fleakr/objects/photo_test.rb +246 -0
  75. data/test/unit/fleakr/objects/search_test.rb +74 -0
  76. data/test/unit/fleakr/objects/set_test.rb +82 -0
  77. data/test/unit/fleakr/objects/tag_test.rb +98 -0
  78. data/test/unit/fleakr/objects/user_test.rb +91 -0
  79. data/test/unit/fleakr/support/attribute_test.rb +126 -0
  80. data/test/unit/fleakr/support/object_test.rb +129 -0
  81. data/test/unit/fleakr_test.rb +171 -0
  82. 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,2 @@
1
+ require 'fleakr/support/attribute'
2
+ require 'fleakr/support/object'
@@ -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