mwilliams-fleakr 0.5.1

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/api/file_parameter.rb +47 -0
  4. data/lib/fleakr/api/method_request.rb +66 -0
  5. data/lib/fleakr/api/option.rb +175 -0
  6. data/lib/fleakr/api/parameter.rb +35 -0
  7. data/lib/fleakr/api/parameter_list.rb +97 -0
  8. data/lib/fleakr/api/response.rb +35 -0
  9. data/lib/fleakr/api/upload_request.rb +75 -0
  10. data/lib/fleakr/api/value_parameter.rb +36 -0
  11. data/lib/fleakr/api.rb +8 -0
  12. data/lib/fleakr/core_ext/false_class.rb +7 -0
  13. data/lib/fleakr/core_ext/hash.rb +22 -0
  14. data/lib/fleakr/core_ext/true_class.rb +7 -0
  15. data/lib/fleakr/core_ext.rb +3 -0
  16. data/lib/fleakr/objects/authentication_token.rb +60 -0
  17. data/lib/fleakr/objects/comment.rb +49 -0
  18. data/lib/fleakr/objects/contact.rb +31 -0
  19. data/lib/fleakr/objects/error.rb +22 -0
  20. data/lib/fleakr/objects/group.rb +36 -0
  21. data/lib/fleakr/objects/image.rb +50 -0
  22. data/lib/fleakr/objects/photo.rb +147 -0
  23. data/lib/fleakr/objects/photo_context.rb +49 -0
  24. data/lib/fleakr/objects/search.rb +30 -0
  25. data/lib/fleakr/objects/set.rb +50 -0
  26. data/lib/fleakr/objects/tag.rb +56 -0
  27. data/lib/fleakr/objects/user.rb +95 -0
  28. data/lib/fleakr/objects.rb +12 -0
  29. data/lib/fleakr/support/attribute.rb +46 -0
  30. data/lib/fleakr/support/object.rb +110 -0
  31. data/lib/fleakr/support.rb +2 -0
  32. data/lib/fleakr/version.rb +13 -0
  33. data/lib/fleakr.rb +164 -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,35 @@
