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 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.
@@ -0,0 +1,4 @@
1
+ require 'aws_credentials'
2
+ require 'aws/s3'
3
+ require 'tmpdir'
4
+ require File.join(File.dirname(__FILE__), 'manipulator', 'manipulator')
@@ -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,11 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+ require 'mocha'
5
+
6
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
7
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
8
+ require 'manipulator'
9
+
10
+ class Test::Unit::TestCase
11
+ end
@@ -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