flickr-wrapper 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
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
+