fleakr 0.6.3 → 0.7.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 +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/objects/set.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
module Fleakr
|
2
2
|
module Objects # :nodoc:
|
3
|
-
|
3
|
+
|
4
4
|
# = Set
|
5
5
|
#
|
6
6
|
# == Attributes
|
7
|
-
#
|
7
|
+
#
|
8
8
|
# [id] The ID for this photoset
|
9
9
|
# [title] The title of this photoset
|
10
10
|
# [description] The description of this set
|
11
11
|
# [count] Count of photos in this set
|
12
|
-
#
|
12
|
+
#
|
13
13
|
# == Associations
|
14
|
-
#
|
14
|
+
#
|
15
15
|
# [photos] The collection of photos for this set. See Fleakr::Objects::Photo
|
16
16
|
# [comments] All comments associated with this set. See Fleakr::Objects::Comment
|
17
17
|
#
|
@@ -27,18 +27,18 @@ module Fleakr
|
|
27
27
|
flickr_attribute :user_id, :from => '@owner'
|
28
28
|
|
29
29
|
find_all :by_user_id, :call => 'photosets.getList', :path => 'photosets/photoset'
|
30
|
-
|
30
|
+
|
31
31
|
find_one :by_id, :using => :photoset_id, :call => 'photosets.getInfo', :path => 'photoset'
|
32
32
|
|
33
33
|
lazily_load :user_id, :with => :load_info
|
34
34
|
|
35
35
|
# Save all photos in this set to the specified directory for the specified size. Allowed
|
36
|
-
# Sizes include <tt>:square</tt>, <tt>:small</tt>, <tt>:thumbnail</tt>, <tt>:medium</tt>,
|
37
|
-
# <tt>:large</tt>, and <tt>:original</tt>. When saving the set, this method will create
|
36
|
+
# Sizes include <tt>:square</tt>, <tt>:small</tt>, <tt>:thumbnail</tt>, <tt>:medium</tt>,
|
37
|
+
# <tt>:large</tt>, and <tt>:original</tt>. When saving the set, this method will create
|
38
38
|
# a subdirectory based on the set's title.
|
39
39
|
#
|
40
40
|
def save_to(path, size)
|
41
|
-
target = "#{path}/#{
|
41
|
+
target = "#{path}/#{folder_name}"
|
42
42
|
FileUtils.mkdir(target) unless File.exist?(target)
|
43
43
|
|
44
44
|
self.photos.each_with_index do |photo, index|
|
@@ -46,34 +46,38 @@ module Fleakr
|
|
46
46
|
image.save_to(target, file_prefix(index)) unless image.nil?
|
47
47
|
end
|
48
48
|
end
|
49
|
-
|
49
|
+
|
50
50
|
def file_prefix(index) # :nodoc:
|
51
51
|
sprintf("%0#{self.count.length}d_", (index + 1))
|
52
52
|
end
|
53
53
|
|
54
|
+
def folder_name # :nodoc:
|
55
|
+
title.gsub("/", ' ').squeeze(' ')
|
56
|
+
end
|
57
|
+
|
54
58
|
# Primary photo for this set. See Fleakr::Objects::Photo for more details.
|
55
59
|
#
|
56
60
|
def primary_photo
|
57
61
|
@primary_photo ||= Photo.find_by_id(primary_photo_id)
|
58
62
|
end
|
59
|
-
|
63
|
+
|
60
64
|
# The URL for this set.
|
61
65
|
#
|
62
66
|
def url
|
63
67
|
"http://www.flickr.com/photos/#{user_id}/sets/#{id}/"
|
64
68
|
end
|
65
|
-
|
69
|
+
|
66
70
|
# The user who created this set.
|
67
71
|
#
|
68
72
|
def user
|
69
73
|
User.find_by_id(user_id)
|
70
74
|
end
|
71
|
-
|
75
|
+
|
72
76
|
def load_info # :nodoc:
|
73
77
|
response = Fleakr::Api::MethodRequest.with_response!('photosets.getInfo', :photoset_id => self.id)
|
74
78
|
self.populate_from(response.body)
|
75
79
|
end
|
76
|
-
|
80
|
+
|
77
81
|
end
|
78
82
|
end
|
79
83
|
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Objects
|
3
|
+
|
4
|
+
class Url
|
5
|
+
|
6
|
+
def initialize(url)
|
7
|
+
@url = url
|
8
|
+
end
|
9
|
+
|
10
|
+
def path
|
11
|
+
expanded_path || original_path
|
12
|
+
end
|
13
|
+
|
14
|
+
def user_identifier
|
15
|
+
(resource_type == Set) ? parts[1] : parts[2]
|
16
|
+
end
|
17
|
+
|
18
|
+
def shortened?
|
19
|
+
!original_path.match(%r{^/p/[123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ]+$}).nil?
|
20
|
+
end
|
21
|
+
|
22
|
+
def resource_identifier
|
23
|
+
parts[3]
|
24
|
+
end
|
25
|
+
|
26
|
+
def user
|
27
|
+
@user ||= User.find_by_identifier(user_identifier)
|
28
|
+
end
|
29
|
+
|
30
|
+
def resource_type
|
31
|
+
if parts[1] == 'people'
|
32
|
+
User
|
33
|
+
elsif parts[1] == 'photos'
|
34
|
+
Photo
|
35
|
+
elsif parts[2] == 'sets'
|
36
|
+
Set
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def collection?
|
41
|
+
resource_identifier.nil?
|
42
|
+
end
|
43
|
+
|
44
|
+
def resource
|
45
|
+
if resource_type == User
|
46
|
+
user
|
47
|
+
else
|
48
|
+
collection? ? resource_type.find_all_by_user_id(user.id) : resource_type.find_by_id(resource_identifier)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def expanded_path
|
55
|
+
Fleakr::Support::UrlExpander.expand(@url) if shortened?
|
56
|
+
end
|
57
|
+
|
58
|
+
def original_path
|
59
|
+
URI.parse(@url).path
|
60
|
+
end
|
61
|
+
|
62
|
+
def parts
|
63
|
+
path.match(matching_pattern)
|
64
|
+
end
|
65
|
+
|
66
|
+
def matching_pattern
|
67
|
+
@matching_pattern ||= patterns.detect {|p| path.match(p) }
|
68
|
+
end
|
69
|
+
|
70
|
+
def patterns
|
71
|
+
[
|
72
|
+
%r{^/photos/([^/]+)/(sets)/(\d+)},
|
73
|
+
%r{^/photos/([^/]+)/(sets)},
|
74
|
+
%r{^/(photos)/([^/]+)/(\d+)},
|
75
|
+
%r{^/(people)/([^/]+)},
|
76
|
+
%r{^/(photos)/([^/]+)}
|
77
|
+
]
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
data/lib/fleakr/objects/user.rb
CHANGED
@@ -68,6 +68,14 @@ module Fleakr
|
|
68
68
|
|
69
69
|
scoped_search
|
70
70
|
|
71
|
+
def self.user_id?(username_or_user_id)
|
72
|
+
(username_or_user_id =~ /^\d+@N\d{2}$/) ? true : false
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.find_by_identifier(identifier)
|
76
|
+
user_id?(identifier) ? find_by_id(identifier) : find_by_username(identifier)
|
77
|
+
end
|
78
|
+
|
71
79
|
# Is this a pro account?
|
72
80
|
def pro?
|
73
81
|
(self.pro.to_i == 0) ? false : true
|
data/lib/fleakr/support.rb
CHANGED
@@ -15,7 +15,7 @@ module Fleakr
|
|
15
15
|
|
16
16
|
def split(source)
|
17
17
|
location, attribute = source.split('@')
|
18
|
-
location = self.name.to_s if
|
18
|
+
location = self.name.to_s if Utility.blank?(location)
|
19
19
|
|
20
20
|
[location, attribute]
|
21
21
|
end
|
@@ -36,7 +36,7 @@ module Fleakr
|
|
36
36
|
def value_from(document)
|
37
37
|
values = sources.map do |source|
|
38
38
|
node = node_for(document, source)
|
39
|
-
[node.attributes[attribute(source)], node.inner_text].reject{|v|
|
39
|
+
[node.attributes[attribute(source)], node.inner_text].reject{|v| Utility.blank?(v) }.first unless node.nil?
|
40
40
|
end
|
41
41
|
values.compact.first
|
42
42
|
end
|
@@ -1,72 +1,76 @@
|
|
1
1
|
module Fleakr
|
2
2
|
module Support # :nodoc:all
|
3
3
|
module Object
|
4
|
-
|
4
|
+
|
5
5
|
module ClassMethods
|
6
|
-
|
6
|
+
|
7
7
|
def attributes
|
8
8
|
@attributes ||= []
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
def flickr_attribute(*names_and_options)
|
12
|
-
options =
|
12
|
+
attributes, options = Utility.extract_options(names_and_options)
|
13
13
|
|
14
|
-
|
14
|
+
attributes.each do |name|
|
15
15
|
self.attributes << Attribute.new(name, options[:from])
|
16
16
|
class_eval "attr_accessor :#{name}"
|
17
17
|
end
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
def has_many(*attributes)
|
21
21
|
class_name = self.name
|
22
22
|
|
23
23
|
attributes.each do |attribute|
|
24
|
-
target =
|
25
|
-
finder_attribute =
|
24
|
+
target = Utility.class_name_for('Fleakr::Objects', attribute)
|
25
|
+
finder_attribute = Utility.id_attribute_for(class_name)
|
26
26
|
|
27
27
|
class_eval <<-CODE
|
28
|
-
def #{attribute}
|
29
|
-
|
28
|
+
def #{attribute}(options = {})
|
29
|
+
options = authentication_options.merge(options)
|
30
|
+
sorted_options = options.sort {|a, b| a[0].to_s <=> b[0].to_s }
|
31
|
+
key = '#{attribute}_' + sorted_options.to_s
|
32
|
+
|
33
|
+
associations[key] ||= #{target}.send("find_all_by_#{finder_attribute}".to_sym, self.id, options)
|
30
34
|
end
|
31
35
|
CODE
|
32
36
|
end
|
33
37
|
end
|
34
|
-
|
38
|
+
|
35
39
|
def find_all(condition, options)
|
36
40
|
attribute = options[:using].nil? ? condition.to_s.sub(/^by_/, '') : options[:using]
|
37
41
|
target_class = options[:class_name].nil? ? self.name : "Fleakr::Objects::#{options[:class_name]}"
|
38
|
-
|
42
|
+
|
39
43
|
class_eval <<-CODE
|
40
44
|
def self.find_all_#{condition}(value, options = {})
|
41
45
|
options.merge!(:#{attribute} => value)
|
42
|
-
|
46
|
+
|
43
47
|
response = Fleakr::Api::MethodRequest.with_response!('#{options[:call]}', options)
|
44
48
|
(response.body/'rsp/#{options[:path]}').map {|e| #{target_class}.new(e, options) }
|
45
49
|
end
|
46
50
|
CODE
|
47
51
|
end
|
48
|
-
|
52
|
+
|
49
53
|
def find_one(condition, options)
|
50
54
|
attribute = options[:using].nil? ? condition.to_s.sub(/^by_/, '') : options[:using]
|
51
|
-
|
55
|
+
|
52
56
|
class_eval <<-CODE
|
53
57
|
def self.find_#{condition}(value, options = {})
|
54
58
|
options.merge!(:#{attribute} => value)
|
55
|
-
|
59
|
+
|
56
60
|
response = Fleakr::Api::MethodRequest.with_response!('#{options[:call]}', options)
|
57
61
|
#{self.name}.new(response.body, options)
|
58
62
|
end
|
59
63
|
CODE
|
60
64
|
end
|
61
|
-
|
65
|
+
|
62
66
|
def scoped_search
|
63
|
-
key =
|
67
|
+
key = Utility.id_attribute_for(self.name)
|
64
68
|
|
65
69
|
class_eval <<-CODE
|
66
70
|
def search(*parameters)
|
67
71
|
options = {:#{key} => self.id}
|
68
72
|
options.merge!(self.authentication_options)
|
69
|
-
|
73
|
+
|
70
74
|
parameters << options
|
71
75
|
Fleakr::Objects::Search.new(*parameters).results
|
72
76
|
end
|
@@ -74,30 +78,30 @@ module Fleakr
|
|
74
78
|
end
|
75
79
|
|
76
80
|
def lazily_load(*attributes)
|
77
|
-
options =
|
81
|
+
attributes, options = Utility.extract_options(attributes)
|
78
82
|
|
79
83
|
attributes.each do |attribute|
|
80
84
|
class_eval <<-CODE
|
81
|
-
|
85
|
+
alias_method :#{attribute}_without_loading, :#{attribute}
|
86
|
+
def #{attribute}
|
82
87
|
self.send(:#{options[:with]}) if @#{attribute}.nil?
|
83
88
|
#{attribute}_without_loading
|
84
89
|
end
|
85
|
-
alias_method_chain :#{attribute}, :loading
|
86
90
|
CODE
|
87
91
|
end
|
88
92
|
end
|
89
|
-
|
93
|
+
|
90
94
|
end
|
91
|
-
|
95
|
+
|
92
96
|
module InstanceMethods
|
93
|
-
|
97
|
+
|
94
98
|
attr_reader :document, :authentication_options
|
95
|
-
|
99
|
+
|
96
100
|
def initialize(document = nil, options = {})
|
97
101
|
self.populate_from(document) unless document.nil?
|
98
102
|
@authentication_options = options.extract!(:auth_token)
|
99
103
|
end
|
100
|
-
|
104
|
+
|
101
105
|
def populate_from(document)
|
102
106
|
@document = document
|
103
107
|
self.class.attributes.each do |attribute|
|
@@ -105,14 +109,25 @@ module Fleakr
|
|
105
109
|
self.send("#{attribute.name}=".to_sym, value) unless value.nil?
|
106
110
|
end
|
107
111
|
end
|
108
|
-
|
112
|
+
|
113
|
+
def inspect
|
114
|
+
names = instance_variables.reject {|n| %w(@associations @document).include?(n.to_s) }
|
115
|
+
attributes = names.map {|n| "#{n}=#{instance_variable_get(n).inspect}" }
|
116
|
+
|
117
|
+
"#<#{self.class} #{attributes.join(', ')}>"
|
118
|
+
end
|
119
|
+
|
120
|
+
def associations
|
121
|
+
@associations ||= {}
|
122
|
+
end
|
123
|
+
|
109
124
|
end
|
110
125
|
|
111
126
|
def self.included(other)
|
112
127
|
other.send(:extend, Fleakr::Support::Object::ClassMethods)
|
113
128
|
other.send(:include, Fleakr::Support::Object::InstanceMethods)
|
114
129
|
end
|
115
|
-
|
130
|
+
|
116
131
|
end
|
117
132
|
end
|
118
133
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Support # :nodoc:all
|
3
|
+
|
4
|
+
class UrlExpander
|
5
|
+
|
6
|
+
def self.expand(url)
|
7
|
+
new(url).expanded_path
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(source_url)
|
11
|
+
@source_url = source_url
|
12
|
+
end
|
13
|
+
|
14
|
+
def path_to_expand
|
15
|
+
"/photo.gne?short=#{short_photo_id}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def expanded_path
|
19
|
+
response_headers['location']
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def response_headers
|
25
|
+
response = nil
|
26
|
+
Net::HTTP.start('www.flickr.com', 80) {|c| response = c.head(path_to_expand) }
|
27
|
+
response
|
28
|
+
end
|
29
|
+
|
30
|
+
def short_photo_id
|
31
|
+
@source_url.match(%r{([^/]+)$})[1]
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Support # :nodoc:
|
3
|
+
|
4
|
+
# = Utility
|
5
|
+
#
|
6
|
+
# Helpful utility methods.
|
7
|
+
#
|
8
|
+
class Utility
|
9
|
+
|
10
|
+
# Given a module name and an underscored name, generate the fully
|
11
|
+
# namespaced class name. For example:
|
12
|
+
#
|
13
|
+
# >> Utility.class_name_for('Fleakr::Api', 'method_request')
|
14
|
+
# => "Fleakr::Api::MethodRequest"
|
15
|
+
#
|
16
|
+
def self.class_name_for(module_name, name)
|
17
|
+
class_name = name.to_s.sub(/^(\w)/) {|m| m.upcase }
|
18
|
+
class_name = class_name.sub(/(_(\w))/) {|m| $2.upcase }
|
19
|
+
class_name = class_name.sub(/s$/, '')
|
20
|
+
|
21
|
+
"#{module_name}::#{class_name}"
|
22
|
+
end
|
23
|
+
|
24
|
+
# Given a class name as a string with an optional namespace, generate
|
25
|
+
# an attribute ID parameter suitable for retrieving the ID of an associated
|
26
|
+
# object. For example:
|
27
|
+
#
|
28
|
+
# >> Utility.id_attribute_for('Fleakr::Objects::Set')
|
29
|
+
# => "set_id"
|
30
|
+
#
|
31
|
+
def self.id_attribute_for(class_name)
|
32
|
+
class_name = class_name.match(/([^:]+)$/)[1]
|
33
|
+
class_name.gsub!(/([A-Z])([A-Z][a-z])/, '\1_\2')
|
34
|
+
class_name.gsub!(/([a-z])([A-Z])/, '\1_\2')
|
35
|
+
|
36
|
+
"#{class_name.downcase}_id"
|
37
|
+
end
|
38
|
+
|
39
|
+
# Determine if the passed value is blank. Blank values include nil,
|
40
|
+
# the empty string, and a string with only whitespace.
|
41
|
+
#
|
42
|
+
def self.blank?(object)
|
43
|
+
object.to_s.sub(/\s+/, '') == ''
|
44
|
+
end
|
45
|
+
|
46
|
+
# Extract the options from an array if present and return the new array
|
47
|
+
# and any available options. For example:
|
48
|
+
#
|
49
|
+
# >> Utility.extract_options([:sets, {:using => :key}])
|
50
|
+
# => [[:sets], {:using => :key}]
|
51
|
+
#
|
52
|
+
# Note that this method does not modify the supplied parameter.
|
53
|
+
#
|
54
|
+
def self.extract_options(array_with_possible_options)
|
55
|
+
array = array_with_possible_options.dup
|
56
|
+
|
57
|
+
options = array.pop if array.last.is_a?(Hash)
|
58
|
+
options ||= {}
|
59
|
+
|
60
|
+
[array, options]
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|