em-amazon-sqs 0.1.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.
@@ -0,0 +1,24 @@
1
+ module SimpleQS
2
+ module Request
3
+ autoload :Base, 'simple_qs/request/base'
4
+ autoload :Get, 'simple_qs/request/get'
5
+ autoload :Post, 'simple_qs/request/post'
6
+
7
+ class UnknownHttpMethod < StandardError; end
8
+
9
+ HTTP_METHODS = {
10
+ :get => :Get,
11
+ :post => :Post
12
+ }
13
+
14
+ class << self
15
+ def build(request, params = {})
16
+ if SimpleQS::Request::HTTP_METHODS.keys.include?(request)
17
+ SimpleQS::Request.const_get(HTTP_METHODS[request]).new(params)
18
+ else
19
+ raise SimpleQS::Request::UnknownHttpMethod, "Method #{request} is unknown or unsupported"
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,105 @@
1
+ require 'uri'
2
+ require 'net/http'
3
+ require 'base64'
4
+ require 'hmac-sha1'
5
+
6
+ module SimpleQS
7
+ module Request
8
+ class Base
9
+
10
+ RESERVED_CHARACTERS = /[^a-zA-Z0-9\-\.\_\~]/
11
+ SIGNATURE_VERSION = 2
12
+ SIGNATURE_METHOD = 'HmacSHA1'
13
+
14
+ def initialize(params = {})
15
+ self.query_params = params
16
+ end
17
+
18
+ def ==(other)
19
+ self.class.http_method == other.class.http_method\
20
+ && query_params == other.query_params\
21
+ && query_string == other.query_string
22
+ end
23
+
24
+ def timestamp
25
+ @timestamp ||= _timestamp
26
+ end
27
+
28
+ def timestamp=(time)
29
+ raise ArgumentError, "expected Time object, bug got #{time.class.to_s} instead." unless time.kind_of?(Time)
30
+ @timestamp = time.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
31
+ end
32
+
33
+ def query_params
34
+ @query_params ||= {}
35
+ @query_params = {
36
+ 'SignatureVersion' => SIGNATURE_VERSION,
37
+ 'SignatureMethod' => SIGNATURE_METHOD,
38
+ 'Version' => SimpleQS::API_VERSION,
39
+ 'Timestamp' => timestamp,
40
+ 'AWSAccessKeyId' => SimpleQS.access_key_id
41
+ }.merge(@query_params)
42
+ @query_params.delete('Timestamp') if @query_params['Expires']
43
+
44
+ @query_params
45
+ end
46
+ attr_writer :query_params
47
+
48
+ def update_query_params(params)
49
+ @query_params = query_params.merge params
50
+ end
51
+
52
+ def query_string
53
+ @query_string ||= "/"
54
+ end
55
+
56
+ def query_string=(value)
57
+ value = value.join('/') if value.kind_of?(Array)
58
+ @query_string = (value =~ /^\// ? value : "/#{value}")
59
+ end
60
+
61
+ # Canonicalizes query string
62
+ def canonical_query_string
63
+ params_to_query(query_params.sort)
64
+ end
65
+
66
+ def signature_base_string
67
+ [
68
+ self.class.http_method.to_s.upcase,
69
+ SimpleQS.host.downcase,
70
+ query_string,
71
+ canonical_query_string
72
+ ].join("\n")
73
+ end
74
+
75
+ def uri(with_query_params = false)
76
+ "http://#{SimpleQS.host}#{query_string}" << (with_query_params ? "?#{params_to_query(query_params)}" : '')
77
+ end
78
+
79
+ def sign!
80
+ update_query_params({
81
+ 'Signature' => Base64.encode64(HMAC::SHA1.digest(SimpleQS.secret_access_key, signature_base_string)).chomp
82
+ })
83
+ self
84
+ end
85
+
86
+ def params_to_query(params)
87
+ params.map {|pair| pair.map {|value| URI.escape(value.to_s, RESERVED_CHARACTERS)}.join('=')}.join('&')
88
+ end
89
+
90
+ class << self
91
+ def http_method(value = nil)
92
+ @http_method = value if value
93
+ @http_method
94
+ end
95
+ end
96
+
97
+ protected
98
+
99
+ # Generates UTC timestamp in dateTime object format
100
+ def _timestamp
101
+ Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,14 @@
1
+ module SimpleQS
2
+ module Request
3
+ class Get < Base
4
+ http_method :get
5
+
6
+ def perform
7
+ sign!
8
+ http = EM::HttpRequest.new(URI.parse(uri(true))).get
9
+ #SimpleQS::Responce.new Net::HTTP.get(URI.parse(uri(true)))
10
+ SimpleQS::Responce.new http.response
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module SimpleQS
2
+ module Request
3
+ class Post < Base
4
+ http_method :post
5
+
6
+ def perform
7
+ sign!
8
+ http = EM::HttpRequest.new(URI.parse(uri)).post :body => query_params
9
+ #SimpleQS::Responce.new Net::HTTP.post_form(URI.parse(uri), query_params).body
10
+ SimpleQS::Responce.new http.response
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,30 @@
1
+ require 'xmlsimple'
2
+
3
+ require 'simple_qs/responce/exceptions'
4
+
5
+ module SimpleQS
6
+ class Responce
7
+
8
+ autoload :SuccessfulBuilder, 'simple_qs/responce/successful_builder'
9
+ autoload :FailureBuilder, 'simple_qs/responce/failure_builder'
10
+
11
+ def initialize(xml)
12
+ _parse xml
13
+ end
14
+
15
+ def successful?
16
+ !@xml_data.key?('ErrorResponse')
17
+ end
18
+
19
+ def root_element
20
+ @xml_data.keys[0]
21
+ end
22
+
23
+ private
24
+
25
+ def _parse(xml)
26
+ @xml_data = XmlSimple.xml_in(xml, {'ForceArray' => false, 'KeepRoot' => true})
27
+ (successful? ? SuccessfulBuilder : FailureBuilder).build(self)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,39 @@
1
+ module SimpleQS
2
+ class Responce
3
+
4
+ class Error < StandardError; end
5
+
6
+ class AccessDeniedError < Error; end
7
+ class AuthFailureError < Error; end
8
+ class AWSSimpleQueueServiceInternalError < Error; end
9
+ class AWSSimpleQueueServiceNonExistentQueueError < Error; end
10
+ class AWSSimpleQueueServiceQueueDeletedRecently < Error; end
11
+ class AWSSimpleQueueServiceQueueNameExists < Error; end
12
+ class ConflictingQueryParameterError < Error; end
13
+ class InvalidParameterValueError < Error; end
14
+ class InternalError < Error; end
15
+ class InvalidAccessKeyIdError < Error; end
16
+ class InvalidActionError < Error; end
17
+ class InvalidAddressError < Error; end
18
+ class InvalidClientTokenIdError < Error; end
19
+ class InvalidHttpRequestError < Error; end
20
+ class InvalidParameterCombinationError < Error; end
21
+ class InvalidParameterValueError < Error; end
22
+ class InvalidQueryParameterError < Error; end
23
+ class InvalidRequestError < Error; end
24
+ class InvalidSecurityError < Error; end
25
+ class InvalidSecurityTokenError < Error; end
26
+ class MalformedVersionError < Error; end
27
+ class MissingClientTokenIdError < Error; end
28
+ class MissingCredentialsError < Error; end
29
+ class MissingParameterError < Error; end
30
+ class NoSuchVersionError < Error; end
31
+ class NotAuthorizedToUseVersionError < Error; end
32
+ class OptInRequiredError < Error; end
33
+ class RequestExpiredError < Error; end
34
+ class RequestThrottledError < Error; end
35
+ class ServiceUnavailableError < Error; end
36
+ class SignatureDoesNotMatchError < Error; end
37
+ class X509ParseError < Error; end
38
+ end
39
+ end
@@ -0,0 +1,33 @@
1
+ module SimpleQS
2
+ class Responce
3
+ class FailureBuilder
4
+ def self.build responce
5
+ responce.instance_eval do
6
+ def sender_error?
7
+ @xml_data[root_element]['Error']['Type'] == 'Sender'
8
+ end
9
+
10
+ def receiver_error?
11
+ @xml_data[root_element]['Error']['Type'] == 'Receiver'
12
+ end
13
+
14
+ def request_id
15
+ @xml_data[root_element]['RequestId']
16
+ end
17
+
18
+ def error_code
19
+ @xml_data[root_element]['Error']['Code'].gsub(/\./, '')
20
+ end
21
+
22
+ def error_message
23
+ @xml_data[root_element]['Error']['Message']
24
+ end
25
+
26
+ def to_error
27
+ self.class.const_get(error_code =~ /Error$/ ? error_code : "#{error_code}Error").new error_message
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,26 @@
1
+ module SimpleQS
2
+ class Responce
3
+ class SuccessfulBuilder
4
+ def self.build responce
5
+ responce.instance_eval do
6
+
7
+ def action_name
8
+ @action_name ||= root_element.gsub(/Response/, '')
9
+ end
10
+
11
+ def request_id
12
+ @xml_data[root_element]['ResponseMetadata']['RequestId']
13
+ end
14
+
15
+ @xml_data[root_element]["#{action_name}Result"].each do |key, value|
16
+ self.instance_eval %{
17
+ def #{key.gsub(/([A-Z]+)/, '_\1').downcase.gsub(/^_/, '')}
18
+ #{value.inspect}
19
+ end
20
+ }
21
+ end if @xml_data[root_element]["#{action_name}Result"]
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
data/lib/version.rb ADDED
@@ -0,0 +1,3 @@
1
+ module SimpleQS
2
+ VERSION = '0.2'
3
+ end
data/simple_qs.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'lib', 'version'))
2
+
3
+ Gem::Specification.new do |s|
4
+ s.platform = Gem::Platform::RUBY
5
+ s.name = 'simple_qs'
6
+ s.version = SimpleQS::VERSION
7
+ s.summary = 'Amazon SQS service library'
8
+ s.description = 'SimpleQS is Ruby wrapper for Amazon Simple Queue Service (SQS) REST API. It allows you perform all kind of calls on queues and messages.'
9
+
10
+ s.required_ruby_version = '>= 1.8.7'
11
+ s.required_rubygems_version = ">= 1.3.6"
12
+
13
+ s.author = "Marjan Krekoten' (Мар'ян Крекотень)"
14
+ s.email = 'krekoten@gmail.com'
15
+ s.homepage = 'http://github.com/krekoten/SimpleQS'
16
+
17
+ s.add_dependency('xml-simple', '>= 1.0.12')
18
+ s.add_dependency('ruby-hmac', '>= 0.3.2')
19
+
20
+ s.add_development_dependency("rspec")
21
+
22
+ s.require_path = 'lib'
23
+ s.files = Dir.glob("lib/**/*") + %w(LICENSE README.md CHANGELOG.md TODO.md)
24
+ end
@@ -0,0 +1,114 @@
1
+ require 'spec_helper'
2
+
3
+ module SimpleQS
4
+ describe Message do
5
+ before :all do
6
+ @queue = SimpleQS::Queue.new('testQueue')
7
+ end
8
+
9
+ it 'should accept string as message' do
10
+ string = 'Test message from SimpleQS::Message spec'
11
+ message = SimpleQS::Message.new(@queue, string)
12
+ message.body.should == string
13
+ end
14
+
15
+ it 'should return message instance when sent from class method' do
16
+ string = 'Test message from SimpleQS::Message spec'
17
+
18
+ message = SimpleQS::Message.send(@queue, string)
19
+
20
+ message.should be_instance_of(SimpleQS::Message)
21
+ message.body.should == string
22
+ message.message_id.should_not be_empty
23
+ end
24
+
25
+ it 'should receive messages' do
26
+ messages = SimpleQS::Message.receive(@queue)
27
+ messages.should have_at_least(1).message
28
+ messages[0].should be_instance_of(SimpleQS::Message)
29
+ end
30
+
31
+ it 'should receive at least 3 messages with attributes' do
32
+ string = 'Test message from SimpleQS::Message spec'
33
+ message = SimpleQS::Message.new(@queue, string)
34
+ message.send
35
+ message.resend
36
+ message.resend
37
+ message.resend
38
+
39
+ messages = SimpleQS::Message.receive(@queue, :All, 3, 30)
40
+ messages.should have_at_least(3).message
41
+ messages[0].should be_instance_of(SimpleQS::Message)
42
+ end
43
+
44
+ describe 'when message is not sent' do
45
+ before :each do
46
+ @message = SimpleQS::Message.new(@queue, 'Test message from SimpleQS::Message spec')
47
+ end
48
+
49
+ it "should be sendable" do
50
+ lambda do
51
+ @message.send
52
+ end.should_not raise_error
53
+ end
54
+ end
55
+
56
+ describe 'when message is sent' do
57
+ before :each do
58
+ @message = SimpleQS::Message.new(@queue, 'Test message from SimpleQS::Message spec')
59
+ @message.send
60
+ end
61
+
62
+ it "should not be sendable" do
63
+ lambda do
64
+ @message.send
65
+ end.should raise_error(SimpleQS::Message::DoubleSendError)
66
+ end
67
+
68
+ it 'should be able to be resend' do
69
+ lambda do
70
+ @message.resend
71
+ end.should_not raise_error
72
+ end
73
+
74
+ describe 'and then is resent' do
75
+ it 'should not be equal to orginal message' do
76
+ new_message = @message.resend
77
+ new_message.should_not == @message
78
+ end
79
+ end
80
+ end
81
+
82
+ describe 'when receive messages' do
83
+ before :each do
84
+ @queue.send_message('Message 1')
85
+ @queue.send_message('Message 2')
86
+ @queue.send_message('Message 3')
87
+ @messages = SimpleQS::Message.receive(@queue, nil, 10)
88
+ end
89
+
90
+ it 'should be able to delete message' do
91
+ lambda do
92
+ @messages.each do |message|
93
+ message.delete
94
+ end
95
+ end.should_not raise_error
96
+ end
97
+ end
98
+
99
+ describe 'change visibility of message' do
100
+ it 'should be able when message is received' do
101
+ lambda do
102
+ @queue.send_message('Message')
103
+ SimpleQS::Message.receive(@queue).last.change_visibility(120)
104
+ end.should_not raise_error
105
+ end
106
+
107
+ it 'should not be able when message is not received' do
108
+ lambda do
109
+ SimpleQS::Message.new(@queue, 'Message').change_visibility(120)
110
+ end.should raise_error(SimpleQS::Message::NotReceivedError)
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,145 @@
1
+ require 'spec_helper'
2
+
3
+ module SimpleQS
4
+ describe Queue do
5
+ describe 'create method' do
6
+ it 'should raise error if queue name is longer than 80 characters' do
7
+ lambda do
8
+ SimpleQS::Queue.create('a' * 81)
9
+ end.should raise_error(ArgumentError)
10
+ end
11
+
12
+ it 'should accept only alphanumeric characters, hyphens (-), and underscores (_)' do
13
+ lambda do
14
+ SimpleQS::Queue.check_queue_name('test&queue')
15
+ end.should raise_error(ArgumentError)
16
+
17
+ lambda do
18
+ SimpleQS::Queue.check_queue_name('test_queue-123ABC')
19
+ end.should_not raise_error(ArgumentError)
20
+ end
21
+
22
+ it 'should raise error if passed too long visibility timeout' do
23
+ lambda do
24
+ SimpleQS::Queue.check_visibility_timeout(SimpleQS::Queue::MAX_VISIBILITY_TIMEOUT + 1)
25
+ end.should raise_error(SimpleQS::Queue::MaxVisibilityError)
26
+ end
27
+
28
+ it 'should raise error if passed negative visibility timeout' do
29
+ lambda do
30
+ SimpleQS::Queue.check_visibility_timeout(-1)
31
+ end.should raise_error(SimpleQS::Queue::MaxVisibilityError)
32
+ end
33
+
34
+ it 'should raise error if visibility timeout is not fixnum' do
35
+ lambda do
36
+ SimpleQS::Queue.check_visibility_timeout(Object.new)
37
+ end.should raise_error(ArgumentError)
38
+ end
39
+
40
+ it 'should return SimpleQS::Queue instance' do
41
+ SimpleQS::Queue.create('testQueue').should be_instance_of(SimpleQS::Queue)
42
+ end
43
+
44
+ it 'should pass SimpleQS::Queue instance to block' do
45
+ _queue = nil
46
+ SimpleQS::Queue.create('testQueue') do |queue|
47
+ _queue = queue
48
+ end
49
+
50
+ _queue.should be_instance_of(SimpleQS::Queue)
51
+ end
52
+ end
53
+
54
+ it 'should have at least 1 queue' do
55
+ SimpleQS::Queue.list.should have_at_least(1).queue
56
+ end
57
+
58
+ it 'should find at least 1 queue by beggining of the name' do
59
+ SimpleQS::Queue.list('test').should have_at_least(1).queue
60
+ end
61
+
62
+ it 'should find 0 queues with nonexistent names' do
63
+ SimpleQS::Queue.list('nonExistantQueueName').should have(0).queue
64
+ end
65
+
66
+ it 'should be able to get all queue attributes' do
67
+ SimpleQS::Queue.new('testQueue').get_attributes(:All).should_not be_empty
68
+ end
69
+
70
+ it 'should be able to change default visibility timeout for queue' do
71
+ lambda do
72
+ SimpleQS::Queue.new('testQueue').set_visibility_timeout(60)
73
+ end.should_not raise_error
74
+ end
75
+
76
+ it 'should be able to change maximum message size' do
77
+ lambda do
78
+ SimpleQS::Queue.new('testQueue').set_maximum_message_size(65536)
79
+ end.should_not raise_error
80
+ end
81
+
82
+ it 'should raise error if message size is wrong' do
83
+ lambda do
84
+ SimpleQS::Queue.new('testQueue').set_maximum_message_size(65537)
85
+ end.should raise_error(SimpleQS::Queue::MaxMessageSizeError)
86
+
87
+ lambda do
88
+ SimpleQS::Queue.new('testQueue').set_maximum_message_size(1023)
89
+ end.should raise_error(SimpleQS::Queue::MaxMessageSizeError)
90
+ end
91
+
92
+ it 'should be able to change message retention period' do
93
+ lambda do
94
+ SimpleQS::Queue.new('testQueue').set_message_retention_period(1209600)
95
+ end.should_not raise_error
96
+ end
97
+
98
+ it 'should raise error if message retention period is wrong' do
99
+ lambda do
100
+ SimpleQS::Queue.new('testQueue').set_message_retention_period(3599)
101
+ end.should raise_error(SimpleQS::Queue::MessageRetentionPeriodError)
102
+
103
+ lambda do
104
+ SimpleQS::Queue.new('testQueue').set_message_retention_period(1209601)
105
+ end.should raise_error(SimpleQS::Queue::MessageRetentionPeriodError)
106
+ end
107
+
108
+ it 'should be able to set default visibility timeout for queue using set_attributes' do
109
+ lambda do
110
+ SimpleQS::Queue.new('testQueue').set_attributes({:VisibilityTimeout => 30})
111
+ end.should_not raise_error
112
+ end
113
+
114
+ it 'should be able to add permissions' do
115
+ lambda do
116
+ SimpleQS::Queue.new('testQueue').add_permissions('testPerms', [
117
+ {:account_id => '098166147350', :action => 'SendMessage'},
118
+ {:account_id => '098166147350', :action => 'ReceiveMessage'}
119
+ ])
120
+ end.should_not raise_error
121
+ end
122
+
123
+ it 'should be able to remove permissions' do
124
+ lambda do
125
+ SimpleQS::Queue.new('testQueue').remove_permissions('testPerms')
126
+ end.should_not raise_error
127
+ end
128
+
129
+ it 'should respond true if queue exists' do
130
+ SimpleQS::Queue.exists?('testQueue').should == true
131
+ end
132
+
133
+ it 'should respond false if queue does not exist' do
134
+ SimpleQS::Queue.exists?('nonExistantQueue123').should == false
135
+ end
136
+
137
+ it 'should be able to delete queue' do
138
+ lambda do
139
+ queue_name = Time.now.to_i.to_s
140
+ SimpleQS::Queue.create(queue_name)
141
+ SimpleQS::Queue.delete(queue_name)
142
+ end.should_not raise_error
143
+ end
144
+ end
145
+ end