thumbnail 0.2.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.
@@ -0,0 +1,8 @@
1
+ == 0.2.0 2007-06-08
2
+
3
+ * Initial release:
4
+ * fully wraps the AWS-AST REST API
5
+ * tested on Mac OS X 10.4.9 (PowerPC architecture) with Ruby 1.8.5
6
+ * complete spec suite
7
+ * basic rdoc and website
8
+ * rad logo
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007 Greg Borenstein
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,28 @@
1
+ client_spec.txt
2
+ response_spec.txt
3
+ History.txt
4
+ License.txt
5
+ lib/thumbnail.rb
6
+ lib/thumbnail/version.rb
7
+ lib/thumbnail/client.rb
8
+ lib/thumbnail/response.rb
9
+ Manifest.txt
10
+ Rakefile
11
+ README.txt
12
+ setup.rb
13
+ spec/client_spec.rb
14
+ spec/response_spec.rb
15
+ spec/spec.opts
16
+ spec/fixtures/batch.xml
17
+ spec/fixtures/single.xml
18
+ spec/fixtures/unexists.xml
19
+ spec/fixtures/invalid_credentials.xml
20
+ website/index.html
21
+ website/javascripts/rounded_corners_lite.inc.js
22
+ website/stylesheets/screen.css
23
+ website/amzn.jpg
24
+ website/craphound.jpg
25
+ website/logo.png
26
+ website/small_crap.jpg
27
+ website/soon.jpg
28
+
@@ -0,0 +1,88 @@
1
+ = THUMBNAIL
2
+
3
+ A Ruby wrapper for the Amazon Web Services Alexa Site Thumbnail Service REST API, which provides website thumbnail images on demand for a small fee ($0.20/1000).
4
+
5
+ by Greg Borenstein
6
+ thumbnail.rubyforge.org
7
+
8
+ == Installing
9
+
10
+ $ sudo gem install thumbnail
11
+
12
+ Thumbnail depends on the terrific Hpricot (http://code.whytheluckystiff.net/hpricot) XHTML parsing and manipulation library by why the lucky stiff; version 0.5 or better has the necessary powers.
13
+
14
+ == Registering with Amazon
15
+
16
+ In order to use the AWS-AST api, you must first complete the following series of bureaucratic tasks:
17
+
18
+ 1. Signup for an Amazon Web Services account (if you don't yet have one) by visiting http://aws.amazon.com . You can use your existing Amazon user account for this or create a new one.
19
+
20
+ 2. Signup for the Alexa Site Thumbnail Service (http://www.amazon.com/b/ref=sc_fe_c_0_239513011_4/102-1130464-8229719?ie=UTF8&node=236156011&no=239513011&me=A36L942TSJ2AJA)
21
+
22
+ 3. Find and note your AWS Access Key Id and your Secret Access Key. You'll need to tell Thumbnail about these for authentication's sake; you should be given them at the end of the signup process.
23
+
24
+ Registering for both of these accounts is free, payment for the Site Thumbnail Service is based entirely on usage, with no charges for requests for unavailable thumbnails.
25
+
26
+ == Some Usage Examples
27
+
28
+ Download a small thumbnail of http://www.craphound.com as a local file called small_crap.jpg:
29
+
30
+ require 'thumbnail'
31
+ require 'open-uri'
32
+
33
+ t = Thumbnail::Client.new :access_key_id => YOUR_ACCESS_KEY_ID, :secret_access_key => YOUR_SECRET_ACCESS_KEY, :size => :small
34
+ url = t.get("www.craphound.com")[:thumbnail][:url]
35
+ File.open("small_crap.jpg", "w") { |f| f.write open(url).read }
36
+
37
+ Write a view helper for use in a Rails application to generate image tags with Amazon redirect urls in your templates. In app/helpers/my_helper.rb:
38
+
39
+ module MyHelper
40
+ def thumbnail_image(url, size=:large)_
41
+ t = Thumbnail::Client.new :access_key_id => AWS_ACCESS_KEY_ID, :secret_access_key => AWS_SECRET_ACCESS_KEY,
42
+ :action => :redirect, :size => size
43
+ "<img src=\"#{t.get(url)}\">"
44
+ end
45
+ end
46
+
47
+ which, assuming you'd defined AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY in your environment.rb file and required thumbnail somewhere along the line, you'd use in your view like so:
48
+
49
+ <%= thumbnail_image("www.craphound.com") %>
50
+
51
+ (Work is underway on a plugin that would make this helper universally available within your app.)
52
+
53
+ Download large thumbnails for a bunch of sites in one batch with error handling:
54
+
55
+ require 'thumbnail'
56
+ require 'open-uri'
57
+
58
+ t = Thumbnail::Client.new :access_key_id => YOUR_ACCESS_KEY_ID, :secret_access_key => YOUR_SECRET_ACCESS_KEY
59
+ result = t.get(["www.craphound.com", "www.urbanhonking.com", "www.twitter.com"])
60
+ if result.is_a? Thumbnail::Response::Success
61
+ result.parsed.each do |r|
62
+ url = r[:thumbnail][:url]
63
+ filename = "#{r[:thumbnail][:request_url].split(".")[1]}.jpg"
64
+ File.open(filename, "w") { |f| f.write open(url).read }
65
+ end
66
+ else
67
+ puts "Request failed! #{result}"
68
+ end
69
+
70
+ For a more verbose walkthrough (and an idea of what the thumbnails look like), visit Thumbnail's rubyforge homepage: http://thumbnail.rubyforge.org. For more details on the library's operation, see the spec output in client_spec.txt and response_spec.txt.
71
+
72
+ == Other Facts of Note
73
+
74
+ -If a site thumbnail is not available, AWS-AST will return the "not available" image and will not charge for the api call. The actual site thumbnail will be created within 24 hours. The parsed results hash of a Thumbnail::Response::Success object offers programatic access to this via the <code>:exists</code> key.
75
+
76
+ -Thumbnail urls generated by calls to the api are good for 15 seconds, so run your code that downloads and saves them right away.
77
+
78
+ == AWS-AST API Docs
79
+
80
+ http://docs.amazonwebservices.com/AlexaSiteThumbnail/2007-01-01/
81
+
82
+ == License
83
+
84
+ Copyright 2007 Greg Borenstein. This code is free to use under the terms of the MIT license.
85
+
86
+ == Feedback
87
+
88
+ Got questions? Got Kudos? Email me: greg@mfdz.com.
@@ -0,0 +1,140 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/clean'
4
+ require 'rake/testtask'
5
+ require 'rake/packagetask'
6
+ require 'rake/gempackagetask'
7
+ require 'rake/rdoctask'
8
+ require 'rake/contrib/rubyforgepublisher'
9
+ require 'fileutils'
10
+ require 'hoe'
11
+ begin
12
+ require 'spec/rake/spectask'
13
+ rescue LoadError
14
+ puts 'To use rspec for testing you must install rspec gem:'
15
+ puts '$ sudo gem install rspec'
16
+ exit
17
+ end
18
+
19
+ include FileUtils
20
+ require File.join(File.dirname(__FILE__), 'lib', 'thumbnail', 'version')
21
+
22
+ AUTHOR = 'Greg Borenstein' # can also be an array of Authors
23
+ EMAIL = "greg@mfdz.com"
24
+ DESCRIPTION = "Wrapper for the Amazon Web Services Alexa Thumbnail Service REST API, which provides website thumbnail images on demand for a small fee ($0.20/1000)"
25
+ GEM_NAME = 'thumbnail' # what ppl will type to install your gem
26
+
27
+ @config_file = "~/.rubyforge/user-config.yml"
28
+ @config = nil
29
+ def rubyforge_username
30
+ unless @config
31
+ begin
32
+ @config = YAML.load(File.read(File.expand_path(@config_file)))
33
+ rescue
34
+ puts <<-EOS
35
+ ERROR: No rubyforge config file found: #{@config_file}"
36
+ Run 'rubyforge setup' to prepare your env for access to Rubyforge
37
+ - See http://newgem.rubyforge.org/rubyforge.html for more details
38
+ EOS
39
+ exit
40
+ end
41
+ end
42
+ @rubyforge_username ||= @config["username"]
43
+ end
44
+
45
+ RUBYFORGE_PROJECT = 'thumbnail' # The unix name for your project
46
+ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
47
+ DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
48
+
49
+ NAME = "thumbnail"
50
+ REV = nil
51
+ # UNCOMMENT IF REQUIRED:
52
+ # REV = `svn info`.each {|line| if line =~ /^Revision:/ then k,v = line.split(': '); break v.chomp; else next; end} rescue nil
53
+ VERS = Thumbnail::VERSION::STRING + (REV ? ".#{REV}" : "")
54
+ CLEAN.include ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store']
55
+ RDOC_OPTS = ['--quiet', '--title', 'thumbnail documentation',
56
+ "--opname", "index.html",
57
+ "--line-numbers",
58
+ "--main", "README",
59
+ "--inline-source"]
60
+
61
+ class Hoe
62
+ def extra_deps
63
+ @extra_deps.reject { |x| Array(x).first == 'hoe' }
64
+ end
65
+ end
66
+
67
+ # Generate all the Rake tasks
68
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
69
+ hoe = Hoe.new(GEM_NAME, VERS) do |p|
70
+ p.author = AUTHOR
71
+ p.description = DESCRIPTION
72
+ p.email = EMAIL
73
+ p.summary = DESCRIPTION
74
+ p.url = HOMEPATH
75
+ p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
76
+ p.test_globs = ["test/**/test_*.rb"]
77
+ p.clean_globs |= CLEAN #An array of file patterns to delete on clean.
78
+
79
+ # == Optional
80
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
81
+ #p.extra_deps = [] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
82
+ #p.spec_extras = {} # A hash of extra values to set in the gemspec.
83
+ end
84
+
85
+ CHANGES = hoe.paragraphs_of('History.txt', 0..1).join("\n\n")
86
+ PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
87
+ hoe.spec.files << "client_spec.txt"
88
+ hoe.spec.files << "response_spec.txt"
89
+ hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
90
+
91
+ desc 'Generate website files'
92
+ task :website_generate do
93
+ Dir['website/**/*.txt'].each do |txt|
94
+ sh %{ ruby scripts/txt2html #{txt} > #{txt.gsub(/txt$/,'html')} }
95
+ end
96
+ end
97
+
98
+ desc 'Upload website files to rubyforge'
99
+ task :website_upload do
100
+ host = "#{rubyforge_username}@rubyforge.org"
101
+ remote_dir = "/var/www/gforge-projects/#{PATH}/"
102
+ local_dir = 'website'
103
+ sh %{rsync -aCv #{local_dir}/ #{host}:#{remote_dir}}
104
+ end
105
+
106
+ desc 'Generate and upload website files'
107
+ task :website => [:website_generate, :website_upload, :publish_docs]
108
+
109
+ desc 'Release the website and new gem version'
110
+ task :deploy => [:check_version, :website, :release] do
111
+ puts "Remember to create SVN tag:"
112
+ puts "svn copy svn+ssh://#{rubyforge_username}@rubyforge.org/var/svn/#{PATH}/trunk " +
113
+ "svn+ssh://#{rubyforge_username}@rubyforge.org/var/svn/#{PATH}/tags/REL-#{VERS} "
114
+ puts "Suggested comment:"
115
+ puts "Tagging release #{CHANGES}"
116
+ end
117
+
118
+ desc 'Runs tasks website_generate and install_gem as a local deployment of the gem'
119
+ task :local_deploy => [:website_generate, :install_gem]
120
+
121
+ task :check_version do
122
+ unless ENV['VERSION']
123
+ puts 'Must pass a VERSION=x.y.z release version'
124
+ exit
125
+ end
126
+ unless ENV['VERSION'] == VERS
127
+ puts "Please update your version.rb to match the release version, currently #{VERS}"
128
+ exit
129
+ end
130
+ end
131
+
132
+ desc "Run the specs under spec/models"
133
+ Spec::Rake::SpecTask.new do |t|
134
+ t.spec_opts = ['--options', "spec/spec.opts"]
135
+ t.spec_files = FileList['spec/*_spec.rb']
136
+ end
137
+
138
+ desc "Default task is to run specs"
139
+ task :default => :spec
140
+
@@ -0,0 +1,58 @@
1
+
2
+ Thumbnail::Client#new
3
+ - should default to the thumbnail action
4
+ - should set the action as redirect if specified
5
+ - should raise an error if the action is neither redirect nor thumbnail
6
+ - should raise an error if the access_key_id is not specified
7
+ - should raise an error if the secret_access_key is not specified
8
+ - should default the size to large
9
+ - should set the size to small if specified
10
+ - should raise an error if the size is neither large nor small
11
+ - should raise an error if a default_image is specified for a Thumbnail action
12
+
13
+ Thumbnail::Client#new for a redirect
14
+ - should set the default_image if specified
15
+
16
+ Thumbnail#query
17
+ - should calculate the signature and timestamp
18
+
19
+ Thumbnail#calculate_signature_and_timestamp
20
+ - should calculate the signature
21
+ - should set the (correctly formatted) timestamp
22
+
23
+ Thumbnail#query with thumbnail action and a single url
24
+ - should compose a thumbnail single query
25
+
26
+ Thumbnail::Client#query with thumbnail action and an array of urls
27
+ - should compose a thumbnail batch query
28
+
29
+ Thumbnail::Client#query with redirect action
30
+ - should compose a redirect query
31
+
32
+ Thumbnail::Client#thumbnail_query_single
33
+ - should compose the query url correctly
34
+
35
+ Thumbnail::Client#thumbnail_query_batch
36
+ - should compose the query url correctly
37
+
38
+ Thumbnail::Client#redirect_query with a default_image
39
+ - should compose the query url correctly
40
+
41
+ Thumbnail::Client#redirect_query without a default_image
42
+ - should compose the query url correctly
43
+
44
+ Thumbnail::Client#get with a thumbnail action
45
+ - should set the url string if given a single url
46
+ - should set the url array if given an array of urls
47
+ - should parse the url before conducting the query
48
+ - should use net/https to conduct the query
49
+ - should build a response object from the xml
50
+
51
+ Thumbnail::Client#get with one or more invalid urls
52
+ - should raise an error with one url that is invalid
53
+ - should raise an error if any of the urls in its give array are invalid
54
+
55
+ Thumbnail::Client#get with a redirect action
56
+ - should raise an error if given an array of urls
57
+ - should calculate the query url
58
+ - should return the redirect query
@@ -0,0 +1 @@
1
+ Dir['thumbnail/**/*.rb'].sort.each { |lib| require lib }
@@ -0,0 +1,106 @@
1
+ require "base64"
2
+ require "net/https"
3
+ require "uri"
4
+ require 'cgi'
5
+
6
+ module Thumbnail
7
+
8
+ class Client
9
+ attr_accessor :size, :action
10
+
11
+ # <code>:access_key_id</code> and <code>:secret_access_key</code> are required.
12
+ # Optionally, you can indicate <code>:action => :redirect</code> (default is :thumbnail),
13
+ # <code>:size => :small</code> (default is :large), and <code>:default_image => '<image_url>'</code> (only available in
14
+ # conjunction with <code>:action => :redirect</code>)
15
+ def initialize(opts={})
16
+ if opts[:action] && ![:thumbnail, :redirect].include?(opts[:action])
17
+ raise ArgumentError, "Bad action. Must be thumbnail or redirect. Defaults to thumbnail."
18
+ end
19
+
20
+ if opts[:size] && ![:large, :small].include?(opts[:size])
21
+ raise ArgumentError, "Size must be large or small. Defaults to large"
22
+ end
23
+
24
+ unless opts[:access_key_id] && opts[:secret_access_key]
25
+ raise ArgumentError, "Must include your access_key_id and your secret_access_key."
26
+ end
27
+
28
+ @size = opts[:size] || :large
29
+ @action = opts[:action] || :thumbnail
30
+ @access_key_id = opts[:access_key_id]
31
+ @secret_access_key = opts[:secret_access_key]
32
+
33
+ if opts[:default_image] && (@action == :thumbnail)
34
+ raise ArgumentError, "Can only specify a default_image on the redirect action."
35
+ end
36
+
37
+ @default_image = opts[:default_image]
38
+ end
39
+
40
+ # The main method for using this library.
41
+ # get() can take a single url, or an array of them (only available for the thumbnail action).
42
+ # For a request with <code>:action => :redirect</code>
43
+ # it will return the composed query url; for a thumbnail action
44
+ # it will build your query, send it to Amazon, and build
45
+ # and return the Thumbnail::Response object.
46
+ def get(url)
47
+ if (@action == :redirect)
48
+ if url.is_a?(Array)
49
+ raise ArgumentError, "Can only get a single url on a redirect action"
50
+ else
51
+ @url = url
52
+ return query
53
+ end
54
+ end
55
+
56
+ url.each{|u| URI.parse(u)}
57
+
58
+ @url = url
59
+ xml = Net::HTTP.get(URI.parse(query))
60
+ Response.build(xml)
61
+ end
62
+
63
+ private
64
+
65
+ # (adapted from amzn's topsites example: http://developer.amazonwebservices.com/connect/entry!default.jspa?categoryID=32&externalID=408&fromSearchPage=true )
66
+ def calculate_signature_and_timestamp
67
+ @timestamp = ( Time.now ).utc.strftime("%Y-%m-%dT%H:%M:%S.000Z")
68
+
69
+ @signature = CGI.escape(Base64.encode64( OpenSSL::HMAC.digest( OpenSSL::Digest::Digest.new( "SHA1" ), @secret_access_key, @action.to_s.capitalize + @timestamp)).strip)
70
+ end
71
+
72
+ def query
73
+ calculate_signature_and_timestamp
74
+
75
+ if @action == :thumbnail
76
+ if @url.is_a? Array
77
+ thumbnail_query_batch
78
+ else
79
+ thumbnail_query_single
80
+ end
81
+ else
82
+ redirect_query
83
+ end
84
+ end
85
+
86
+
87
+ def thumbnail_query_single
88
+ "http://ast.amazonaws.com/?Action=Thumbnail&AWSAccessKeyId=#{@access_key_id}&Signature=#{@signature}&Timestamp=#{@timestamp}&Url=#{@url}&Size=#{@size.to_s.capitalize}"
89
+ end
90
+
91
+ def thumbnail_query_batch
92
+ url_params = ""
93
+ @url.each_with_index do |a,i|
94
+ url_params << "Thumbnail.#{i+1}.Url=#{a}"
95
+ url_params << "&" unless (i+1)==@url.length
96
+ end
97
+ "http://ast.amazonaws.com/?Action=Thumbnail&AWSAccessKeyId=#{@access_key_id}&Signature=#{@signature}&Timestamp=#{@timestamp}&Shared.Size=#{@size.to_s.capitalize}&#{url_params}"
98
+ end
99
+
100
+ def redirect_query
101
+ default_param = @default_image ? "&DefaultImage=#{@default_image}" : ""
102
+ "http://ast.amazonaws.com/?Action=Redirect&AWSAccessKeyId=#{@access_key_id}&Signature=#{@signature}&Timestamp=#{@timestamp}&Url=#{@url}&Size=#{@size.to_s.capitalize}#{default_param}"
103
+ end
104
+ end
105
+
106
+ end