happening 0.1.0 → 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.
- data/README.md +15 -0
- data/lib/happening/aws.rb +9 -3
- data/lib/happening/s3/item.rb +15 -7
- data/lib/happening/s3/request.rb +5 -4
- data/lib/happening/utils.rb +26 -0
- data/lib/happening.rb +1 -5
- data/test/aws_test.rb +1 -1
- data/test/s3/item_test.rb +22 -4
- data/test/s3/request_test.rb +1 -1
- data/test/s3_test.rb +1 -1
- data/test/test_helper.rb +1 -1
- metadata +35 -6
data/README.md
CHANGED
@@ -124,6 +124,21 @@ Happening support the simple S3 PUT upload:
|
|
124
124
|
|
125
125
|
Amazon returns no content on delete, so having a success handler is usually not needed for delete operations.
|
126
126
|
|
127
|
+
Head
|
128
|
+
=============
|
129
|
+
|
130
|
+
You can also just load the headers of an S3 item:
|
131
|
+
|
132
|
+
EM.run do
|
133
|
+
on_error = Proc.new {|response| puts "An error occured: #{response.response_header.status}"; EM.stop }
|
134
|
+
item = Happening::S3::Item.new('bucket', 'item_id', :aws_access_key_id => 'Your-ID', :aws_secret_access_key => 'secret')
|
135
|
+
item.head(:on_error => on_error) do |response|
|
136
|
+
puts "Headers: #{response.inspect}"
|
137
|
+
EM.stop
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
|
127
142
|
SSL Support
|
128
143
|
=============
|
129
144
|
|
data/lib/happening/aws.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
+
require 'time'
|
1
2
|
module Happening
|
2
3
|
class AWS
|
4
|
+
include Utils
|
3
5
|
|
4
6
|
AMAZON_HEADER_PREFIX = 'x-amz-'
|
5
7
|
AMAZON_METADATA_PREFIX = 'x-amz-meta-'
|
@@ -10,12 +12,12 @@ module Happening
|
|
10
12
|
def initialize(aws_access_key_id, aws_secret_access_key)
|
11
13
|
@aws_access_key_id = aws_access_key_id
|
12
14
|
@aws_secret_access_key = aws_secret_access_key
|
13
|
-
raise ArgumentError, "need AWS Access Key Id and AWS Secret Key"
|
15
|
+
raise ArgumentError, "need AWS Access Key Id and AWS Secret Key" if blank?(aws_access_key_id) || blank?(aws_secret_access_key)
|
14
16
|
end
|
15
17
|
|
16
18
|
def sign(method, path, headers={})
|
17
19
|
headers = {
|
18
|
-
'date' =>
|
20
|
+
'date' => utc_httpdate
|
19
21
|
}.update(headers)
|
20
22
|
|
21
23
|
request_description = canonical_request_description(method, path, headers)
|
@@ -24,6 +26,10 @@ module Happening
|
|
24
26
|
|
25
27
|
protected
|
26
28
|
|
29
|
+
def utc_httpdate
|
30
|
+
Time.now.utc.httpdate
|
31
|
+
end
|
32
|
+
|
27
33
|
def generate_signature(request_description)
|
28
34
|
Base64.encode64(OpenSSL::HMAC.digest(DIGEST, aws_secret_access_key, request_description)).strip
|
29
35
|
end
|
@@ -56,4 +62,4 @@ module Happening
|
|
56
62
|
end
|
57
63
|
|
58
64
|
end
|
59
|
-
end
|
65
|
+
end
|
data/lib/happening/s3/item.rb
CHANGED
@@ -4,12 +4,13 @@ require 'cgi'
|
|
4
4
|
module Happening
|
5
5
|
module S3
|
6
6
|
class Item
|
7
|
+
include Utils
|
7
8
|
|
8
9
|
REQUIRED_FIELDS = [:server]
|
9
10
|
VALID_HEADERS = ['Cache-Control', 'Content-Disposition', 'Content-Encoding', 'Content-Length', 'Content-MD5', 'Content-Type', 'Expect', 'Expires']
|
10
11
|
|
11
12
|
attr_accessor :bucket, :aws_id, :options
|
12
|
-
|
13
|
+
|
13
14
|
def initialize(bucket, aws_id, options = {})
|
14
15
|
@options = {
|
15
16
|
:timeout => 10,
|
@@ -20,14 +21,21 @@ module Happening
|
|
20
21
|
:retry_count => 4,
|
21
22
|
:permissions => 'private',
|
22
23
|
:ssl => Happening::S3.ssl_options
|
23
|
-
}.update(options
|
24
|
-
|
24
|
+
}.update(symbolize_keys(options))
|
25
|
+
assert_valid_keys(options, :timeout, :server, :protocol, :aws_access_key_id, :aws_secret_access_key, :retry_count, :permissions, :ssl)
|
25
26
|
@aws_id = aws_id.to_s
|
26
27
|
@bucket = bucket.to_s
|
27
28
|
|
28
29
|
validate
|
29
30
|
end
|
30
31
|
|
32
|
+
def head(request_options = {}, &blk)
|
33
|
+
headers = needs_to_sign? ? aws.sign("HEAD", path) : {}
|
34
|
+
request_options[:on_success] = blk if blk
|
35
|
+
request_options.update(:headers => headers)
|
36
|
+
Happening::S3::Request.new(:head, url, {:ssl => options[:ssl]}.update(request_options)).execute
|
37
|
+
end
|
38
|
+
|
31
39
|
def get(request_options = {}, &blk)
|
32
40
|
headers = needs_to_sign? ? aws.sign("GET", path) : {}
|
33
41
|
request_options[:on_success] = blk if blk
|
@@ -64,7 +72,7 @@ module Happening
|
|
64
72
|
protected
|
65
73
|
|
66
74
|
def needs_to_sign?
|
67
|
-
options[:aws_access_key_id]
|
75
|
+
present?(options[:aws_access_key_id])
|
68
76
|
end
|
69
77
|
|
70
78
|
def dns_bucket?
|
@@ -81,11 +89,11 @@ module Happening
|
|
81
89
|
end
|
82
90
|
|
83
91
|
def validate
|
84
|
-
raise ArgumentError, "need a bucket name" unless
|
85
|
-
raise ArgumentError, "need a AWS Key" unless
|
92
|
+
raise ArgumentError, "need a bucket name" unless present?(bucket)
|
93
|
+
raise ArgumentError, "need a AWS Key" unless present?(aws_id)
|
86
94
|
|
87
95
|
REQUIRED_FIELDS.each do |field|
|
88
|
-
raise ArgumentError, "need field #{field}" unless options[field]
|
96
|
+
raise ArgumentError, "need field #{field}" unless present?(options[field])
|
89
97
|
end
|
90
98
|
|
91
99
|
raise ArgumentError, "unknown protocoll #{options[:protocol]}" unless ['http', 'https'].include?(options[:protocol])
|
data/lib/happening/s3/request.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
module Happening
|
2
2
|
module S3
|
3
3
|
class Request
|
4
|
+
include Utils
|
4
5
|
|
5
|
-
VALID_HTTP_METHODS = [:get, :put, :delete]
|
6
|
+
VALID_HTTP_METHODS = [:head, :get, :put, :delete]
|
6
7
|
|
7
8
|
attr_accessor :http_method, :url, :options, :response
|
8
9
|
|
@@ -19,7 +20,7 @@ module Happening
|
|
19
20
|
:verify_peer => false
|
20
21
|
}
|
21
22
|
}.update(options)
|
22
|
-
|
23
|
+
assert_valid_keys(options, :timeout, :on_success, :on_error, :retry_count, :headers, :data, :ssl)
|
23
24
|
@http_method = http_method
|
24
25
|
@url = url
|
25
26
|
|
@@ -99,7 +100,7 @@ module Happening
|
|
99
100
|
|
100
101
|
def handle_redirect
|
101
102
|
new_location = response.response_header['LOCATION'] rescue ''
|
102
|
-
raise "Could not find the location to redirect to, empty location header?" if
|
103
|
+
raise "Could not find the location to redirect to, empty location header?" if blank?(new_location)
|
103
104
|
|
104
105
|
new_request = self.class.new(http_method, new_location, options)
|
105
106
|
new_request.execute
|
@@ -107,4 +108,4 @@ module Happening
|
|
107
108
|
|
108
109
|
end
|
109
110
|
end
|
110
|
-
end
|
111
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Happening
|
2
|
+
module Utils
|
3
|
+
protected
|
4
|
+
|
5
|
+
def symbolize_keys(hash)
|
6
|
+
hash.inject({}) do |h, kv|
|
7
|
+
h[kv[0].to_sym] = kv[1]
|
8
|
+
h
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def assert_valid_keys(hash, *valid_keys)
|
13
|
+
unknown_keys = hash.keys - [valid_keys].flatten
|
14
|
+
raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty?
|
15
|
+
end
|
16
|
+
|
17
|
+
def present?(obj)
|
18
|
+
!blank?(obj)
|
19
|
+
end
|
20
|
+
|
21
|
+
def blank?(obj)
|
22
|
+
obj.respond_to?(:empty?) ? obj.empty? : !obj
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
data/lib/happening.rb
CHANGED
@@ -3,11 +3,7 @@ require 'em-http'
|
|
3
3
|
require 'openssl'
|
4
4
|
require 'logger'
|
5
5
|
|
6
|
-
require '
|
7
|
-
unless {}.respond_to?(:assert_valid_keys)
|
8
|
-
require 'active_support/core_ext'
|
9
|
-
end
|
10
|
-
|
6
|
+
require File.dirname(__FILE__) + '/happening/utils'
|
11
7
|
require File.dirname(__FILE__) + '/happening/log'
|
12
8
|
require File.dirname(__FILE__) + '/happening/aws'
|
13
9
|
require File.dirname(__FILE__) + '/happening/s3'
|
data/test/aws_test.rb
CHANGED
data/test/s3/item_test.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.
|
1
|
+
require File.expand_path('../../test_helper', __FILE__)
|
2
2
|
|
3
3
|
class ItemTest < Test::Unit::TestCase
|
4
4
|
context "An Happening::S3::Item instance" do
|
@@ -8,7 +8,8 @@ class ItemTest < Test::Unit::TestCase
|
|
8
8
|
@item = Happening::S3::Item.new('the-bucket', 'the-key', :aws_access_key_id => '123', :aws_secret_access_key => 'secret', :server => '127.0.0.1')
|
9
9
|
|
10
10
|
@time = "Thu, 25 Feb 2010 10:00:00 GMT"
|
11
|
-
Time.stubs(:now).returns(
|
11
|
+
Time.stubs(:now).returns(Time.parse(@time))
|
12
|
+
#stub(:utc_httpdate => @time, :to_i => 99, :usec => 88))
|
12
13
|
end
|
13
14
|
|
14
15
|
context "validation" do
|
@@ -106,7 +107,7 @@ class ItemTest < Test::Unit::TestCase
|
|
106
107
|
|
107
108
|
should "sign requests if AWS credentials are passend" do
|
108
109
|
time = "Thu, 25 Feb 2010 12:06:33 GMT"
|
109
|
-
Time.stubs(:now).returns(
|
110
|
+
Time.stubs(:now).returns(Time.parse(time))
|
110
111
|
EventMachine::MockHttpRequest.register('https://bucket.s3.amazonaws.com:443/the-key', :get, {"Authorization"=>"AWS abc:3OEcVbE//maUUmqh3A5ETEcr9TE=", 'date' => time}, fake_response("data-here"))
|
111
112
|
|
112
113
|
@item = Happening::S3::Item.new('bucket', 'the-key', :aws_access_key_id => 'abc', :aws_secret_access_key => '123')
|
@@ -254,7 +255,24 @@ class ItemTest < Test::Unit::TestCase
|
|
254
255
|
end
|
255
256
|
end
|
256
257
|
end
|
257
|
-
|
258
|
+
|
259
|
+
context "when loading the headers" do
|
260
|
+
should "request via HEAD" do
|
261
|
+
EventMachine::MockHttpRequest.register('https://bucket.s3.amazonaws.com:443/the-key', :head, {}, fake_response('hy there'))
|
262
|
+
|
263
|
+
@item = Happening::S3::Item.new('bucket', 'the-key')
|
264
|
+
run_in_em_loop do
|
265
|
+
@item.head
|
266
|
+
|
267
|
+
EM.add_timer(1) {
|
268
|
+
EM.stop_event_loop
|
269
|
+
assert_equal 1, EventMachine::MockHttpRequest.count('https://bucket.s3.amazonaws.com:443/the-key', :head, {})
|
270
|
+
}
|
271
|
+
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
258
276
|
context "when saving an item" do
|
259
277
|
|
260
278
|
should "post to the desired location" do
|
data/test/s3/request_test.rb
CHANGED
data/test/s3_test.rb
CHANGED
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: happening
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 2
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Jonathan Weiss
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2011-05-20 00:00:00 +02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -33,7 +33,7 @@ dependencies:
|
|
33
33
|
type: :runtime
|
34
34
|
version_requirements: *id001
|
35
35
|
- !ruby/object:Gem::Dependency
|
36
|
-
name:
|
36
|
+
name: jeweler
|
37
37
|
prerelease: false
|
38
38
|
requirement: &id002 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
@@ -44,8 +44,36 @@ dependencies:
|
|
44
44
|
segments:
|
45
45
|
- 0
|
46
46
|
version: "0"
|
47
|
-
type: :
|
47
|
+
type: :development
|
48
48
|
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: shoulda
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :development
|
62
|
+
version_requirements: *id003
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: mocha
|
65
|
+
prerelease: false
|
66
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
hash: 3
|
72
|
+
segments:
|
73
|
+
- 0
|
74
|
+
version: "0"
|
75
|
+
type: :development
|
76
|
+
version_requirements: *id004
|
49
77
|
description: An EventMachine based S3 client - using em-http-request
|
50
78
|
email: info@peritor.com
|
51
79
|
executables: []
|
@@ -64,6 +92,7 @@ files:
|
|
64
92
|
- lib/happening/s3.rb
|
65
93
|
- lib/happening/s3/item.rb
|
66
94
|
- lib/happening/s3/request.rb
|
95
|
+
- lib/happening/utils.rb
|
67
96
|
- test/aws_test.rb
|
68
97
|
- test/s3/item_test.rb
|
69
98
|
- test/s3/request_test.rb
|