ungulate 0.0.1 → 0.0.2
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/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:
|