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
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,47 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Api # :nodoc:
|
3
|
+
|
4
|
+
# = FileParameter
|
5
|
+
#
|
6
|
+
# Parameter class to encapsulate file data sent to the Flickr upload API
|
7
|
+
#
|
8
|
+
class FileParameter < Parameter
|
9
|
+
|
10
|
+
MIME_TYPES = {
|
11
|
+
'.jpg' => 'image/jpeg',
|
12
|
+
'.png' => 'image/png',
|
13
|
+
'.gif' => 'image/gif'
|
14
|
+
}
|
15
|
+
|
16
|
+
# Create a parameter with name and specified filename
|
17
|
+
#
|
18
|
+
def initialize(name, filename)
|
19
|
+
@filename = filename
|
20
|
+
super(name, false)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Discover MIME type by file extension using MIME_TYPES constant
|
24
|
+
#
|
25
|
+
def mime_type
|
26
|
+
MIME_TYPES[File.extname(@filename)]
|
27
|
+
end
|
28
|
+
|
29
|
+
# File data (from @filename) to pass to the Flickr API
|
30
|
+
#
|
31
|
+
def value
|
32
|
+
@value ||= File.read(@filename)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Generate a form representation of this file for upload (as multipart/form-data)
|
36
|
+
#
|
37
|
+
def to_form
|
38
|
+
"Content-Disposition: form-data; name=\"#{self.name}\"; filename=\"#{@filename}\"\r\n" +
|
39
|
+
"Content-Type: #{self.mime_type}\r\n" +
|
40
|
+
"\r\n" +
|
41
|
+
"#{self.value}\r\n"
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Api # :nodoc:
|
3
|
+
|
4
|
+
# = MethodRequest
|
5
|
+
#
|
6
|
+
# Handles all API requests that are non-upload related. For upload requests see the
|
7
|
+
# UploadRequest class.
|
8
|
+
#
|
9
|
+
class MethodRequest
|
10
|
+
attr_reader :parameters, :method
|
11
|
+
|
12
|
+
# Makes a request to the Flickr API and returns a valid Response object. If
|
13
|
+
# there are errors on the response it will raise an ApiError exception. See
|
14
|
+
# MethodRequest#new for details about the additional parameters
|
15
|
+
#
|
16
|
+
def self.with_response!(method, additional_parameters = {})
|
17
|
+
request = self.new(method, additional_parameters)
|
18
|
+
response = request.send
|
19
|
+
|
20
|
+
raise(Fleakr::ApiError, "Code: #{response.error.code} - #{response.error.message}") if response.error?
|
21
|
+
|
22
|
+
response
|
23
|
+
end
|
24
|
+
|
25
|
+
# Create a new request for the specified API method and pass along any additional
|
26
|
+
# parameters. The Flickr API uses namespacing for its methods - this is optional
|
27
|
+
# when calling this method.
|
28
|
+
#
|
29
|
+
# This must be called after initializing the library with the required API key
|
30
|
+
# see (#Fleakr.api_key=)
|
31
|
+
#
|
32
|
+
# The <tt>additional_parameters</tt> is a list of parameters to pass directly to
|
33
|
+
# the Flickr API call. The exception to this is the <tt>:authenticate?</tt> option
|
34
|
+
# that will force the call to not be authenticated if it is set to false (The default
|
35
|
+
# behavior is to authenticate all calls when we have a token).
|
36
|
+
#
|
37
|
+
def initialize(method, additional_parameters = {})
|
38
|
+
@parameters = ParameterList.new(additional_parameters)
|
39
|
+
|
40
|
+
self.method = method
|
41
|
+
end
|
42
|
+
|
43
|
+
def method=(method) # :nodoc:
|
44
|
+
@method = method.sub(/^(flickr\.)?/, 'flickr.')
|
45
|
+
@parameters << ValueParameter.new('method', @method)
|
46
|
+
end
|
47
|
+
|
48
|
+
def send # :nodoc:
|
49
|
+
logger.info("Sending request to: #{endpoint_uri}")
|
50
|
+
response_xml = Net::HTTP.get(endpoint_uri)
|
51
|
+
logger.debug("Response data:\n#{response_xml}")
|
52
|
+
|
53
|
+
Response.new(response_xml)
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
def endpoint_uri
|
58
|
+
uri = URI.parse('http://api.flickr.com/services/rest/')
|
59
|
+
uri.query = self.parameters.to_query
|
60
|
+
uri
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Api # :nodoc:
|
3
|
+
|
4
|
+
# = Option
|
5
|
+
#
|
6
|
+
# Top-level class for creating specific option instances based on type
|
7
|
+
#
|
8
|
+
class Option
|
9
|
+
|
10
|
+
MAPPING = {
|
11
|
+
:tags => 'TagOption',
|
12
|
+
:viewable_by => 'ViewOption',
|
13
|
+
:level => 'LevelOption',
|
14
|
+
:type => 'TypeOption',
|
15
|
+
:hide? => 'HiddenOption'
|
16
|
+
}
|
17
|
+
|
18
|
+
# Initialize a new option for the specified type and value
|
19
|
+
#
|
20
|
+
def self.for(type, value)
|
21
|
+
class_for(type).new(type, value)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.class_for(type) # :nodoc:
|
25
|
+
class_name = MAPPING[type] || 'SimpleOption'
|
26
|
+
"Fleakr::Api::#{class_name}".constantize
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
# = SimpleOption
|
32
|
+
#
|
33
|
+
# Simple name / value option pair
|
34
|
+
#
|
35
|
+
class SimpleOption
|
36
|
+
|
37
|
+
attr_reader :type, :value
|
38
|
+
|
39
|
+
# Create an option of the specified type and value
|
40
|
+
#
|
41
|
+
def initialize(type, value)
|
42
|
+
@type = type
|
43
|
+
@value = value
|
44
|
+
end
|
45
|
+
|
46
|
+
# Generate hash representation of this option
|
47
|
+
#
|
48
|
+
def to_hash
|
49
|
+
{type => value}
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
# = TagOption
|
55
|
+
#
|
56
|
+
# Represents values for tags
|
57
|
+
#
|
58
|
+
class TagOption < SimpleOption
|
59
|
+
|
60
|
+
# Tag with specified values. Value passed will be converted to an array if it isn't
|
61
|
+
# already
|
62
|
+
#
|
63
|
+
def initialize(type, value)
|
64
|
+
super type, value
|
65
|
+
@value = Array(self.value)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Hash representation of tag values (separated by spaces). Handles multi-word tags
|
69
|
+
# by enclosing each tag in double quotes.
|
70
|
+
#
|
71
|
+
def to_hash
|
72
|
+
tags = value.map {|tag| "\"#{tag}\"" }
|
73
|
+
{type => tags.join(' ')}
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
# = ViewOption
|
79
|
+
#
|
80
|
+
# Specify who is able to view the photo.
|
81
|
+
#
|
82
|
+
class ViewOption < SimpleOption
|
83
|
+
|
84
|
+
# Specify who this is viewable by (e.g. everyone / friends / family).
|
85
|
+
#
|
86
|
+
def initialize(type, value)
|
87
|
+
super type, Array(value)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Is this publicly viewable? (i.e. :everyone)
|
91
|
+
#
|
92
|
+
def public?
|
93
|
+
value == [:everyone]
|
94
|
+
end
|
95
|
+
|
96
|
+
# Is this viewable by friends? (i.e. :friends)
|
97
|
+
#
|
98
|
+
def friends?
|
99
|
+
value.include?(:friends)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Is this viewable by family? (i.e. :family)
|
103
|
+
#
|
104
|
+
def family?
|
105
|
+
value.include?(:family)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Hash representation of photo permissions
|
109
|
+
#
|
110
|
+
def to_hash
|
111
|
+
{:is_public => public?.to_i, :is_friend => friends?.to_i, :is_family => family?.to_i}
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
# = LevelOption
|
117
|
+
#
|
118
|
+
# Specify the "safety level" of this photo (e.g. safe / moderate / restricted)
|
119
|
+
#
|
120
|
+
class LevelOption < SimpleOption
|
121
|
+
|
122
|
+
def value # :nodoc:
|
123
|
+
case @value
|
124
|
+
when :safe then 1
|
125
|
+
when :moderate then 2
|
126
|
+
when :restricted then 3
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Hash representation of the safety_level for this photo
|
131
|
+
#
|
132
|
+
def to_hash
|
133
|
+
{:safety_level => value}
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
# = TypeOption
|
139
|
+
#
|
140
|
+
# Specify the type of this photo (e.g. photo / screenshot / other)
|
141
|
+
#
|
142
|
+
class TypeOption < SimpleOption
|
143
|
+
|
144
|
+
def value # :nodoc:
|
145
|
+
case @value
|
146
|
+
when :photo then 1
|
147
|
+
when :screenshot then 2
|
148
|
+
when :other then 3
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Hash representation of this type
|
153
|
+
#
|
154
|
+
def to_hash
|
155
|
+
{:content_type => value}
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
# = HiddenOption
|
161
|
+
#
|
162
|
+
# Specify whether this photo should be hidden from search
|
163
|
+
#
|
164
|
+
class HiddenOption < SimpleOption
|
165
|
+
|
166
|
+
# Hash representation of whether to hide this photo
|
167
|
+
#
|
168
|
+
def to_hash
|
169
|
+
{:hidden => (value ? 2 : 1)}
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
end
|
@@ -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
|