manipulator 0.1.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/LICENSE +20 -0
- data/README.rdoc +46 -0
- data/lib/manipulator.rb +4 -0
- data/lib/manipulator/manipulator.rb +66 -0
- data/test/helper.rb +11 -0
- data/test/test_manipulator.rb +114 -0
- metadata +102 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 The New York Times Company
|
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.
|
data/README.rdoc
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
= manipulator
|
2
|
+
|
3
|
+
S3 is great for storing image files, but manipulating images after they've been saved to S3 (rotating, resizing, cropping, etc.) is a bit of a chore. Usually it goes something like this:
|
4
|
+
|
5
|
+
* download file from S3, save to local tmp file
|
6
|
+
* run imagemagick over local tmp file
|
7
|
+
* push modified image back to S3
|
8
|
+
* remember to clean up tmp file
|
9
|
+
|
10
|
+
After re-implementing this pattern a few times over (and filling up /tmp by forgetting the last step), we decided to roll this into a gem that makes S3 image manipulation as easy as:
|
11
|
+
|
12
|
+
m = Manipulator.new
|
13
|
+
|
14
|
+
m.manipulate('my-bucket','some/file.jpg') do |img|
|
15
|
+
img.rotate(90).resize_to_fit(75,75)
|
16
|
+
end
|
17
|
+
|
18
|
+
The idea is to instantiate a Manipulator and then pass it a bucket name and key to manipulate. Manipulator will download your image to a local tmp file, then yield an RMagick Image wrapping that file. Call methods on the yielded image to make modifications; as soon as the block returns, the modified image is pushed back to S3 and the tmp file is removed.
|
19
|
+
|
20
|
+
== Install
|
21
|
+
|
22
|
+
Manipulator is released on gemcutter. If you haven't already set yourself up with that, here's how:
|
23
|
+
|
24
|
+
$ gem install gemcutter
|
25
|
+
$ gem tumble
|
26
|
+
|
27
|
+
Then, to install Manipulator:
|
28
|
+
|
29
|
+
$ gem install manipulator
|
30
|
+
|
31
|
+
You can also just declare the source:
|
32
|
+
|
33
|
+
$ gem install manipulator -s http://gemcutter.org
|
34
|
+
|
35
|
+
== Dependencies
|
36
|
+
|
37
|
+
* AWSCredentials (http://github.com/bkoski/aws_credentials)
|
38
|
+
* AWS/S3 (http://gemcutter.org/gems/aws-s3)
|
39
|
+
|
40
|
+
== Future
|
41
|
+
|
42
|
+
* Allow images to be manipulated from other sources (FTP, HTTP, etc.)
|
43
|
+
|
44
|
+
== Copyright
|
45
|
+
|
46
|
+
Copyright (c) 2009 The New York Times Company. See LICENSE for details.
|
data/lib/manipulator.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# Tries to avoid case-sensitivity issues when requiring RMagick.
|
2
|
+
# Borrowed from jnicklas' carrierwave.
|
3
|
+
unless defined? Magick
|
4
|
+
begin
|
5
|
+
require 'rmagick'
|
6
|
+
rescue LoadError
|
7
|
+
require 'RMagick'
|
8
|
+
rescue LoadError
|
9
|
+
puts "WARNING: Failed to require rmagick, image processing may fail!"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
# Using Manipulator is as simple as creating an instance and calling manipulate.
|
15
|
+
#
|
16
|
+
# The connection to S3 is opened using credentials read from AWSCredentials.
|
17
|
+
class Manipulator
|
18
|
+
|
19
|
+
# Path to the local temp file used during manipulation
|
20
|
+
attr_accessor :temp_file_path
|
21
|
+
|
22
|
+
# Downloads the specified bucket, key from S3 to a local temp file
|
23
|
+
# and sets temp_file_path
|
24
|
+
def download(bucket, key)
|
25
|
+
AWS::S3::Base.establish_connection!(:access_key_id => AWSCredentials.access_key, :secret_access_key => AWSCredentials.secret_access_key)
|
26
|
+
@temp_file_path = File.join(Dir.tmpdir, key.gsub('/', '-'))
|
27
|
+
File.open(temp_file_path, 'w+') do |f|
|
28
|
+
f.puts AWS::S3::S3Object.value(key,bucket)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Pushes contents of temp file back to specified bucket, key on S3
|
33
|
+
def upload(bucket, key)
|
34
|
+
AWS::S3::S3Object.store(key, File.open(temp_file_path, 'r'), bucket, :access => :public_read)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Specify a S3 key to manipulate (and its bucket).
|
38
|
+
#
|
39
|
+
# Block yields a Magick::Image image instance with access to the usual RMagick methods.
|
40
|
+
# Note that RMagick methods should be chained so that block returns image with all manipulations.
|
41
|
+
# For example, use
|
42
|
+
# m.manipulate('my-bucket', 'my-key') do |img|
|
43
|
+
# img.rotate(90).sepia_tone
|
44
|
+
# end
|
45
|
+
# rather than
|
46
|
+
# img.rotate(90)
|
47
|
+
# img.sepia_tone
|
48
|
+
# This last case will only <tt>sepia_tone</tt> the image.
|
49
|
+
def manipulate(bucket, key, &block)
|
50
|
+
download(bucket, key)
|
51
|
+
begin
|
52
|
+
image = ::Magick::Image.read(temp_file_path).first
|
53
|
+
new_image = yield(image)
|
54
|
+
new_image.write(temp_file_path)
|
55
|
+
upload(bucket, key)
|
56
|
+
ensure
|
57
|
+
cleanup
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Removes the temp file
|
62
|
+
def cleanup
|
63
|
+
File.delete(temp_file_path)
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'helper'
|
2
|
+
class TestManipulator < Test::Unit::TestCase
|
3
|
+
|
4
|
+
def setup
|
5
|
+
@key = File.join('data', 'ham.jpg')
|
6
|
+
@access_key = '1234'
|
7
|
+
AWSCredentials.stubs(:access_key).returns(@access_key)
|
8
|
+
@secret_key = '5678'
|
9
|
+
AWSCredentials.stubs(:secret_access_key).returns(@secret_key)
|
10
|
+
@bucket = 'foobar'
|
11
|
+
AWS::S3::Base.stubs(:establish_connection!)
|
12
|
+
@temp_file_path = '/tmp/whatever.jpg'
|
13
|
+
@new_file = stub('new file', :read => '123')
|
14
|
+
File.stubs(:open).returns(@new_file)
|
15
|
+
end
|
16
|
+
|
17
|
+
context "#download" do
|
18
|
+
setup do
|
19
|
+
@manipulator = Manipulator.new
|
20
|
+
AWS::S3::S3Object.stubs(:value).returns('123')
|
21
|
+
end
|
22
|
+
should "establish connection with credentials from aws_credentials" do
|
23
|
+
AWS::S3::Base.expects(:establish_connection!).with(:access_key_id => @access_key, :secret_access_key => @secret_key)
|
24
|
+
@manipulator.download(@bucket, @key)
|
25
|
+
end
|
26
|
+
should "set temp_file_path, replacing any /s with -s" do
|
27
|
+
@manipulator.download(@bucket, @key)
|
28
|
+
assert_equal File.join(Dir.tmpdir, @key.gsub('/', '-')), @manipulator.temp_file_path
|
29
|
+
end
|
30
|
+
should "write a local file with the correct name" do
|
31
|
+
local_file = mock('local_file')
|
32
|
+
local_file.expects(:puts).with('123')
|
33
|
+
File.expects(:open).yields(local_file)
|
34
|
+
@manipulator.download(@bucket, @key)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "#manipulate" do
|
39
|
+
setup do
|
40
|
+
@manipulator = Manipulator.new
|
41
|
+
@file = stub_everything('fake file')
|
42
|
+
@manipulator.stubs(:download).returns(@key)
|
43
|
+
@manipulator.stubs(:upload)
|
44
|
+
@manipulator.stubs(:cleanup)
|
45
|
+
@manipulator.stubs(:temp_file_path).returns(@temp_file_path)
|
46
|
+
@mocked = mock('fake rmagick instance')
|
47
|
+
Magick::Image.stubs(:read).returns([@mocked])
|
48
|
+
@mocked.stubs(:write)
|
49
|
+
end
|
50
|
+
|
51
|
+
should "download the file" do
|
52
|
+
@manipulator.expects(:download).with(@bucket, @key).returns(@key)
|
53
|
+
@manipulator.manipulate(@bucket, @key) { |img| img }
|
54
|
+
end
|
55
|
+
should "create an RMagick Image instance and yield it to the block" do
|
56
|
+
Magick::Image.expects(:read).with(@temp_file_path).returns([@mocked])
|
57
|
+
@manipulator.manipulate(@bucket, @key) do |img|
|
58
|
+
assert_equal @mocked, img
|
59
|
+
img
|
60
|
+
end
|
61
|
+
end
|
62
|
+
should "write the returned manipulated image to the temp file" do
|
63
|
+
@mocked.expects(:write).with(@temp_file_path)
|
64
|
+
Magick::Image.stubs(:read).with(@temp_file_path).returns([@mocked])
|
65
|
+
@manipulator.manipulate(@bucket, @key) { |img| img }
|
66
|
+
end
|
67
|
+
should "upload the file back to s3" do
|
68
|
+
@manipulator.expects(:upload).with(@bucket, @key)
|
69
|
+
@manipulator.manipulate(@bucket, @key) { |img| img }
|
70
|
+
end
|
71
|
+
should "not run cleanup if download raises an error" do
|
72
|
+
@manipulator.stubs(:download).raises('foo')
|
73
|
+
@manipulator.expects(:cleanup).never
|
74
|
+
begin
|
75
|
+
@manipulator.manipulate(@bucket, @key) { |img| img }
|
76
|
+
rescue
|
77
|
+
end
|
78
|
+
end
|
79
|
+
should "ensure that it calls cleanup if download success" do
|
80
|
+
@manipulator.expects(:cleanup)
|
81
|
+
begin
|
82
|
+
@manipulator.manipulate(@bucket, @key) do |img|
|
83
|
+
raise "an error"
|
84
|
+
end
|
85
|
+
rescue
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "#upload" do
|
91
|
+
setup do
|
92
|
+
@manipulator = Manipulator.new
|
93
|
+
@manipulator.stubs(:temp_file_path).returns(@temp_file_path)
|
94
|
+
end
|
95
|
+
should "call S3Object.store with contents of tempfile" do
|
96
|
+
AWS::S3::S3Object.expects(:store).with(@key,@new_file,@bucket, :access => :public_read)
|
97
|
+
@manipulator.upload(@bucket, @key)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
context "#cleanup" do
|
103
|
+
setup do
|
104
|
+
@manipulator = Manipulator.new
|
105
|
+
@key = File.join(File.dirname(__FILE__), 'data', 'ham.jpg')
|
106
|
+
@manipulator.stubs(:temp_file_path).returns(@temp_file_path)
|
107
|
+
Manipulator.any_instance.stubs(:download)
|
108
|
+
end
|
109
|
+
should "call delete on temp_file" do
|
110
|
+
File.expects(:delete).with(@temp_file_path)
|
111
|
+
@manipulator.cleanup
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: manipulator
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jacqui Maher
|
8
|
+
- Ben Koski
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2009-12-22 00:00:00 -05:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: shoulda
|
18
|
+
type: :development
|
19
|
+
version_requirement:
|
20
|
+
version_requirements: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "0"
|
25
|
+
version:
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: mocha
|
28
|
+
type: :development
|
29
|
+
version_requirement:
|
30
|
+
version_requirements: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: "0"
|
35
|
+
version:
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: aws_credentials
|
38
|
+
type: :runtime
|
39
|
+
version_requirement:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: "0"
|
45
|
+
version:
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: aws-s3
|
48
|
+
type: :runtime
|
49
|
+
version_requirement:
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: "0"
|
55
|
+
version:
|
56
|
+
description: A library to allow photo manipulation on S3 images using RMagick
|
57
|
+
email: jacqui@brighter.net
|
58
|
+
executables: []
|
59
|
+
|
60
|
+
extensions: []
|
61
|
+
|
62
|
+
extra_rdoc_files:
|
63
|
+
- LICENSE
|
64
|
+
- README.rdoc
|
65
|
+
files:
|
66
|
+
- LICENSE
|
67
|
+
- README.rdoc
|
68
|
+
- lib/manipulator.rb
|
69
|
+
- lib/manipulator/manipulator.rb
|
70
|
+
- test/helper.rb
|
71
|
+
- test/test_manipulator.rb
|
72
|
+
has_rdoc: true
|
73
|
+
homepage: http://github.com/newsdev/manipulator
|
74
|
+
licenses: []
|
75
|
+
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options:
|
78
|
+
- --charset=UTF-8
|
79
|
+
require_paths:
|
80
|
+
- lib
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: "0"
|
86
|
+
version:
|
87
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: "0"
|
92
|
+
version:
|
93
|
+
requirements: []
|
94
|
+
|
95
|
+
rubyforge_project:
|
96
|
+
rubygems_version: 1.3.5
|
97
|
+
signing_key:
|
98
|
+
specification_version: 3
|
99
|
+
summary: manipulate your photos on s3
|
100
|
+
test_files:
|
101
|
+
- test/helper.rb
|
102
|
+
- test/test_manipulator.rb
|