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 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