ungulate 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +13 -1
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/lib/ungulate/file_upload.rb +21 -19
- data/lib/ungulate.rb +6 -1
- data/spec/ungulate/file_upload_spec.rb +80 -18
- data/spec/ungulate_spec.rb +8 -4
- data/ungulate.gemspec +5 -2
- metadata +20 -7
data/README.rdoc
CHANGED
@@ -2,8 +2,20 @@
|
|
2
2
|
|
3
3
|
According to Wikipedia, this can mean "hoofed animal". Camels have hooves.
|
4
4
|
|
5
|
-
This is a
|
5
|
+
This is a gem for uploading and processing images using an Amazon Web Services stack.
|
6
|
+
|
7
|
+
It comes with a few goodies:
|
8
|
+
|
9
|
+
* ungulate_server.rb - simple queue runner that expects a YAML-encoded job description for RMagick
|
10
|
+
* Ungulate::FileUpload - a model for e.g. Rails that does some cryptography stuff - example to follow
|
11
|
+
|
12
|
+
And some planned goodies for the future:
|
13
|
+
|
14
|
+
* A view helper for Rails that makes it easy to use Ungulate::FileUpload
|
6
15
|
|
16
|
+
== Installation
|
17
|
+
gem install ungulate
|
18
|
+
|
7
19
|
== Note on Patches/Pull Requests
|
8
20
|
|
9
21
|
* Fork the project.
|
data/Rakefile
CHANGED
@@ -13,6 +13,7 @@ begin
|
|
13
13
|
gem.add_dependency "activesupport", ">= 2.3.5"
|
14
14
|
gem.add_dependency "right_aws", ">= 1.10.0"
|
15
15
|
gem.add_dependency "rmagick", ">= 2.12.2"
|
16
|
+
gem.add_dependency "mime-types", ">= 1.16"
|
16
17
|
gem.add_development_dependency "rspec", ">= 1.2.9"
|
17
18
|
gem.add_development_dependency "cucumber", ">= 0.6.2"
|
18
19
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.2
|
data/lib/ungulate/file_upload.rb
CHANGED
@@ -1,35 +1,41 @@
|
|
1
1
|
require 'active_support'
|
2
|
-
require 'hmac/sha1'
|
3
2
|
class Ungulate::FileUpload
|
4
3
|
attr_accessor(
|
5
|
-
:access_key_id,
|
6
4
|
:bucket_url,
|
7
5
|
:key,
|
8
|
-
:policy
|
6
|
+
:policy
|
7
|
+
)
|
8
|
+
|
9
|
+
cattr_accessor(
|
10
|
+
:access_key_id,
|
11
|
+
:queue_name,
|
9
12
|
:secret_access_key
|
10
13
|
)
|
11
14
|
|
12
15
|
def initialize(params)
|
13
|
-
self.access_key_id = params[:access_key_id]
|
14
16
|
self.bucket_url = params[:bucket_url]
|
15
17
|
self.key = params[:key]
|
16
18
|
self.policy = params[:policy]
|
17
|
-
self.secret_access_key = params[:secret_access_key]
|
18
19
|
end
|
19
20
|
|
20
21
|
def acl
|
21
22
|
condition 'acl'
|
22
23
|
end
|
23
24
|
|
25
|
+
def condition(key)
|
26
|
+
conditions.find {|condition| condition.first == key}.second
|
27
|
+
end
|
28
|
+
|
24
29
|
def conditions
|
25
30
|
@conditions ||=
|
26
|
-
|
27
|
-
map {|condition| condition.to_a.flatten}
|
31
|
+
@policy_ruby['conditions'].map {|condition| condition.to_a.flatten}
|
28
32
|
end
|
29
33
|
|
30
|
-
def policy=(
|
31
|
-
@
|
32
|
-
@
|
34
|
+
def policy=(policy)
|
35
|
+
@policy_ruby = policy
|
36
|
+
@policy_ruby['expiration'].utc
|
37
|
+
policy_json = ActiveSupport::JSON.encode(@policy_ruby)
|
38
|
+
@policy = Base64.encode64(policy_json).gsub("\n", '')
|
33
39
|
end
|
34
40
|
|
35
41
|
def success_action_redirect
|
@@ -37,14 +43,10 @@ class Ungulate::FileUpload
|
|
37
43
|
end
|
38
44
|
|
39
45
|
def signature
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
protected
|
46
|
-
|
47
|
-
def condition(key)
|
48
|
-
conditions.find {|condition| condition.first == key}.second
|
46
|
+
Base64.encode64(
|
47
|
+
OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha1'),
|
48
|
+
secret_access_key,
|
49
|
+
policy)
|
50
|
+
).gsub("\n", '')
|
49
51
|
end
|
50
52
|
end
|
data/lib/ungulate.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'right_aws'
|
3
3
|
require 'RMagick'
|
4
|
+
require 'mime/types'
|
4
5
|
|
5
6
|
module Ungulate
|
6
7
|
def self.logger
|
@@ -71,7 +72,11 @@ module Ungulate
|
|
71
72
|
processed_versions.each do |version, image|
|
72
73
|
version_key = version_key version
|
73
74
|
@logger.info "Storing #{version} @ #{version_key}"
|
74
|
-
bucket.put(version_key,
|
75
|
+
bucket.put(version_key,
|
76
|
+
image.to_blob,
|
77
|
+
{},
|
78
|
+
'public-read',
|
79
|
+
{'Content-Type' => MIME::Types.type_for(image.format).to_s})
|
75
80
|
end
|
76
81
|
end
|
77
82
|
|
@@ -3,29 +3,34 @@ require 'ungulate/file_upload'
|
|
3
3
|
|
4
4
|
module Ungulate
|
5
5
|
describe FileUpload do
|
6
|
-
|
6
|
+
before do
|
7
|
+
@expiration = 10.hours.from_now
|
7
8
|
@bucket_url = "http://images.bob.com/"
|
8
|
-
@access_key_id = "asdf"
|
9
9
|
@key = "new-file"
|
10
10
|
|
11
|
-
@policy =
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
11
|
+
@policy = {
|
12
|
+
"expiration" => @expiration,
|
13
|
+
"conditions" => [
|
14
|
+
{"bucket" => "johnsmith" },
|
15
|
+
["starts-with", "$key", "user/eric/"],
|
16
|
+
{"acl" => "public-read" },
|
17
|
+
{"success_action_redirect" => "http://johnsmith.s3.amazonaws.com/successful_upload.html" },
|
18
|
+
["starts-with", "$Content-Type", "image/"],
|
19
|
+
{"x-amz-meta-uuid" => "14365123651274"},
|
20
|
+
["starts-with", "$x-amz-meta-tag", ""]
|
21
|
+
]
|
22
|
+
}
|
23
|
+
|
24
|
+
@access_key_id = "asdf"
|
23
25
|
@secret_access_key = 'uV3F3YluFJax1cknvbcGwgjvx4QpvB+leU8dUj2o'
|
26
|
+
FileUpload.access_key_id = @access_key_id
|
27
|
+
FileUpload.secret_access_key = @secret_access_key
|
28
|
+
FileUpload.queue_name = 'some-queue-name'
|
29
|
+
end
|
24
30
|
|
31
|
+
subject do
|
25
32
|
FileUpload.new(
|
26
33
|
:bucket_url => @bucket_url,
|
27
|
-
:access_key_id => @access_key_id,
|
28
|
-
:secret_access_key => @secret_access_key,
|
29
34
|
:policy => @policy,
|
30
35
|
:key => @key
|
31
36
|
)
|
@@ -44,15 +49,72 @@ module Ungulate
|
|
44
49
|
] }
|
45
50
|
its(:access_key_id) { should == @access_key_id }
|
46
51
|
its(:key) { should == @key }
|
52
|
+
its(:queue_name) { should == 'some-queue-name' }
|
47
53
|
its(:success_action_redirect) { should == 'http://johnsmith.s3.amazonaws.com/successful_upload.html' }
|
48
|
-
|
49
|
-
|
54
|
+
|
55
|
+
describe "condition" do
|
56
|
+
before do
|
57
|
+
subject.stub(:conditions).
|
58
|
+
and_return([ ['colour', 'blue'], ['predicate', 'subject', 'object'] ])
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should return the value of a tuple" do
|
62
|
+
subject.condition('colour').should == 'blue'
|
63
|
+
end
|
64
|
+
end
|
50
65
|
|
51
66
|
describe "conditions" do
|
52
67
|
it "should memoize" do
|
53
68
|
subject.instance_variable_set('@conditions', :cache)
|
54
69
|
subject.conditions.should == :cache
|
55
70
|
end
|
71
|
+
|
72
|
+
it "should convert mixed hash and array policy to nested arrays" do
|
73
|
+
subject.
|
74
|
+
instance_variable_set('@policy_ruby',
|
75
|
+
{
|
76
|
+
'conditions' => [
|
77
|
+
{'colour' => 'blue'},
|
78
|
+
['predicate', 'subject', 'object']
|
79
|
+
]
|
80
|
+
})
|
81
|
+
subject.conditions.should == [ ['colour', 'blue'], ['predicate', 'subject', 'object'] ]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe "policy=" do
|
86
|
+
it "should store the ruby version for later use" do
|
87
|
+
subject.policy = @policy
|
88
|
+
subject.instance_variable_get('@policy_ruby').should_not be_blank
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should store the base64 encoded JSON" do
|
92
|
+
subject # load subject without stubs
|
93
|
+
|
94
|
+
ActiveSupport::JSON.stub(:encode).with(@policy).and_return(:some_json)
|
95
|
+
Base64.stub(:encode64).with(:some_json).and_return("ENCODED\nLINE\nLINE")
|
96
|
+
subject.policy = @policy
|
97
|
+
subject.policy.should == "ENCODEDLINELINE"
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should ensure the expiration is utc" do
|
101
|
+
@expiration.should_receive(:utc).at_least(:once)
|
102
|
+
subject.policy = @policy
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "signature" do
|
107
|
+
before do
|
108
|
+
subject.stub(:policy).and_return(:policy)
|
109
|
+
@sha1 = mock('SHA1')
|
110
|
+
OpenSSL::Digest::Digest.stub(:new).with('sha1').and_return(@sha1)
|
111
|
+
OpenSSL::HMAC.stub(:digest).with(@sha1, @secret_access_key, :policy).and_return(:digest)
|
112
|
+
Base64.stub(:encode64).with(:digest).and_return("str\nipme\n")
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should return the stripped base64 encoded digest" do
|
116
|
+
subject.signature.should == "stripme"
|
117
|
+
end
|
56
118
|
end
|
57
119
|
end
|
58
120
|
end
|
data/spec/ungulate_spec.rb
CHANGED
@@ -193,8 +193,10 @@ module Ungulate
|
|
193
193
|
describe :process do
|
194
194
|
subject do
|
195
195
|
job = Job.new
|
196
|
-
@big = mock('Image', :to_blob => 'bigdata')
|
197
|
-
@little = mock('Image', :to_blob => 'littledata')
|
196
|
+
@big = mock('Image', :to_blob => 'bigdata', :format => 'JPEG')
|
197
|
+
@little = mock('Image', :to_blob => 'littledata', :format => 'JPEG')
|
198
|
+
@mime_type = mock('MimeType', :to_s => 'image/jpeg')
|
199
|
+
MIME::Types.stub(:type_for).with('JPEG').and_return(@mime_type)
|
198
200
|
job.stub(:processed_versions).and_return([[:big, @big], [:little, @little]])
|
199
201
|
job.stub(:bucket).and_return(@bucket)
|
200
202
|
job.stub(:version_key).with(:big).and_return('path/to/someimage_big.jpg')
|
@@ -208,11 +210,13 @@ module Ungulate
|
|
208
210
|
@bucket.should_receive(:put).with('path/to/someimage_big.jpg',
|
209
211
|
'bigdata',
|
210
212
|
{},
|
211
|
-
'public-read'
|
213
|
+
'public-read',
|
214
|
+
{'Content-Type' => 'image/jpeg'})
|
212
215
|
@bucket.should_receive(:put).with('path/to/someimage_little.jpg',
|
213
216
|
'littledata',
|
214
217
|
{},
|
215
|
-
'public-read'
|
218
|
+
'public-read',
|
219
|
+
{'Content-Type' => 'image/jpeg'})
|
216
220
|
end
|
217
221
|
|
218
222
|
context "empty array" do
|
data/ungulate.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{ungulate}
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.2"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Andrew Bruce"]
|
12
|
-
s.date = %q{2010-04-
|
12
|
+
s.date = %q{2010-04-11}
|
13
13
|
s.default_executable = %q{ungulate_server.rb}
|
14
14
|
s.description = %q{WIP}
|
15
15
|
s.email = %q{andrew@camelpunch.com}
|
@@ -60,12 +60,14 @@ Gem::Specification.new do |s|
|
|
60
60
|
s.add_runtime_dependency(%q<activesupport>, [">= 2.3.5"])
|
61
61
|
s.add_runtime_dependency(%q<right_aws>, [">= 1.10.0"])
|
62
62
|
s.add_runtime_dependency(%q<rmagick>, [">= 2.12.2"])
|
63
|
+
s.add_runtime_dependency(%q<mime-types>, [">= 1.16"])
|
63
64
|
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
64
65
|
s.add_development_dependency(%q<cucumber>, [">= 0.6.2"])
|
65
66
|
else
|
66
67
|
s.add_dependency(%q<activesupport>, [">= 2.3.5"])
|
67
68
|
s.add_dependency(%q<right_aws>, [">= 1.10.0"])
|
68
69
|
s.add_dependency(%q<rmagick>, [">= 2.12.2"])
|
70
|
+
s.add_dependency(%q<mime-types>, [">= 1.16"])
|
69
71
|
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
70
72
|
s.add_dependency(%q<cucumber>, [">= 0.6.2"])
|
71
73
|
end
|
@@ -73,6 +75,7 @@ Gem::Specification.new do |s|
|
|
73
75
|
s.add_dependency(%q<activesupport>, [">= 2.3.5"])
|
74
76
|
s.add_dependency(%q<right_aws>, [">= 1.10.0"])
|
75
77
|
s.add_dependency(%q<rmagick>, [">= 2.12.2"])
|
78
|
+
s.add_dependency(%q<mime-types>, [">= 1.16"])
|
76
79
|
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
77
80
|
s.add_dependency(%q<cucumber>, [">= 0.6.2"])
|
78
81
|
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 2
|
9
|
+
version: 0.0.2
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Andrew Bruce
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-04-
|
17
|
+
date: 2010-04-11 00:00:00 +01:00
|
18
18
|
default_executable: ungulate_server.rb
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -60,9 +60,22 @@ dependencies:
|
|
60
60
|
type: :runtime
|
61
61
|
version_requirements: *id003
|
62
62
|
- !ruby/object:Gem::Dependency
|
63
|
-
name:
|
63
|
+
name: mime-types
|
64
64
|
prerelease: false
|
65
65
|
requirement: &id004 !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
segments:
|
70
|
+
- 1
|
71
|
+
- 16
|
72
|
+
version: "1.16"
|
73
|
+
type: :runtime
|
74
|
+
version_requirements: *id004
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: rspec
|
77
|
+
prerelease: false
|
78
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
66
79
|
requirements:
|
67
80
|
- - ">="
|
68
81
|
- !ruby/object:Gem::Version
|
@@ -72,11 +85,11 @@ dependencies:
|
|
72
85
|
- 9
|
73
86
|
version: 1.2.9
|
74
87
|
type: :development
|
75
|
-
version_requirements: *
|
88
|
+
version_requirements: *id005
|
76
89
|
- !ruby/object:Gem::Dependency
|
77
90
|
name: cucumber
|
78
91
|
prerelease: false
|
79
|
-
requirement: &
|
92
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
80
93
|
requirements:
|
81
94
|
- - ">="
|
82
95
|
- !ruby/object:Gem::Version
|
@@ -86,7 +99,7 @@ dependencies:
|
|
86
99
|
- 2
|
87
100
|
version: 0.6.2
|
88
101
|
type: :development
|
89
|
-
version_requirements: *
|
102
|
+
version_requirements: *id006
|
90
103
|
description: WIP
|
91
104
|
email: andrew@camelpunch.com
|
92
105
|
executables:
|