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.
Files changed (54) hide show
  1. data/README.rdoc +88 -57
  2. data/Rakefile +28 -9
  3. data/lib/fleakr.rb +25 -31
  4. data/lib/fleakr/api/option.rb +34 -34
  5. data/lib/fleakr/api/parameter_list.rb +23 -23
  6. data/lib/fleakr/objects.rb +4 -1
  7. data/lib/fleakr/objects/metadata.rb +35 -0
  8. data/lib/fleakr/objects/metadata_collection.rb +36 -0
  9. data/lib/fleakr/objects/photo.rb +15 -11
  10. data/lib/fleakr/objects/set.rb +17 -13
  11. data/lib/fleakr/objects/url.rb +83 -0
  12. data/lib/fleakr/objects/user.rb +8 -0
  13. data/lib/fleakr/support.rb +3 -1
  14. data/lib/fleakr/support/attribute.rb +2 -2
  15. data/lib/fleakr/support/object.rb +44 -29
  16. data/lib/fleakr/support/url_expander.rb +37 -0
  17. data/lib/fleakr/support/utility.rb +66 -0
  18. data/lib/fleakr/version.rb +5 -5
  19. data/test/fixtures/photos.getExif.xml +362 -0
  20. data/test/test_helper.rb +44 -47
  21. data/test/unit/fleakr/api/authentication_request_test.rb +12 -12
  22. data/test/unit/fleakr/api/file_parameter_test.rb +15 -15
  23. data/test/unit/fleakr/api/method_request_test.rb +1 -1
  24. data/test/unit/fleakr/api/option_test.rb +44 -44
  25. data/test/unit/fleakr/api/parameter_list_test.rb +40 -40
  26. data/test/unit/fleakr/api/response_test.rb +10 -10
  27. data/test/unit/fleakr/api/upload_request_test.rb +28 -28
  28. data/test/unit/fleakr/api/value_parameter_test.rb +10 -10
  29. data/test/unit/fleakr/core_ext/false_class_test.rb +5 -5
  30. data/test/unit/fleakr/core_ext/hash_test.rb +9 -9
  31. data/test/unit/fleakr/core_ext/true_class_test.rb +5 -5
  32. data/test/unit/fleakr/objects/authentication_token_test.rb +23 -23
  33. data/test/unit/fleakr/objects/collection_test.rb +23 -23
  34. data/test/unit/fleakr/objects/comment_test.rb +15 -15
  35. data/test/unit/fleakr/objects/contact_test.rb +11 -11
  36. data/test/unit/fleakr/objects/error_test.rb +8 -8
  37. data/test/unit/fleakr/objects/group_test.rb +13 -13
  38. data/test/unit/fleakr/objects/image_test.rb +4 -4
  39. data/test/unit/fleakr/objects/metadata_collection_test.rb +55 -0
  40. data/test/unit/fleakr/objects/metadata_test.rb +27 -0
  41. data/test/unit/fleakr/objects/photo_context_test.rb +23 -23
  42. data/test/unit/fleakr/objects/photo_test.rb +71 -64
  43. data/test/unit/fleakr/objects/search_test.rb +22 -22
  44. data/test/unit/fleakr/objects/set_test.rb +62 -40
  45. data/test/unit/fleakr/objects/tag_test.rb +32 -31
  46. data/test/unit/fleakr/objects/url_test.rb +185 -0
  47. data/test/unit/fleakr/objects/user_test.rb +43 -16
  48. data/test/unit/fleakr/support/attribute_test.rb +15 -15
  49. data/test/unit/fleakr/support/object_test.rb +77 -33
  50. data/test/unit/fleakr/support/request_test.rb +12 -12
  51. data/test/unit/fleakr/support/url_expander_test.rb +44 -0
  52. data/test/unit/fleakr/support/utility_test.rb +70 -0
  53. data/test/unit/fleakr_test.rb +48 -41
  54. metadata +43 -23
@@ -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}/#{self.title}"
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
@@ -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
@@ -1,3 +1,5 @@
1
1
  require 'fleakr/support/attribute'
2
2
  require 'fleakr/support/object'
3
- require 'fleakr/support/request'
3
+ require 'fleakr/support/request'
4
+ require 'fleakr/support/url_expander'
5
+ require 'fleakr/support/utility'
@@ -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 location.blank?
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| v.blank?}.first unless node.nil?
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 = names_and_options.extract_options!
12
+ attributes, options = Utility.extract_options(names_and_options)
13
13
 
14
- names_and_options.each do |name|
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 = "Fleakr::Objects::#{attribute.to_s.classify}"
25
- finder_attribute = "#{class_name.demodulize.underscore}_id"
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
- @#{attribute} ||= #{target}.send("find_all_by_#{finder_attribute}".to_sym, self.id, self.authentication_options)
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 = "#{self.name.demodulize.underscore.downcase}_id".to_sym
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 = attributes.extract_options!
81
+ attributes, options = Utility.extract_options(attributes)
78
82
 
79
83
  attributes.each do |attribute|
80
84
  class_eval <<-CODE
81
- def #{attribute}_with_loading
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