flickr-wrapper 0.1.3

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 ADDED
@@ -0,0 +1,7 @@
1
+ A grassroots readonly flickr wrapper - Strictly for convenience
2
+ Not a full API, no groups, no community, who needs friends anyway?
3
+
4
+ If you want a full API, use the `flickr` gem hosted on Rubyforge, its
5
+ old but it was nailed with its 1.0 release.
6
+
7
+ Check the specs for usage
@@ -0,0 +1,16 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "flickr-wrapper"
3
+ s.version = "0.1.3"
4
+ s.date = "2008-06-07"
5
+ s.summary = "A grassroots wrapper for flickr"
6
+ s.email = "ben@germanforblack.com"
7
+ s.homepage = "http://github.com/benschwarz/flickr-wrapper"
8
+ s.description = "A grassroots wrapper around flickr - Strictly for convenience"
9
+ s.authors = ["Ben Schwarz"]
10
+ s.files = ["README", "flickr-wrapper.gemspec", "lib/flickr-wrapper.rb", "lib/flickr-wrapper/base.rb", "lib/flickr-wrapper/machine_tag.rb", "lib/flickr-wrapper/photo.rb", "lib/flickr-wrapper/photoset.rb", "lib/flickr-wrapper/tag.rb", "lib/flickr-wrapper/user.rb", "lib/vendor/parallel.rb"]
11
+ s.require_path = "lib"
12
+
13
+ # Deps
14
+ s.add_dependency("flickr-rest", ">= 0.0.1")
15
+ s.add_dependency("validatable", ">= 1.6.7")
16
+ end
@@ -0,0 +1,21 @@
1
+ # Dependencies
2
+ begin
3
+ require 'minigems'
4
+ rescue LoadError
5
+ require 'rubygems'
6
+ end
7
+
8
+ require 'flickr-rest'
9
+ require 'time'
10
+ require 'validatable'
11
+
12
+ # Concurrency
13
+ require File.join(File.dirname(__FILE__), 'vendor', 'parallel.rb')
14
+
15
+ # Namespace junkie
16
+ module Flickr
17
+ MAX_THREADS = 50
18
+ end
19
+
20
+ # Classes
21
+ %w(base photoset photo tag machine_tag user).each {|r| require File.join(File.dirname(__FILE__), 'flickr-wrapper', r)}
@@ -0,0 +1,30 @@
1
+ class Flickr::Base
2
+ attr_reader :user_id
3
+
4
+ def initialize(user_id)
5
+ @user_id = user_id
6
+ end
7
+
8
+ # Add some convenience class methods, lovley and general; sets, photos, tags and machine tags
9
+
10
+ # List all photo sets
11
+ def sets
12
+ Flickr::PhotoSet.list(self.user_id)
13
+ end
14
+
15
+ # This is a cheat, we're just going to search with no params.
16
+ # Maximum amount of photos flickr will let me return is 500
17
+ def photos
18
+ Flickr::Photo.list(self.user_id)
19
+ end
20
+
21
+ # List all tags
22
+ def tags
23
+ Flickr::Tag.list(self.user_id)
24
+ end
25
+
26
+ # List all machine tags
27
+ def machine_tags
28
+ Flickr::MachineTag.list(self.user_id)
29
+ end
30
+ end
@@ -0,0 +1,24 @@
1
+ class Flickr::MachineTag < Flickr::Base #:nodoc
2
+ class Invalid < StandardError; end;
3
+
4
+ include Validatable
5
+ validates_presence_of :namespace, :predicate, :value
6
+
7
+ attr_accessor :namespace, :predicate, :value
8
+
9
+ def initialize(namespace, predicate, value)
10
+ @namespace, @predicate, @value = namespace, predicate, value
11
+ end
12
+
13
+ def self.list(user_id)
14
+ (Flickr::Query.new(user_id).execute('flickr.tags.getListUser')/:tag).parallel_map(Flickr::MAX_THREADS) {|tag| self.from_s tag.inner_text.to_s }.compact
15
+ end
16
+
17
+ def self.from_s(string)
18
+ new($1, $2, $3) if string =~ /(.*)\:(.*)\=(.*)/
19
+ end
20
+
21
+ def to_s
22
+ "#{namespace}:#{predicate}=#{value}"
23
+ end
24
+ end
@@ -0,0 +1,52 @@
1
+ class Flickr::Photo < Flickr::Base
2
+ class Size < Struct.new :width, :height, :source, :url; end
3
+
4
+ attr_accessor :id, :title, :description, :tags, :machine_tags, :taken, :posted, :caller
5
+
6
+ def initialize(id, title, description)
7
+ @id, @title, @description = id, title, description
8
+ end
9
+
10
+ def self.list(user_id)
11
+ search(user_id)
12
+ end
13
+
14
+ # Find will grab all sizes of images, process the tags and standard attributes of a photo
15
+ def self.find(user_id, id)
16
+ photo_call = Flickr::Query.new(user_id).execute('flickr.photos.getInfo', :photo_id => id)
17
+
18
+ # Set basic attributes
19
+ photo = self.new(id, *%w(title description).map {|a| (photo_call/a).inner_text })
20
+ photo.taken = Time.parse(photo_call.at(:dates)['taken'])
21
+ photo.posted = Time.at(photo_call.at(:dates)['posted'].to_i)
22
+
23
+ # Set tags for photo
24
+ photo.tags = (photo_call/"tag[@machine_tag=0]").map{|tag| Flickr::Tag.new tag['raw'] }
25
+ photo.machine_tags = (photo_call/"tag[@machine_tag=1]").map{|tag| Flickr::MachineTag.from_s tag['raw'] }
26
+
27
+ return photo
28
+ end
29
+
30
+ # Find a collection of photos by text
31
+ # Search options should be hashes with string values or arrays for multiple values
32
+ def self.search(user_id, search_options={})
33
+ parse(user_id, Flickr::Query.new(user_id).execute('flickr.photos.search', search_options))
34
+ end
35
+
36
+ def sizes
37
+ hash = {}
38
+ (Flickr::Query.new(user_id).execute('flickr.photos.getSizes', :photo_id => id)/:size).parallel_each(Flickr::MAX_THREADS) do |size|
39
+ hash[size['label'].downcase.to_sym] = Size.new(*%w(width height source url).map{|a| size[a]})
40
+ end
41
+ return hash
42
+ end
43
+
44
+ private
45
+ # Parse applies Hpricot to the photos and maps them to a Photo class instance
46
+ def self.parse(user_id, collection)
47
+ photos = (collection/:photo)
48
+ photos.empty? ? [] : photos.parallel_map(Flickr::MAX_THREADS) do |photo|
49
+ self.find(user_id, photo[:id])
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,78 @@
1
+ class Flickr::PhotoSet < Flickr::Base
2
+ attr_accessor :id, :title, :description, :photo_count
3
+
4
+ def initialize id, title, description, photo_count
5
+ @id, @title, @description, @photo_count = id, title, description, photo_count
6
+ end
7
+
8
+ # Class methods require scope of the user, it is to be sent into the method
9
+ def self.list user_id
10
+ query = Flickr::Query.new user_id
11
+ parse(query.execute('flickr.photosets.getList'))
12
+ end
13
+
14
+ # Get information regarding set by searching with the set's ID
15
+ def self.find user_id, id
16
+ # Keep scope of the user that made the call
17
+ @user_id = user_id
18
+
19
+ query = Flickr::Query.new(@user_id).execute('flickr.photosets.getInfo', :photoset_id => id)
20
+ # Find should return a single entity
21
+ parse(query).first
22
+ end
23
+
24
+ # Get the photos within a set
25
+ # Queries:
26
+ # > photosets.getPhotos (1 call)
27
+ # > Photo.find ID (n calls)
28
+ def photos
29
+ return (Flickr::Query.new(user_id).execute('flickr.photosets.getPhotos', :photoset_id => id)/:photo).parallel_map(Flickr::MAX_THREADS) do |photo|
30
+ Flickr::Photo.find user_id, photo[:id]
31
+ end
32
+ end
33
+
34
+ # Return a list of tags for the set
35
+ # Queries:
36
+ # > photosets.getPhotos (1 call)
37
+ # > Photo.find ID (n calls)
38
+ def tags
39
+ tags = []
40
+ photos.each do |photo|
41
+ photo.tags.each do |tag|
42
+ tags << tag.value
43
+ end
44
+ end
45
+ tags.uniq
46
+ end
47
+
48
+ # Return a list of machine_tags for the set
49
+ # Queries:
50
+ # > photosets.getPhotos (1 call)
51
+ # > Photo.find ID (n calls)
52
+ def machine_tags
53
+ tags = []
54
+ photos.each do |photo|
55
+ photo.machine_tags.each do |tag|
56
+ tags << tag
57
+ end
58
+ end
59
+ tags.uniq
60
+ end
61
+
62
+ # Search for photosets that have similar tags
63
+ def similar
64
+ sets = []
65
+ Flickr::PhotoSet.list(user_id).each do |set|
66
+ sets << set unless (self.tags & set.tags).empty? or set == self
67
+ end
68
+ sets.uniq
69
+ end
70
+
71
+ private
72
+ def self.parse collection
73
+ photosets = (collection/:photoset)
74
+ photosets.empty? ? [] : photosets.map do |set|
75
+ new(set[:id], (set/:title).inner_text, (set/:description).inner_text, set[:photos])
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,13 @@
1
+ class Flickr::Tag < Flickr::Base
2
+ attr_accessor :value
3
+
4
+ def initialize(tag)
5
+ @value = tag
6
+ end
7
+
8
+ def self.list(user_id)
9
+ (Flickr::Query.new(user_id).execute('flickr.tags.getListUser')/:tag).map { |tag|
10
+ self.new(tag.inner_text.to_s) if tag.inner_text.to_s =~ /^([a-zA-Z0-9_-]+)$/
11
+ }.compact
12
+ end
13
+ end
@@ -0,0 +1,67 @@
1
+ class Flickr::User < Flickr::Base
2
+ attr_accessor :username, :nsid
3
+
4
+ def initialize username, nsid
5
+ @username, @nsid = username, nsid
6
+ end
7
+
8
+ #
9
+ # ==== Class methods
10
+ #
11
+
12
+ def self.find_by_email email
13
+ result = Flickr::Query.new('').execute('flickr.people.findByEmail', :find_email => email)
14
+ return Flickr::User.new(result.at(:username).inner_text, result.at(:user)['nsid'])
15
+ end
16
+
17
+ def self.find_by_username username
18
+ result = Flickr::Query.new('').execute('flickr.people.findByUsername', :username => username)
19
+ return Flickr::User.new(result.at(:username).inner_text, result.at(:user)['nsid'])
20
+ end
21
+
22
+ #
23
+ # ==== Instance methods
24
+ #
25
+
26
+ def realname
27
+ get_info.at(:realname).inner_text
28
+ end
29
+
30
+ # The location string as entered in flickr
31
+ def location
32
+ get_info.at(:location).inner_text || "Unknown"
33
+ end
34
+
35
+ # has the user shelled out for flickr-pro account?
36
+ def pro?
37
+ (get_info.at(:person)["ispro"] == "1") ? true : false
38
+ end
39
+
40
+ # Do they have a buddy icon or not?
41
+ def buddy_icon?
42
+ (icon_server > 0) ? true : false
43
+ end
44
+
45
+ def buddy_icon_url
46
+ buddy_icon? ? "http://farm#{icon_farm}.static.flickr.com/#{icon_server}/buddyicons/#{nsid}.jpg" : "http://www.flickr.com/images/buddyicon.jpg"
47
+ end
48
+
49
+ #
50
+ # ==== Private methods
51
+ #
52
+
53
+ private
54
+ # Get info for the user, once and once only
55
+ def get_info
56
+ @info = Flickr::Query.new(@nsid).execute('flickr.people.getInfo', :user_id => nsid) unless @info
57
+ @info
58
+ end
59
+
60
+ def icon_server
61
+ get_info.at(:person)["iconserver"].to_i
62
+ end
63
+
64
+ def icon_farm
65
+ get_info.at(:person)["iconfarm"].to_i
66
+ end
67
+ end
@@ -0,0 +1,121 @@
1
+ # Stolen from http://rubyforge.org/projects/concurrent
2
+
3
+ #
4
+ # concurrent/parallel - data-parallel programming for Ruby
5
+ #
6
+ # Copyright (C) 2007 MenTaLguY <mental@rydia.net>
7
+ #
8
+ # This file is made available under the same terms as Ruby.
9
+ #
10
+
11
+ module Concurrent #:nodoc:
12
+ module Parallel #:nodoc:
13
+ end
14
+ end
15
+
16
+ module Enumerable #:nodoc:
17
+ def parallel_each( n, &block )
18
+ parallel_subsets( n ).map do |slice|
19
+ Thread.new { slice.each &block }
20
+ end.each do |thread|
21
+ thread.join
22
+ end
23
+ self
24
+ end
25
+
26
+ def parallel_map( n, &block )
27
+ parallel_subsets( n ).map do |slice|
28
+ Thread.new { slice.map &block }
29
+ end.inject( [] ) do |a, thread|
30
+ a.push *thread.value
31
+ end
32
+ end
33
+
34
+ def parallel_select( n, &block )
35
+ parallel_subsets( n ).map do |slice|
36
+ Thread.new { slice.select &block }
37
+ end.inject( [] ) do |a, results|
38
+ a.push *thread.value
39
+ end
40
+ end
41
+
42
+ def parallel_reject( n, &block )
43
+ parallel_subsets( n ).map do |slice|
44
+ Thread.new { slice.reject &block }
45
+ end.inject( [] ) do |a, thread|
46
+ a.push *thread.value
47
+ end
48
+ end
49
+
50
+ def parallel_max( n )
51
+ parallel_subsets( n ).map do |slice|
52
+ Thread.new { slice.max }
53
+ end.map { |t| t.value }.max
54
+ end
55
+
56
+ def parallel_min( n )
57
+ parallel_subsets( n ).map do |slice|
58
+ Thread.new { slice.min }
59
+ end.map { |t| t.value }.min
60
+ end
61
+
62
+ def parallel_partition( n, &block )
63
+ parallel_subsets( n ).map do |slice|
64
+ Thread.new { slice.partition &block }
65
+ end.inject( [ [], [] ] ) do |acc, thread|
66
+ pair = thread.value
67
+ acc[0].push *pair[0]
68
+ acc[1].push *pair[1]
69
+ acc
70
+ end
71
+ end
72
+
73
+ def parallel_grep( re, n, &block )
74
+ parallel_subsets( n ).map do |slice|
75
+ Thread.new { slice.grep( re, &block ) }
76
+ end.inject( [] ) do |acc, thread|
77
+ acc.push *thread.value
78
+ end
79
+ end
80
+
81
+ def parallel_all?( n, &block )
82
+ parallel_subsets( n ).map do |slice|
83
+ Thread.new { slice.all? &block }
84
+ end.inject( true ) do |acc, thread|
85
+ acc && thread.value
86
+ end
87
+ end
88
+
89
+ def parallel_any?( n, &block )
90
+ parallel_subsets( n ).map do |slice|
91
+ Thread.new { slice.any? &block }
92
+ end.inject( false ) do |acc, thread|
93
+ acc || thread.value
94
+ end
95
+ end
96
+
97
+ def parallel_include?( n, obj )
98
+ parallel_subsets( n ).map do |slice|
99
+ Thread.new { slice.include? obj }
100
+ end.inject( false ) do |acc, thread|
101
+ acc || thread.value
102
+ end
103
+ end
104
+
105
+ def parallel_subsets( n )
106
+ to_a.parallel_subsets( n )
107
+ end
108
+ end
109
+
110
+ class Array #:nodoc:
111
+ def parallel_subsets( n )
112
+ if n > 1
113
+ slice_size = (size.to_f / n.to_f).ceil
114
+ (0...(size.to_f / slice_size)).map do |i|
115
+ self[i*slice_size, slice_size.ceil]
116
+ end
117
+ else
118
+ [ self ]
119
+ end
120
+ end
121
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: flickr-wrapper
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.3
5
+ platform: ruby
6
+ authors:
7
+ - Ben Schwarz
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-06-07 00:00:00 +10:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: flickr-rest
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.1
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: validatable
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.6.7
34
+ version:
35
+ description: A grassroots wrapper around flickr - Strictly for convenience
36
+ email: ben@germanforblack.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files: []
42
+
43
+ files:
44
+ - README
45
+ - flickr-wrapper.gemspec
46
+ - lib/flickr-wrapper.rb
47
+ - lib/flickr-wrapper/base.rb
48
+ - lib/flickr-wrapper/machine_tag.rb
49
+ - lib/flickr-wrapper/photo.rb
50
+ - lib/flickr-wrapper/photoset.rb
51
+ - lib/flickr-wrapper/tag.rb
52
+ - lib/flickr-wrapper/user.rb
53
+ - lib/vendor/parallel.rb
54
+ has_rdoc: false
55
+ homepage: http://github.com/benschwarz/flickr-wrapper
56
+ post_install_message:
57
+ rdoc_options: []
58
+
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: "0"
66
+ version:
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: "0"
72
+ version:
73
+ requirements: []
74
+
75
+ rubyforge_project:
76
+ rubygems_version: 1.3.1
77
+ signing_key:
78
+ specification_version: 2
79
+ summary: A grassroots wrapper for flickr
80
+ test_files: []
81
+