reagent-fleakr 0.2.1 → 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.
Files changed (49) hide show
  1. data/{README.markdown → README.rdoc} +53 -26
  2. data/Rakefile +3 -3
  3. data/lib/fleakr/api/request.rb +58 -0
  4. data/lib/fleakr/api/response.rb +35 -0
  5. data/lib/fleakr/objects/contact.rb +31 -0
  6. data/lib/fleakr/objects/error.rb +22 -0
  7. data/lib/fleakr/objects/group.rb +26 -0
  8. data/lib/fleakr/objects/image.rb +51 -0
  9. data/lib/fleakr/objects/photo.rb +55 -0
  10. data/lib/fleakr/objects/search.rb +33 -0
  11. data/lib/fleakr/objects/set.rb +45 -0
  12. data/lib/fleakr/objects/user.rb +106 -0
  13. data/lib/fleakr/support/attribute.rb +28 -0
  14. data/lib/fleakr/support/object.rb +88 -0
  15. data/lib/fleakr/version.rb +3 -3
  16. data/lib/fleakr.rb +66 -11
  17. data/test/fixtures/contacts.getPublicList.xml +7 -0
  18. data/test/fixtures/groups.pools.getPhotos.xml +7 -0
  19. data/test/fixtures/people.getInfo.xml +1 -1
  20. data/test/fixtures/photos.getSizes.xml +10 -0
  21. data/test/test_helper.rb +21 -6
  22. data/test/unit/fleakr/api/request_test.rb +93 -0
  23. data/test/{fleakr → unit/fleakr/api}/response_test.rb +2 -2
  24. data/test/unit/fleakr/objects/contact_test.rb +58 -0
  25. data/test/{fleakr → unit/fleakr/objects}/error_test.rb +2 -2
  26. data/test/{fleakr → unit/fleakr/objects}/group_test.rb +6 -2
  27. data/test/unit/fleakr/objects/image_test.rb +76 -0
  28. data/test/unit/fleakr/objects/photo_test.rb +101 -0
  29. data/test/{fleakr → unit/fleakr/objects}/search_test.rb +21 -16
  30. data/test/{fleakr → unit/fleakr/objects}/set_test.rb +17 -12
  31. data/test/{fleakr → unit/fleakr/objects}/user_test.rb +44 -3
  32. data/test/{fleakr → unit/fleakr/support}/attribute_test.rb +2 -2
  33. data/test/{fleakr → unit/fleakr/support}/object_test.rb +3 -3
  34. data/test/unit/fleakr_test.rb +44 -0
  35. metadata +43 -29
  36. data/lib/fleakr/attribute.rb +0 -26
  37. data/lib/fleakr/error.rb +0 -10
  38. data/lib/fleakr/group.rb +0 -12
  39. data/lib/fleakr/image.rb +0 -35
  40. data/lib/fleakr/object.rb +0 -75
  41. data/lib/fleakr/photo.rb +0 -26
  42. data/lib/fleakr/request.rb +0 -45
  43. data/lib/fleakr/response.rb +0 -21
  44. data/lib/fleakr/search.rb +0 -31
  45. data/lib/fleakr/set.rb +0 -22
  46. data/lib/fleakr/user.rb +0 -33
  47. data/test/fleakr/image_test.rb +0 -82
  48. data/test/fleakr/photo_test.rb +0 -64
  49. data/test/fleakr/request_test.rb +0 -99
@@ -1,10 +1,16 @@
1
- # Fleakr
1
+ = Fleakr
2
2
 
3
- ## Description
3
+ == Description
4
4
 
5
5
  A teeny tiny gem to interface with Flickr photostreams
6
6
 
7
- ## Installation
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
- ## Usage
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::Request.api_key = '<your api key here>'
32
+ >> Fleakr.api_key = '<your api key here>'
27
33
 
28
34
  Find a user by username:
29
35
 
30
- >> user = Fleakr::User.find_by_username('the decapitator')
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::User.find_by_email('user@host.com')
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
- >> search = Fleakr::Search.new('ponies!!')
81
- >> search.results
82
- => [#<Fleakr::Photo:0x11f4e64 @title="hiroshima atomic garden", @id="3078234390">,
83
- #<Fleakr::Photo:0x11f4928 @title="PONYLOV", @id="3077360853">, ...
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
- >> search = Fleakr::Search.new(nil, :tags => 'macro')
90
- >> search.results.first.title
105
+ >> photos = Fleakr.search(:tags => 'macro')
106
+ >> photos.first.title
91
107
  => "Demure"
92
- >> search.results.first.id
108
+ >> photos.first.id
93
109
  => "3076049945"
94
110
 
95
- ## TODO
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
- ## License
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.markdown)
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.markdown Rakefile) + Dir.glob("{lib,test}/**/*")
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