fleakr 0.6.3 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +88 -57
- data/Rakefile +28 -9
- data/lib/fleakr.rb +25 -31
- data/lib/fleakr/api/option.rb +34 -34
- data/lib/fleakr/api/parameter_list.rb +23 -23
- data/lib/fleakr/objects.rb +4 -1
- data/lib/fleakr/objects/metadata.rb +35 -0
- data/lib/fleakr/objects/metadata_collection.rb +36 -0
- data/lib/fleakr/objects/photo.rb +15 -11
- data/lib/fleakr/objects/set.rb +17 -13
- data/lib/fleakr/objects/url.rb +83 -0
- data/lib/fleakr/objects/user.rb +8 -0
- data/lib/fleakr/support.rb +3 -1
- data/lib/fleakr/support/attribute.rb +2 -2
- data/lib/fleakr/support/object.rb +44 -29
- data/lib/fleakr/support/url_expander.rb +37 -0
- data/lib/fleakr/support/utility.rb +66 -0
- data/lib/fleakr/version.rb +5 -5
- data/test/fixtures/photos.getExif.xml +362 -0
- data/test/test_helper.rb +44 -47
- data/test/unit/fleakr/api/authentication_request_test.rb +12 -12
- data/test/unit/fleakr/api/file_parameter_test.rb +15 -15
- data/test/unit/fleakr/api/method_request_test.rb +1 -1
- data/test/unit/fleakr/api/option_test.rb +44 -44
- data/test/unit/fleakr/api/parameter_list_test.rb +40 -40
- data/test/unit/fleakr/api/response_test.rb +10 -10
- data/test/unit/fleakr/api/upload_request_test.rb +28 -28
- data/test/unit/fleakr/api/value_parameter_test.rb +10 -10
- data/test/unit/fleakr/core_ext/false_class_test.rb +5 -5
- data/test/unit/fleakr/core_ext/hash_test.rb +9 -9
- data/test/unit/fleakr/core_ext/true_class_test.rb +5 -5
- data/test/unit/fleakr/objects/authentication_token_test.rb +23 -23
- data/test/unit/fleakr/objects/collection_test.rb +23 -23
- data/test/unit/fleakr/objects/comment_test.rb +15 -15
- data/test/unit/fleakr/objects/contact_test.rb +11 -11
- data/test/unit/fleakr/objects/error_test.rb +8 -8
- data/test/unit/fleakr/objects/group_test.rb +13 -13
- data/test/unit/fleakr/objects/image_test.rb +4 -4
- data/test/unit/fleakr/objects/metadata_collection_test.rb +55 -0
- data/test/unit/fleakr/objects/metadata_test.rb +27 -0
- data/test/unit/fleakr/objects/photo_context_test.rb +23 -23
- data/test/unit/fleakr/objects/photo_test.rb +71 -64
- data/test/unit/fleakr/objects/search_test.rb +22 -22
- data/test/unit/fleakr/objects/set_test.rb +62 -40
- data/test/unit/fleakr/objects/tag_test.rb +32 -31
- data/test/unit/fleakr/objects/url_test.rb +185 -0
- data/test/unit/fleakr/objects/user_test.rb +43 -16
- data/test/unit/fleakr/support/attribute_test.rb +15 -15
- data/test/unit/fleakr/support/object_test.rb +77 -33
- data/test/unit/fleakr/support/request_test.rb +12 -12
- data/test/unit/fleakr/support/url_expander_test.rb +44 -0
- data/test/unit/fleakr/support/utility_test.rb +70 -0
- data/test/unit/fleakr_test.rb +48 -41
- metadata +43 -23
data/lib/fleakr/api/option.rb
CHANGED
@@ -6,7 +6,7 @@ module Fleakr
|
|
6
6
|
# Top-level class for creating specific option instances based on type
|
7
7
|
#
|
8
8
|
class Option
|
9
|
-
|
9
|
+
|
10
10
|
MAPPING = {
|
11
11
|
:tags => 'TagOption',
|
12
12
|
:viewable_by => 'ViewOption',
|
@@ -14,85 +14,85 @@ module Fleakr
|
|
14
14
|
:type => 'TypeOption',
|
15
15
|
:hide? => 'HiddenOption'
|
16
16
|
}
|
17
|
-
|
17
|
+
|
18
18
|
# Initialize a new option for the specified type and value
|
19
19
|
#
|
20
20
|
def self.for(type, value)
|
21
21
|
class_for(type).new(type, value)
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
def self.class_for(type) # :nodoc:
|
25
25
|
class_name = MAPPING[type] || 'SimpleOption'
|
26
|
-
|
26
|
+
Fleakr::Api.const_get(class_name)
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
# = SimpleOption
|
32
|
-
#
|
32
|
+
#
|
33
33
|
# Simple name / value option pair
|
34
34
|
#
|
35
35
|
class SimpleOption
|
36
36
|
|
37
37
|
attr_reader :type, :value
|
38
|
-
|
38
|
+
|
39
39
|
# Create an option of the specified type and value
|
40
40
|
#
|
41
41
|
def initialize(type, value)
|
42
42
|
@type = type
|
43
43
|
@value = value
|
44
44
|
end
|
45
|
-
|
45
|
+
|
46
46
|
# Generate hash representation of this option
|
47
47
|
#
|
48
48
|
def to_hash
|
49
49
|
{type => value}
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
52
|
end
|
53
|
-
|
53
|
+
|
54
54
|
# = TagOption
|
55
55
|
#
|
56
56
|
# Represents values for tags
|
57
57
|
#
|
58
58
|
class TagOption < SimpleOption
|
59
|
-
|
59
|
+
|
60
60
|
# Tag with specified values. Value passed will be converted to an array if it isn't
|
61
61
|
# already
|
62
|
-
#
|
62
|
+
#
|
63
63
|
def initialize(type, value)
|
64
64
|
super type, value
|
65
65
|
@value = Array(self.value)
|
66
66
|
end
|
67
|
-
|
68
|
-
# Hash representation of tag values (separated by spaces). Handles multi-word tags
|
67
|
+
|
68
|
+
# Hash representation of tag values (separated by spaces). Handles multi-word tags
|
69
69
|
# by enclosing each tag in double quotes.
|
70
70
|
#
|
71
71
|
def to_hash
|
72
72
|
tags = value.map {|tag| "\"#{tag}\"" }
|
73
73
|
{type => tags.join(' ')}
|
74
74
|
end
|
75
|
-
|
75
|
+
|
76
76
|
end
|
77
|
-
|
77
|
+
|
78
78
|
# = ViewOption
|
79
79
|
#
|
80
80
|
# Specify who is able to view the photo.
|
81
81
|
#
|
82
82
|
class ViewOption < SimpleOption
|
83
|
-
|
83
|
+
|
84
84
|
# Specify who this is viewable by (e.g. everyone / friends / family).
|
85
85
|
#
|
86
86
|
def initialize(type, value)
|
87
87
|
super type, Array(value)
|
88
88
|
end
|
89
|
-
|
89
|
+
|
90
90
|
# Is this publicly viewable? (i.e. :everyone)
|
91
91
|
#
|
92
92
|
def public?
|
93
93
|
value == [:everyone]
|
94
94
|
end
|
95
|
-
|
95
|
+
|
96
96
|
# Is this viewable by friends? (i.e. :friends)
|
97
97
|
#
|
98
98
|
def friends?
|
@@ -104,43 +104,43 @@ module Fleakr
|
|
104
104
|
def family?
|
105
105
|
value.include?(:family)
|
106
106
|
end
|
107
|
-
|
107
|
+
|
108
108
|
# Hash representation of photo permissions
|
109
109
|
#
|
110
110
|
def to_hash
|
111
111
|
{:is_public => public?.to_i, :is_friend => friends?.to_i, :is_family => family?.to_i}
|
112
112
|
end
|
113
|
-
|
113
|
+
|
114
114
|
end
|
115
|
-
|
115
|
+
|
116
116
|
# = LevelOption
|
117
117
|
#
|
118
118
|
# Specify the "safety level" of this photo (e.g. safe / moderate / restricted)
|
119
119
|
#
|
120
120
|
class LevelOption < SimpleOption
|
121
|
-
|
122
|
-
def value # :nodoc:
|
121
|
+
|
122
|
+
def value # :nodoc:
|
123
123
|
case @value
|
124
124
|
when :safe then 1
|
125
125
|
when :moderate then 2
|
126
126
|
when :restricted then 3
|
127
127
|
end
|
128
128
|
end
|
129
|
-
|
129
|
+
|
130
130
|
# Hash representation of the safety_level for this photo
|
131
131
|
#
|
132
132
|
def to_hash
|
133
133
|
{:safety_level => value}
|
134
134
|
end
|
135
|
-
|
135
|
+
|
136
136
|
end
|
137
|
-
|
137
|
+
|
138
138
|
# = TypeOption
|
139
139
|
#
|
140
140
|
# Specify the type of this photo (e.g. photo / screenshot / other)
|
141
141
|
#
|
142
142
|
class TypeOption < SimpleOption
|
143
|
-
|
143
|
+
|
144
144
|
def value # :nodoc:
|
145
145
|
case @value
|
146
146
|
when :photo then 1
|
@@ -148,27 +148,27 @@ module Fleakr
|
|
148
148
|
when :other then 3
|
149
149
|
end
|
150
150
|
end
|
151
|
-
|
151
|
+
|
152
152
|
# Hash representation of this type
|
153
153
|
#
|
154
154
|
def to_hash
|
155
155
|
{:content_type => value}
|
156
156
|
end
|
157
|
-
|
157
|
+
|
158
158
|
end
|
159
|
-
|
159
|
+
|
160
160
|
# = HiddenOption
|
161
161
|
#
|
162
162
|
# Specify whether this photo should be hidden from search
|
163
163
|
#
|
164
164
|
class HiddenOption < SimpleOption
|
165
|
-
|
165
|
+
|
166
166
|
# Hash representation of whether to hide this photo
|
167
167
|
#
|
168
168
|
def to_hash
|
169
169
|
{:hidden => (value ? 2 : 1)}
|
170
170
|
end
|
171
|
-
|
171
|
+
|
172
172
|
end
|
173
173
|
|
174
174
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Fleakr
|
2
2
|
module Api # :nodoc:
|
3
|
-
|
3
|
+
|
4
4
|
# = ParameterList
|
5
5
|
#
|
6
6
|
# Represents a list of parameters that get passed as part of a
|
@@ -8,13 +8,13 @@ module Fleakr
|
|
8
8
|
# into query strings (using #to_query) or form data (using #to_form)
|
9
9
|
#
|
10
10
|
class ParameterList
|
11
|
-
|
11
|
+
|
12
12
|
attr_reader :upload_options # :nodoc:
|
13
|
-
|
13
|
+
|
14
14
|
# Create a new parameter list with optional parameters:
|
15
15
|
#
|
16
16
|
# list = Fleakr::Api::ParameterList.new(:username => 'reagent')
|
17
|
-
#
|
17
|
+
#
|
18
18
|
# You can also disable the sending of the authentication token using
|
19
19
|
# the second parameter:
|
20
20
|
#
|
@@ -27,35 +27,35 @@ module Fleakr
|
|
27
27
|
end
|
28
28
|
|
29
29
|
# Should we send an authentication token as part of this list of parameters?
|
30
|
-
# By default this is true if the token is available as a global value or if
|
30
|
+
# By default this is true if the token is available as a global value or if
|
31
31
|
# the :auth_token key/value is part of the initial list. You can override this
|
32
32
|
# in the constructor.
|
33
|
-
#
|
33
|
+
#
|
34
34
|
def send_authentication_token?
|
35
35
|
@send_authentication_token && !authentication_token.nil?
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
# The default options to send as part of the parameter list, defaults to
|
39
39
|
# sending the API key
|
40
40
|
#
|
41
41
|
def default_options
|
42
42
|
{:api_key => Fleakr.api_key}
|
43
43
|
end
|
44
|
-
|
44
|
+
|
45
45
|
# Should this parameter list be signed? This will be true if Fleakr.shared_secret
|
46
46
|
# is set, false if not.
|
47
47
|
#
|
48
48
|
def sign?
|
49
|
-
!Fleakr.
|
49
|
+
!Fleakr::Support::Utility.blank?(Fleakr.shared_secret)
|
50
50
|
end
|
51
|
-
|
52
|
-
# Generate the query string representation of this parameter
|
51
|
+
|
52
|
+
# Generate the query string representation of this parameter
|
53
53
|
# list - e.g. <tt>foo=bar&blee=baz</tt>
|
54
54
|
#
|
55
55
|
def to_query
|
56
56
|
list.map {|element| element.to_query }.join('&')
|
57
57
|
end
|
58
|
-
|
58
|
+
|
59
59
|
# Generate the form representation of this parameter list including the
|
60
60
|
# boundary
|
61
61
|
#
|
@@ -65,54 +65,54 @@ module Fleakr
|
|
65
65
|
|
66
66
|
form
|
67
67
|
end
|
68
|
-
|
68
|
+
|
69
69
|
# Retrieve the authentication token from either the list of parameters
|
70
70
|
# or the global value (e.g. Fleakr.auth_token)
|
71
71
|
#
|
72
72
|
def authentication_token
|
73
73
|
Fleakr.auth_token.nil? ? @options[:auth_token] : Fleakr.auth_token
|
74
74
|
end
|
75
|
-
|
75
|
+
|
76
76
|
# Add an option to the list that should be sent with a request.
|
77
77
|
#
|
78
78
|
def add_option(name, value)
|
79
79
|
@options.merge!(name => value)
|
80
80
|
end
|
81
|
-
|
81
|
+
|
82
82
|
# Add an option that should be sent with an upload request.
|
83
83
|
#
|
84
84
|
def add_upload_option(name, value)
|
85
85
|
@upload_options.merge!(name => value)
|
86
86
|
end
|
87
|
-
|
87
|
+
|
88
88
|
def options # :nodoc:
|
89
89
|
options = default_options.merge(@options)
|
90
90
|
options.merge!(:auth_token => authentication_token) if send_authentication_token?
|
91
|
-
|
91
|
+
|
92
92
|
options
|
93
93
|
end
|
94
|
-
|
94
|
+
|
95
95
|
def boundary # :nodoc:
|
96
96
|
@boundary ||= Digest::MD5.hexdigest(rand.to_s)
|
97
97
|
end
|
98
|
-
|
98
|
+
|
99
99
|
def signature # :nodoc:
|
100
100
|
sorted_options = options_without_signature.sort {|a,b| a[0].to_s <=> b[0].to_s }
|
101
101
|
signature_text = sorted_options.map {|o| "#{o[0]}#{o[1]}" }.join
|
102
102
|
|
103
103
|
Digest::MD5.hexdigest("#{Fleakr.shared_secret}#{signature_text}")
|
104
104
|
end
|
105
|
-
|
105
|
+
|
106
106
|
def options_without_signature # :nodoc:
|
107
107
|
options.reject {|k,v| k.to_s == 'api_sig'}
|
108
108
|
end
|
109
|
-
|
109
|
+
|
110
110
|
def options_with_signature # :nodoc:
|
111
111
|
options_without_signature.merge(:api_sig => signature)
|
112
112
|
end
|
113
|
-
|
113
|
+
|
114
114
|
def list # :nodoc:
|
115
|
-
options_for_list = sign? ? options_with_signature : options_without_signature
|
115
|
+
options_for_list = sign? ? options_with_signature : options_without_signature
|
116
116
|
value_parameters = options_for_list.map {|k,v| ValueParameter.new(k, v) }
|
117
117
|
file_parameters = upload_options.map {|k,v| FileParameter.new(k, v) }
|
118
118
|
|
data/lib/fleakr/objects.rb
CHANGED
@@ -2,6 +2,8 @@ require 'fleakr/objects/authentication_token'
|
|
2
2
|
require 'fleakr/objects/collection'
|
3
3
|
require 'fleakr/objects/contact'
|
4
4
|
require 'fleakr/objects/error'
|
5
|
+
require 'fleakr/objects/metadata_collection'
|
6
|
+
require 'fleakr/objects/metadata'
|
5
7
|
require 'fleakr/objects/group'
|
6
8
|
require 'fleakr/objects/image'
|
7
9
|
require 'fleakr/objects/photo_context'
|
@@ -10,4 +12,5 @@ require 'fleakr/objects/comment'
|
|
10
12
|
require 'fleakr/objects/tag'
|
11
13
|
require 'fleakr/objects/search'
|
12
14
|
require 'fleakr/objects/set'
|
13
|
-
require 'fleakr/objects/user'
|
15
|
+
require 'fleakr/objects/user'
|
16
|
+
require 'fleakr/objects/url'
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Objects
|
3
|
+
|
4
|
+
# = Metadata
|
5
|
+
#
|
6
|
+
# This class wraps the functionality of EXIF data extraction. It's called
|
7
|
+
# by the Fleakr::Objects::Photo class to obtain EXIF values
|
8
|
+
#
|
9
|
+
# Example:
|
10
|
+
#
|
11
|
+
# user = Fleakr.user('brownout')
|
12
|
+
# user.photos.first.metadata
|
13
|
+
#
|
14
|
+
# == Attributes
|
15
|
+
#
|
16
|
+
# [raw] The raw value returned by Flickr (e.g. 1/500 for ExposureTime)
|
17
|
+
# [clean] The clean value returned by Flickr for the tag (e.g. 0.0.02 (1/500) for ExposureTime)
|
18
|
+
# [tagspace] The tag space of the EXIF data, (e.g. TIFF, ExifIFD, Composite)
|
19
|
+
# [tag] The label as a tag, (e.g. ExposureProgram)
|
20
|
+
# [label] The label for the tag, (e.g. 'Exposure Program')
|
21
|
+
#
|
22
|
+
|
23
|
+
class Metadata
|
24
|
+
|
25
|
+
include Fleakr::Support::Object
|
26
|
+
|
27
|
+
flickr_attribute :tagspace, :tag, :label
|
28
|
+
flickr_attribute :raw, :from => 'raw'
|
29
|
+
flickr_attribute :clean, :from => 'clean'
|
30
|
+
|
31
|
+
find_all :by_photo_id, :call => 'photos.getExif', :path => 'photo/exif'
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Objects
|
3
|
+
|
4
|
+
class MetadataCollection
|
5
|
+
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
attr_reader :photo
|
9
|
+
|
10
|
+
def initialize(photo)
|
11
|
+
@photo = photo
|
12
|
+
end
|
13
|
+
|
14
|
+
def data
|
15
|
+
@data ||= begin
|
16
|
+
elements = Metadata.find_all_by_photo_id(photo.id)
|
17
|
+
elements.inject({}) {|c, e| c.merge(e.label => e) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def keys
|
22
|
+
data.keys
|
23
|
+
end
|
24
|
+
|
25
|
+
def [](key)
|
26
|
+
data[key].raw
|
27
|
+
end
|
28
|
+
|
29
|
+
def each(&block)
|
30
|
+
data.each(&block)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
data/lib/fleakr/objects/photo.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
module Fleakr
|
2
2
|
module Objects # :nodoc:
|
3
|
-
|
3
|
+
|
4
4
|
# = Photo
|
5
5
|
#
|
6
|
-
# Handles both the retrieval of Photo objects from various associations (e.g. User / Set) as
|
6
|
+
# Handles both the retrieval of Photo objects from various associations (e.g. User / Set) as
|
7
7
|
# well as the ability to upload images to the Flickr site.
|
8
|
-
#
|
8
|
+
#
|
9
9
|
# == Attributes
|
10
10
|
#
|
11
11
|
# [id] The ID for this photo
|
@@ -22,7 +22,7 @@ module Fleakr
|
|
22
22
|
# [large] The large representation of this photo
|
23
23
|
# [original] The original photo
|
24
24
|
# [previous] The previous photo based on the current context
|
25
|
-
# [next] The next photo based on the current context
|
25
|
+
# [next] The next photo based on the current context
|
26
26
|
#
|
27
27
|
# == Associations
|
28
28
|
#
|
@@ -57,13 +57,17 @@ module Fleakr
|
|
57
57
|
find_all :by_set_id, :using => :photoset_id, :call => 'photosets.getPhotos', :path => 'photoset/photo'
|
58
58
|
find_all :by_user_id, :call => 'people.getPublicPhotos', :path => 'photos/photo'
|
59
59
|
find_all :by_group_id, :call => 'groups.pools.getPhotos', :path => 'photos/photo'
|
60
|
-
|
60
|
+
|
61
61
|
find_one :by_id, :using => :photo_id, :call => 'photos.getInfo'
|
62
|
-
|
62
|
+
|
63
63
|
lazily_load :posted, :taken, :updated, :comment_count, :url, :description, :with => :load_info
|
64
|
-
|
64
|
+
|
65
65
|
has_many :images, :tags, :comments
|
66
66
|
|
67
|
+
def metadata
|
68
|
+
@metadata ||= MetadataCollection.new(self)
|
69
|
+
end
|
70
|
+
|
67
71
|
# Upload the photo specified by <tt>filename</tt> to the user's Flickr account. When uploading,
|
68
72
|
# there are several options available (none are required):
|
69
73
|
#
|
@@ -85,7 +89,7 @@ module Fleakr
|
|
85
89
|
Photo.find_by_id(photo.id)
|
86
90
|
end
|
87
91
|
|
88
|
-
# Replace the current photo's image with the one specified by filename. This
|
92
|
+
# Replace the current photo's image with the one specified by filename. This
|
89
93
|
# call requires authentication.
|
90
94
|
#
|
91
95
|
def replace_with(filename)
|
@@ -108,7 +112,7 @@ module Fleakr
|
|
108
112
|
end
|
109
113
|
|
110
114
|
# The user who uploaded this photo. See Fleakr::Objects::User for additional information.
|
111
|
-
#
|
115
|
+
#
|
112
116
|
def owner
|
113
117
|
@owner ||= User.find_by_id(owner_id)
|
114
118
|
end
|
@@ -118,13 +122,13 @@ module Fleakr
|
|
118
122
|
def posted_at
|
119
123
|
Time.at(posted.to_i)
|
120
124
|
end
|
121
|
-
|
125
|
+
|
122
126
|
# When was this photo taken?
|
123
127
|
#
|
124
128
|
def taken_at
|
125
129
|
Time.parse(taken)
|
126
130
|
end
|
127
|
-
|
131
|
+
|
128
132
|
# When was this photo last updated? This includes addition of tags and other metadata.
|
129
133
|
#
|
130
134
|
def updated_at
|