1
+ module Fleakr
2
+ module Api # :nodoc:
3
+
4
+ # = Parameter
5
+ #
6
+ # Base class for other parameters that get passed to the Flickr API - see
7
+ # #FileParameter and #ValueParameter for examples
8
+ #
9
+ class Parameter
10
+
11
+ attr_reader :name
12
+
13
+ # A new named parameter (never used directly)
14
+ #
15
+ def initialize(name, include_in_signature = true)
16
+ @name = name
17
+ @include_in_signature = include_in_signature
18
+ end
19
+
20
+ # Should this parameter be used when generating the signature?
21
+ #
22
+ def include_in_signature?
23
+ (@include_in_signature == true) ? true : false
24
+ end
25
+
26
+ # Used for sorting when generating a signature
27
+ #
28
+ def <=>(other)
29
+ self.name <=> other.name
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,97 @@
1
+ module Fleakr
2
+ module Api # :nodoc:
3
+
4
+ # = ParameterList
5
+ #
6
+ # Represents a list of parameters that get passed as part of a
7
+ # MethodRequest or UploadRequest. These can be transformed as necessary
8
+ # into query strings (using #to_query) or form data (using #to_form)
9
+ #
10
+ class ParameterList
11
+
12
+ # Create a new parameter list with optional parameters:
13
+ # [:authenticate?] Request will automatically be authenticated if Fleakr.token is available
14
+ # set this to false to force it not to authenticate
15
+ #
16
+ # Any additional name / value pairs will be created as individual
17
+ # ValueParameters as part of the list. Example:
18
+ #
19
+ # >> list = Fleakr::Api::ParameterList.new(:foo => 'bar')
20
+ # => #<Fleakr::Api::ParameterList:0x1656e6c @list=... >
21
+ # >> list[:foo]
22
+ # => #<Fleakr::Api::ValueParameter:0x1656da4 @include_in_signature=true, @name="foo", @value="bar">
23
+ #
24
+ def initialize(options = {})
25
+ # TODO: need to find a way to move the unexpected behavior in Fleakr.token elsewhere
26
+ @api_options = options.extract!(:authenticate?)
27
+
28
+ @list = Hash.new
29
+
30
+ options.each {|k,v| self << ValueParameter.new(k.to_s, v) }
31
+
32
+ self << ValueParameter.new('api_key', Fleakr.api_key)
33
+ self << ValueParameter.new('auth_token', Fleakr.token.value) if authenticate?
34
+ end
35
+
36
+ # Add a new parameter (ValueParameter / FileParameter) to the list
37
+ #
38
+ def <<(parameter)
39
+ @list.merge!(parameter.name => parameter)
40
+ end
41
+
42
+ # Should this parameter list be signed?
43
+ #
44
+ def sign?
45
+ !Fleakr.shared_secret.blank?
46
+ end
47
+
48
+ # Should we send the auth_token with the request?
49
+ #
50
+ def authenticate?
51
+ @api_options.has_key?(:authenticate?) ? @api_options[:authenticate?] : !Fleakr.token.blank?
52
+ end
53
+
54
+ # Access an individual parameter by key (symbol or string)
55
+ #
56
+ def [](key)
57
+ list[key.to_s]
58
+ end
59
+
60
+ def boundary # :nodoc:
61
+ @boundary ||= Digest::MD5.hexdigest(rand.to_s)
62
+ end
63
+
64
+ # Generate the query string representation of this parameter
65
+ # list - e.g. <tt>foo=bar&blee=baz</tt>
66
+ #
67
+ def to_query
68
+ list.values.map {|element| element.to_query }.join('&')
69
+ end
70
+
71
+ # Generate the form representation of this parameter list including the
72
+ # boundary
73
+ #
74
+ def to_form
75
+ form = list.values.map {|p| "--#{self.boundary}\r\n#{p.to_form}" }.join
76
+ form << "--#{self.boundary}--"
77
+
78
+ form
79
+ end
80
+
81
+ def signature # :nodoc:
82
+ parameters_to_sign = @list.values.reject {|p| !p.include_in_signature? }
83
+ signature_text = parameters_to_sign.sort.map {|p| "#{p.name}#{p.value}" }.join
84
+
85
+ Digest::MD5.hexdigest("#{Fleakr.shared_secret}#{signature_text}")
86
+ end
87
+
88
+ private
89
+ def list
90
+ list = @list
91
+ list.merge!('api_sig' => ValueParameter.new('api_sig', signature, false)) if self.sign?
92
+
93
+ list
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,35 @@
1
+ module Fleakr
2
+ module Api # :nodoc:
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::MethodRequest.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,75 @@
1
+ module Fleakr
2
+ module Api # :nodoc:
3
+
4
+ # = UploadRequest
5
+ #
6
+ # This implements the upload functionality of the Flickr API which is needed
7
+ # to create new photos and replace the photo content of existing photos
8
+ #
9
+ class UploadRequest
10
+
11
+ ENDPOINT_URIS = {
12
+ :create => 'http://api.flickr.com/services/upload/',
13
+ :update => 'http://api.flickr.com/services/replace/'
14
+ }
15
+
16
+ attr_reader :parameters, :type
17
+
18
+ # Send a request and return a Response object. If an API error occurs, this raises
19
+ # a Fleakr::ApiError with the reason for the error. See UploadRequest#new for more
20
+ # details.
21
+ #
22
+ def self.with_response!(filename, type = :create, options = {})
23
+ request = self.new(filename, type, options)
24
+ response = request.send
25
+
26
+ raise(Fleakr::ApiError, "Code: #{response.error.code} - #{response.error.message}") if response.error?
27
+
28
+ response
29
+ end
30
+
31
+ # Create a new UploadRequest with the specified filename, type, and options. Type
32
+ # is one of <tt>:create</tt> or <tt>:update</tt> to specify whether we are saving a new
33
+ # image or replacing an existing one.
34
+ #
35
+ # For a list of available options, see the documentation in Fleakr::Objects::Photo
36
+ #
37
+ def initialize(filename, type = :create, options = {})
38
+ @type = type
39
+ @options = options
40
+
41
+ @parameters = ParameterList.new(upload_options)
42
+ @parameters << FileParameter.new('photo', filename)
43
+ end
44
+
45
+ # A list of upload options for this upload request (see Fleakr::Api::Option)
46
+ #
47
+ def upload_options
48
+ option_list = @options.map {|key, value| Option.for(key, value) }
49
+ option_list.inject({}) {|hash, option| hash.merge(option.to_hash)}
50
+ end
51
+
52
+ def headers # :nodoc:
53
+ {'Content-Type' => "multipart/form-data; boundary=#{self.parameters.boundary}"}
54
+ end
55
+
56
+ def send # :nodoc:
57
+ response = Net::HTTP.start(endpoint_uri.host, endpoint_uri.port) do |http|
58
+ logger.info("Sending upload request to: #{endpoint_uri}")
59
+ logger.debug("Request data:\n#{self.parameters.to_form}")
60
+ logger.debug("Request headers:\n#{self.headers.inspect}")
61
+
62
+ http.post(endpoint_uri.path, self.parameters.to_form, self.headers)
63
+ end
64
+ logger.debug("Response data:\n#{response.body}")
65
+ Response.new(response.body)
66
+ end
67
+
68
+ private
69
+ def endpoint_uri
70
+ @endpoint_uri ||= URI.parse(ENDPOINT_URIS[self.type])
71
+ end
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,36 @@
1
+ module Fleakr
2
+ module Api # :nodoc:
3
+
4
+ # = ValueParameter
5
+ #
6
+ # A simple name / value parameter for use in API calls
7
+ #
8
+ class ValueParameter < Parameter
9
+
10
+ attr_reader :value
11
+
12
+ # Create a new parameter with the specified name / value pair.
13
+ #
14
+ def initialize(name, value, include_in_signature = true)
15
+ @value = value
16
+ super(name, include_in_signature)
17
+ end
18
+
19
+ # Generate the query string representation of this parameter.
20
+ #
21
+ def to_query
22
+ "#{self.name}=#{CGI.escape(self.value.to_s)}"
23
+ end
24
+
25
+ # Generate the form representation of this parameter.
26
+ #
27
+ def to_form
28
+ "Content-Disposition: form-data; name=\"#{self.name}\"\r\n" +
29
+ "\r\n" +
30
+ "#{self.value}\r\n"
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+ end
data/lib/fleakr/api.rb ADDED
@@ -0,0 +1,8 @@
1
+ require "fleakr/api/response"
2
+ require "fleakr/api/method_request"
3
+ require "fleakr/api/option"
4
+ require "fleakr/api/upload_request"
5
+ require "fleakr/api/parameter_list"
6
+ require "fleakr/api/parameter"
7
+ require "fleakr/api/value_parameter"
8
+ require "fleakr/api/file_parameter"
@@ -0,0 +1,7 @@
1
+ class FalseClass # :nodoc:
2
+
3
+ def to_i
4
+ 0
5
+ end
6
+
7
+ end
@@ -0,0 +1,22 @@
1
+ class Hash
2
+
3
+ # Extract the matching keys from the source hash and return
4
+ # a new hash with those keys:
5
+ #
6
+ # >> h = {:a => 'b', :c => 'd'}
7
+ # => {:a=>"b", :c=>"d"}
8
+ # >> h.extract!(:a)
9
+ # => {:a=>"b"}
10
+ # >> h
11
+ # => {:c=>"d"}
12
+ #
13
+ def extract!(*keys)
14
+ value = {}
15
+
16
+ keys.each {|k| value.merge!({k => self[k]}) if self.has_key?(k) }
17
+ keys.each {|k| delete(k) }
18
+
19
+ value
20
+ end
21
+
22
+ end
@@ -0,0 +1,7 @@
1
+ class TrueClass # :nodoc:
2
+
3
+ def to_i
4
+ 1
5
+ end
6
+
7
+ end
@@ -0,0 +1,3 @@
1
+ require 'fleakr/core_ext/hash'
2
+ require 'fleakr/core_ext/false_class'
3
+ require 'fleakr/core_ext/true_class'
@@ -0,0 +1,60 @@
1
+ module Fleakr
2
+ module Objects # :nodoc:
3
+
4
+ # = AuthenticationToken
5
+ #
6
+ # This class represents an authentication token used for API calls that
7
+ # require authentication before they can be used
8
+ #
9
+ # == Attributes
10
+ #
11
+ # [value] The token value that is used in subsequent API calls
12
+ # [permissions] The permissions granted to this application (read / write / delete)
13
+ #
14
+ class AuthenticationToken
15
+
16
+ include Fleakr::Support::Object
17
+
18
+ flickr_attribute :value, :from => 'auth/token'
19
+ flickr_attribute :permissions, :from => 'auth/perms'
20
+ flickr_attribute :user_id, :from => 'auth/user@nsid'
21
+ flickr_attribute :user_name, :from => 'auth/user@username'
22
+ flickr_attribute :full_name, :from => 'auth/user@fullname'
23
+
24
+ # Retrieve a full authentication token from the supplied mini-token (e.g. 123-456-789)
25
+ #
26
+ def self.from_mini_token(mini_token)
27
+ from :mini_token, mini_token
28
+ end
29
+
30
+ # Retrieve a full authentication token from the supplied auth_token string
31
+ # (e.g. 45-76598454353455)
32
+ #
33
+ def self.from_auth_token(auth_token)
34
+ from :auth_token, auth_token
35
+ end
36
+
37
+ # Retrieve a full authentication token from the supplied frob
38
+ def self.from_frob(frob)
39
+ from :frob, frob
40
+ end
41
+
42
+ def self.from(thing, value) # :nodoc:
43
+ api_methods = {
44
+ :mini_token => 'getFullToken',
45
+ :auth_token => 'checkToken',
46
+ :frob => 'getToken'
47
+ }
48
+
49
+ method = "auth.#{api_methods[thing]}"
50
+
51
+ parameters = {thing => value, :authenticate? => false}
52
+ response = Fleakr::Api::MethodRequest.with_response!(method, parameters)
53
+
54
+ self.new(response.body)
55
+ end
56
+
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,49 @@
1
+ module Fleakr
2
+ module Objects # :nodoc:
3
+
4
+ # = Comment
5
+ #
6
+ # This class represents a comment that can be associated with a single photo or an
7
+ # entire photoset.
8
+ #
9
+ # == Attributes
10
+ #
11
+ # [id] The unique identifier for this comment
12
+ # [url] The direct URL / permalink to reference this comment
13
+ # [body] The comment itself - also available with <tt>to_s</tt>
14
+ #
15
+ class Comment
16
+
17
+ include Fleakr::Support::Object
18
+
19
+ find_all :by_photo_id, :call => 'photos.comments.getList', :path => 'comments/comment'
20
+ find_all :by_set_id, :using => :photoset_id, :call => 'photosets.comments.getList', :path => 'comments/comment'
21
+
22
+ flickr_attribute :id
23
+ flickr_attribute :author_id, :from => '@author'
24
+ flickr_attribute :created, :from => '@datecreate'
25
+ flickr_attribute :url, :from => '@permalink'
26
+ flickr_attribute :body, :from => '.'
27
+
28
+ # The user who supplied the comment. See Fleakr::Objects::User for more information
29
+ #
30
+ def author
31
+ @author ||= User.find_by_id(author_id)
32
+ end
33
+
34
+ # When was this comment created?
35
+ #
36
+ def created_at
37
+ Time.at(created.to_i)
38
+ end
39
+
40
+ # The contents of the comment - also available as <tt>body</tt>
41
+ #
42
+ def to_s
43
+ body
44
+ end
45
+
46
+ end
47
+
48
+ end
49
+ 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, :from => '@nsid'
8
+ flickr_attribute :username
9
+ flickr_attribute :icon_server, :from => '@iconserver'
10
+ flickr_attribute :icon_farm, :from => '@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::MethodRequest.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, :from => 'err@code'
18
+ flickr_attribute :message, :from => 'err@msg'
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,36 @@
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
+ # == Associations
12
+ #
13
+ # [photos] The photos that are in this group
14
+ #
15
+ class Group
16
+
17
+ include Fleakr::Support::Object
18
+
19
+ flickr_attribute :id, :from => '@nsid'
20
+ flickr_attribute :name
21
+ flickr_attribute :adult_flag, :from => '@eighteenplus'
22
+
23
+ find_all :by_user_id, :call => 'people.getPublicGroups', :path => 'groups/group'
24
+
25
+ has_many :photos
26
+
27
+ scoped_search
28
+
29
+ # Is this group adult-only? (e.g. only 18+ allowed to view)
30
+ def adult?
31
+ (adult_flag == '1')
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,50 @@
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 :width, :height
28
+ flickr_attribute :size, :from => '@label'
29
+ flickr_attribute :url, :from => '@source'
30
+ flickr_attribute :page, :from => '@url'
31
+
32
+ find_all :by_photo_id, :call => 'photos.getSizes', :path => 'sizes/size'
33
+
34
+ # The filename portion of the image (without the full URL)
35
+ def filename
36
+ self.url.match(/([^\/]+)$/)[1]
37
+ end
38
+
39
+ # Save this image to the specified directory or file. If the target is a
40
+ # directory, the file will be created with the original filename from Flickr.
41
+ # If the target is a file, it will be saved with the specified name. In the
42
+ # case that the target file already exists, this method will overwrite it.
43
+ def save_to(target, prefix = nil)
44
+ destination = File.directory?(target) ? "#{target}/#{prefix}#{self.filename}" : "#{target}"
45
+ File.open(destination, 'w') {|f| f << Net::HTTP.get(URI.parse(self.url)) }
46
+ end
47
+
48
+ end
49
+ end
50
+ end