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.
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