happening 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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" unless aws_access_key_id.present? && aws_secret_access_key.present?
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' => Time.now.httpdate
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
@@ -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.symbolize_keys)
24
- options.assert_valid_keys(:timeout, :server, :protocol, :aws_access_key_id, :aws_secret_access_key, :retry_count, :permissions, :ssl)
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].present?
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 bucket.present?
85
- raise ArgumentError, "need a AWS Key" unless aws_id.present?
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].present?
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])
@@ -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
- options.assert_valid_keys(:timeout, :on_success, :on_error, :retry_count, :headers, :data, :ssl)
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 new_location.blank?
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 'active_support'
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
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + "/test_helper"
1
+ require File.expand_path('../test_helper', __FILE__)
2
2
 
3
3
  class ItemTest < Test::Unit::TestCase
4
4
  context "An Happening::AWS instance" do
data/test/s3/item_test.rb CHANGED
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + "/../test_helper"
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(stub(:httpdate => @time, :to_i => 99, :usec => 88))
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(mock(:httpdate => time))
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
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + "/../test_helper"
1
+ require File.expand_path('../../test_helper', __FILE__)
2
2
 
3
3
  class ItemTest < Test::Unit::TestCase
4
4
  context "An Happening::S3::Request instance" do
data/test/s3_test.rb CHANGED
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + "/test_helper"
1
+ require File.expand_path('../test_helper', __FILE__)
2
2
 
3
3
  class S3Test < Test::Unit::TestCase
4
4
  context "The Happening::S3 module" do
data/test/test_helper.rb CHANGED
@@ -91,4 +91,4 @@ module EventMachine
91
91
  class MockHttpRequest
92
92
  @@pass_through_requests = false
93
93
  end
94
- end
94
+ end
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: 27
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 1
8
+ - 2
9
9
  - 0
10
- version: 0.1.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: 2010-09-28 00:00:00 +02:00
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: activesupport
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: :runtime
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