simple_qs 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +8 -0
- data/LICENSE +20 -0
- data/README.md +250 -0
- data/TODO.md +3 -0
- data/lib/simple_qs.rb +40 -0
- data/lib/simple_qs/message.rb +135 -0
- data/lib/simple_qs/queue.rb +273 -0
- data/lib/simple_qs/request.rb +24 -0
- data/lib/simple_qs/request/base.rb +105 -0
- data/lib/simple_qs/request/get.rb +12 -0
- data/lib/simple_qs/request/post.rb +12 -0
- data/lib/simple_qs/responce.rb +30 -0
- data/lib/simple_qs/responce/exceptions.rb +38 -0
- data/lib/simple_qs/responce/failure_builder.rb +33 -0
- data/lib/simple_qs/responce/successful_builder.rb +26 -0
- data/lib/version.rb +3 -0
- metadata +32 -5
data/CHANGELOG.md
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Marjan Krekoten'
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOa AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SaALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,250 @@
|
|
1
|
+
# English #
|
2
|
+
|
3
|
+
## Installation ##
|
4
|
+
|
5
|
+
[sudo] gem install simple_qs
|
6
|
+
|
7
|
+
## Usage ##
|
8
|
+
|
9
|
+
require 'simple_qs'
|
10
|
+
SimpleQS.account_id = 'your-account-id'
|
11
|
+
SimpleQS.access_key_id = 'YOUR_ACCESS_KEY'
|
12
|
+
SimpleQS.secret_access_key = 'YOUR_SECRET_ACCESS_KEY'
|
13
|
+
|
14
|
+
### Region ###
|
15
|
+
|
16
|
+
You can select region for queue:
|
17
|
+
|
18
|
+
# :default => 'queue.amazonaws.com'
|
19
|
+
# :us_west_1 => 'us-west-1.queue.amazonaws.com'
|
20
|
+
# :eu_west_1 => 'eu-west-1.queue.amazonaws.com'
|
21
|
+
SimpleQS.host # => 'queue.amazonaws.com'
|
22
|
+
|
23
|
+
SimpleQS.host = :eu_west_1
|
24
|
+
SimpleQS.host # => 'eu-west-1.queue.amazonaws.com'
|
25
|
+
|
26
|
+
### Queues ###
|
27
|
+
|
28
|
+
Create a queue:
|
29
|
+
|
30
|
+
queue = SimpleQS::Queue.create('queue_name')
|
31
|
+
|
32
|
+
Or:
|
33
|
+
|
34
|
+
SimpleQS::Queue.create('queue_name') do |queue|
|
35
|
+
# ...
|
36
|
+
end
|
37
|
+
|
38
|
+
Create a queue with specific default visibility timeout for received messages:
|
39
|
+
|
40
|
+
SimpleQS::Queue.create('queue_name', 60) # Visibility timeout set to 60 secinds instead of default 30
|
41
|
+
|
42
|
+
List all account's queues:
|
43
|
+
|
44
|
+
SimpleQS::Queue.list # returns list of SimpleQS::Queue objects
|
45
|
+
|
46
|
+
List all account's queues starting with 'test':
|
47
|
+
|
48
|
+
SimpleQS::Queue.list('test')
|
49
|
+
|
50
|
+
Change default visibility timeout for queue:
|
51
|
+
|
52
|
+
SimpleQS::Queue.new('queue_name').set_visibility_timeout(120)
|
53
|
+
|
54
|
+
Set queue attributes:
|
55
|
+
|
56
|
+
SimpleQS::Queue.new('queue_name').set_attributes({:VisibilityTimeout => 120, :Policy => "policy JSON here"})
|
57
|
+
|
58
|
+
Get queue attributes:
|
59
|
+
|
60
|
+
SimpleQS::Queue.new('queue_name').get_attributes(:All)
|
61
|
+
|
62
|
+
Add permissions:
|
63
|
+
|
64
|
+
SimpleQS::Queue.new('queue_name').add_permissions('testPerms', [
|
65
|
+
{:account_id => '098166147350', :action => 'SendMessage'},
|
66
|
+
{:account_id => '098166147350', :action => 'ReceiveMessage'}
|
67
|
+
])
|
68
|
+
|
69
|
+
Remove permissions:
|
70
|
+
|
71
|
+
SimpleQS::Queue.new('queue_name').remove_permissions('testPerms')
|
72
|
+
|
73
|
+
Delete queue:
|
74
|
+
|
75
|
+
SimpleQS::Queue.new('queue_name').delete
|
76
|
+
|
77
|
+
### Messages ###
|
78
|
+
|
79
|
+
Send message:
|
80
|
+
|
81
|
+
queue = SimpleQS::Queue.new('queue_name')
|
82
|
+
queue.send_message('message 1')
|
83
|
+
queue.send_message('message 2')
|
84
|
+
|
85
|
+
Or:
|
86
|
+
|
87
|
+
SimpleQS::Message.send(queue, 'message 1')
|
88
|
+
SimpleQS::Message.send(queue, 'message 2')
|
89
|
+
|
90
|
+
Receive message(s):
|
91
|
+
|
92
|
+
queue.receive_messages # always returns array of messages
|
93
|
+
queue.receive_messages(:All, 10, 70) # receive maximum 10 messages with all attributes (:All)
|
94
|
+
# and set for them visibility timeout to 70 seconds
|
95
|
+
|
96
|
+
Or:
|
97
|
+
|
98
|
+
SimpleQS::Message.receive(queue)
|
99
|
+
SimpleQS::Message.receive(queue, :All, 10, 70)
|
100
|
+
|
101
|
+
Resend received message:
|
102
|
+
|
103
|
+
messages = queue.receive_messages
|
104
|
+
messages.first.resend
|
105
|
+
|
106
|
+
Change visibility timeout for this message:
|
107
|
+
|
108
|
+
messages.first.change_visibility(120) # Change to 120 seconds
|
109
|
+
|
110
|
+
Delete received message:
|
111
|
+
|
112
|
+
messages.first.delete
|
113
|
+
|
114
|
+
## TODO ##
|
115
|
+
|
116
|
+
See [TODO](http://github.com/krekoten/SimpleQS/blob/master/TODO.md) file
|
117
|
+
|
118
|
+
## Issues ##
|
119
|
+
|
120
|
+
[ISSUES](http://github.com/krekoten/SimpleQS/issues)
|
121
|
+
|
122
|
+
## Authors ##
|
123
|
+
|
124
|
+
Marjan Krekoten' (krekoten@gmail.com)
|
125
|
+
|
126
|
+
# Українською #
|
127
|
+
|
128
|
+
## Встановлення ##
|
129
|
+
|
130
|
+
[sudo] gem install simple_qs
|
131
|
+
|
132
|
+
## Користування ##
|
133
|
+
|
134
|
+
require 'simple_qs'
|
135
|
+
SimpleQS.account_id = 'your-account-id'
|
136
|
+
SimpleQS.access_key_id = 'YOUR_ACCESS_KEY'
|
137
|
+
|
138
|
+
SimpleQS.secret_access_key = 'YOUR_SECRET_ACCESS_KEY'
|
139
|
+
|
140
|
+
### Регіон ###
|
141
|
+
|
142
|
+
Можна обрати регіон, в якому буде створена черга:
|
143
|
+
|
144
|
+
# :default => 'queue.amazonaws.com'
|
145
|
+
# :us_west_1 => 'us-west-1.queue.amazonaws.com'
|
146
|
+
# :eu_west_1 => 'eu-west-1.queue.amazonaws.com'
|
147
|
+
SimpleQS.host # => 'queue.amazonaws.com'
|
148
|
+
|
149
|
+
SimpleQS.host = :eu_west_1
|
150
|
+
SimpleQS.host # => 'eu-west-1.queue.amazonaws.com'
|
151
|
+
|
152
|
+
### Черги ###
|
153
|
+
|
154
|
+
Створити чергу:
|
155
|
+
|
156
|
+
queue = SimpleQS::Queue.create('queue_name')
|
157
|
+
|
158
|
+
Або:
|
159
|
+
|
160
|
+
SimpleQS::Queue.create('queue_name') do |queue|
|
161
|
+
# ...
|
162
|
+
end
|
163
|
+
|
164
|
+
Створити чергу із специфічним обмеженням часу невидимості для отриманих повідомлень:
|
165
|
+
|
166
|
+
SimpleQS::Queue.create('queue_name', 60) # Час невидимості встановленно на 60 секунд, натомість типових 30-ти
|
167
|
+
|
168
|
+
Перелічити всі черги даного рахунку:
|
169
|
+
|
170
|
+
SimpleQS::Queue.list # returns list of SimpleQS::Queue objects
|
171
|
+
|
172
|
+
Перелічити всі черги даного рахунку, що починаються на 'test':
|
173
|
+
|
174
|
+
SimpleQS::Queue.list('test')
|
175
|
+
|
176
|
+
Змінити типовий час невидимості для даної черги:
|
177
|
+
|
178
|
+
SimpleQS::Queue.new('queue_name').set_visibility_timeout(120)
|
179
|
+
|
180
|
+
Встановити атрибути черги:
|
181
|
+
|
182
|
+
SimpleQS::Queue.new('queue_name').set_attributes({:VisibilityTimeout => 120, :Policy => "policy JSON here"})
|
183
|
+
|
184
|
+
Отримати поточні атрибути черги:
|
185
|
+
|
186
|
+
SimpleQS::Queue.new('queue_name').get_attributes(:All)
|
187
|
+
|
188
|
+
Надати дозволи:
|
189
|
+
|
190
|
+
SimpleQS::Queue.new('queue_name').add_permissions('testPerms', [
|
191
|
+
{:account_id => '098166147350', :action => 'SendMessage'},
|
192
|
+
{:account_id => '098166147350', :action => 'ReceiveMessage'}
|
193
|
+
])
|
194
|
+
|
195
|
+
Скасувати дозволи:
|
196
|
+
|
197
|
+
SimpleQS::Queue.new('queue_name').remove_permissions('testPerms')
|
198
|
+
|
199
|
+
Видалити чергу:
|
200
|
+
|
201
|
+
SimpleQS::Queue.new('queue_name').delete
|
202
|
+
|
203
|
+
### Повідомлення ###
|
204
|
+
|
205
|
+
Відправити повідомлення:
|
206
|
+
|
207
|
+
queue = SimpleQS::Queue.new('queue_name')
|
208
|
+
queue.send_message('message 1')
|
209
|
+
queue.send_message('message 2')
|
210
|
+
|
211
|
+
Або:
|
212
|
+
|
213
|
+
SimpleQS::Message.send(queue, 'message 1')
|
214
|
+
SimpleQS::Message.send(queue, 'message 2')
|
215
|
+
|
216
|
+
Отримати повідомлення:
|
217
|
+
|
218
|
+
queue.receive_messages # завжди повертає масив
|
219
|
+
queue.receive_messages(:All, 10, 70) # отримати максимум 10 повідомлень зі всіма атрибутами (:All)
|
220
|
+
# та встановленим часом невидимості у 70 секунд
|
221
|
+
|
222
|
+
Або:
|
223
|
+
|
224
|
+
SimpleQS::Message.receive(queue)
|
225
|
+
SimpleQS::Message.receive(queue, :All, 10, 70)
|
226
|
+
|
227
|
+
Надіслати отримані повідомлення ще раз:
|
228
|
+
|
229
|
+
messages = queue.receive_messages
|
230
|
+
messages.first.resend
|
231
|
+
|
232
|
+
Змінити час невидимості для даного повідомлення:
|
233
|
+
|
234
|
+
messages.first.change_visibility(120) # Встановити у 120 секунд
|
235
|
+
|
236
|
+
Видалити отримане повідомлення з черги:
|
237
|
+
|
238
|
+
messages.first.delete
|
239
|
+
|
240
|
+
## Що потрібно зробити ##
|
241
|
+
|
242
|
+
Дивіться [TODO](http://github.com/krekoten/SimpleQS/blob/master/TODO.md)
|
243
|
+
|
244
|
+
## Помилки, побажання і т.і. ##
|
245
|
+
|
246
|
+
[ISSUES](http://github.com/krekoten/SimpleQS/issues)
|
247
|
+
|
248
|
+
## Автори ##
|
249
|
+
|
250
|
+
Мар'ян Крекотень (krekoten@gmail.com)
|
data/TODO.md
ADDED
data/lib/simple_qs.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
$: << File.join(File.dirname(__FILE__))
|
4
|
+
|
5
|
+
module SimpleQS
|
6
|
+
|
7
|
+
API_VERSION = '2009-02-01'
|
8
|
+
|
9
|
+
SQS_HOSTS = {
|
10
|
+
:default => 'queue.amazonaws.com',
|
11
|
+
:us_west_1 => 'us-west-1.queue.amazonaws.com',
|
12
|
+
:eu_west_1 => 'eu-west-1.queue.amazonaws.com'
|
13
|
+
}
|
14
|
+
|
15
|
+
autoload :Message, 'simple_qs/message'
|
16
|
+
autoload :Queue, 'simple_qs/queue'
|
17
|
+
autoload :Request, 'simple_qs/request'
|
18
|
+
autoload :Responce, 'simple_qs/responce'
|
19
|
+
|
20
|
+
class << self
|
21
|
+
attr_accessor :access_key_id, :secret_access_key
|
22
|
+
|
23
|
+
def account_id=(value)
|
24
|
+
@account_id = value.gsub(/[^0-9]/, '')
|
25
|
+
end
|
26
|
+
attr_reader :account_id
|
27
|
+
|
28
|
+
def host=(value)
|
29
|
+
raise ArgumentError, 'Expected value to be one of: :default, :us_west_1, :eu_west_1' unless SQS_HOSTS.key?(value)
|
30
|
+
@host = value
|
31
|
+
end
|
32
|
+
|
33
|
+
def host
|
34
|
+
@host ||= :default
|
35
|
+
SQS_HOSTS[@host]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
require 'version'
|
@@ -0,0 +1,135 @@
|
|
1
|
+
module SimpleQS
|
2
|
+
class Message
|
3
|
+
|
4
|
+
class DoubleSendError < StandardError; end
|
5
|
+
class NotReceivedError < StandardError; end
|
6
|
+
|
7
|
+
attr_accessor :queue
|
8
|
+
attr_reader :message_id, :receipt_handle, :body, :md5_of_body
|
9
|
+
attr_reader :sender_id, :sent_timestamp, :approximate_receive_count, :approximate_first_receive_timestamp
|
10
|
+
|
11
|
+
def initialize(queue, params = nil)
|
12
|
+
@queue = queue
|
13
|
+
|
14
|
+
@body = params if params.class == String
|
15
|
+
_from_responce(params) if params.class == SimpleQS::Responce
|
16
|
+
_from_hash(params) if params.class == Hash
|
17
|
+
end
|
18
|
+
|
19
|
+
def send
|
20
|
+
raise DoubleSendError, "Cann't send already sent message. Use resend() method." if message_id
|
21
|
+
raise DoubleSendError, "Cann't send received message. Use resend() method." if receipt_handle
|
22
|
+
|
23
|
+
params = {
|
24
|
+
'Action' => 'SendMessage',
|
25
|
+
'MessageBody' => body
|
26
|
+
}
|
27
|
+
request = queue.build_request(:post, params)
|
28
|
+
responce = request.perform
|
29
|
+
raise responce.to_error unless responce.successful?
|
30
|
+
_from_responce(responce)
|
31
|
+
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def resend
|
36
|
+
dup.send
|
37
|
+
end
|
38
|
+
|
39
|
+
def delete
|
40
|
+
raise NotReceivedError, "Cann't delete message that was not received" unless receipt_handle
|
41
|
+
params = {
|
42
|
+
'Action' => 'DeleteMessage',
|
43
|
+
'ReceiptHandle' => receipt_handle
|
44
|
+
}
|
45
|
+
request = queue.build_request(:get, params)
|
46
|
+
responce = request.perform
|
47
|
+
raise responce.to_error unless responce.successful?
|
48
|
+
end
|
49
|
+
alias_method :destroy, :delete
|
50
|
+
|
51
|
+
def change_visibility(visibility_timeout)
|
52
|
+
SimpleQS::Queue.check_visibility_timeout(visibility_timeout)
|
53
|
+
raise NotReceivedError, "Cann't change visibility timeout for message that was not received" unless receipt_handle
|
54
|
+
|
55
|
+
params = {
|
56
|
+
'Action' => 'ChangeMessageVisibility',
|
57
|
+
'ReceiptHandle' => receipt_handle,
|
58
|
+
'VisibilityTimeout' => visibility_timeout
|
59
|
+
}
|
60
|
+
|
61
|
+
request = queue.build_request(:get, params)
|
62
|
+
responce = request.perform
|
63
|
+
|
64
|
+
raise responce.to_error unless responce.successful?
|
65
|
+
end
|
66
|
+
|
67
|
+
def dup
|
68
|
+
self.class.new(queue, body)
|
69
|
+
end
|
70
|
+
|
71
|
+
def ==(other)
|
72
|
+
message_id == other.message_id && receipt_handle == other.receipt_handle
|
73
|
+
end
|
74
|
+
|
75
|
+
class << self
|
76
|
+
def send(queue, message_body)
|
77
|
+
new(queue, message_body).send
|
78
|
+
end
|
79
|
+
|
80
|
+
def receive(queue, attributes = nil, max_number_of_messages = nil, visibility_timeout = nil)
|
81
|
+
|
82
|
+
SimpleQS::Queue.check_visibility_timeout(visibility_timeout) if visibility_timeout
|
83
|
+
if max_number_of_messages && !(1..10).include?(max_number_of_messages)
|
84
|
+
raise ArgumentError, "Maximum number of messages should be in 1..10 range"
|
85
|
+
end
|
86
|
+
|
87
|
+
params = {
|
88
|
+
'Action' => 'ReceiveMessage'
|
89
|
+
}
|
90
|
+
|
91
|
+
if attributes
|
92
|
+
attributes = [attributes] unless attributes.class == Array
|
93
|
+
attributes.uniq!
|
94
|
+
unless (attributes - [:All, :SenderId, :SentTimestamp, :ApproximateReceiveCount, :ApproximateFirstReceiveTimestamp]).empty?
|
95
|
+
raise ArgumentError,\
|
96
|
+
"Allowed attributes: :All, :SenderId, :SentTimestamp, :ApproximateReceiveCount, :ApproximateFirstReceiveTimestamp"
|
97
|
+
end
|
98
|
+
attributes.each_index do |i|
|
99
|
+
params["AttributeName.#{i + 1}"] = attributes[i]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
params['MaxNumberOfMessages'] = max_number_of_messages if max_number_of_messages
|
103
|
+
params['VisibilityTimeout'] = visibility_timeout if visibility_timeout
|
104
|
+
|
105
|
+
request = queue.build_request(:get, params)
|
106
|
+
responce = request.perform
|
107
|
+
if responce.respond_to?(:message)
|
108
|
+
messages = (responce.message.class == Array ? responce.message : [responce.message])
|
109
|
+
messages.map {|message| new(queue, message)}
|
110
|
+
else
|
111
|
+
[]
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
protected
|
117
|
+
|
118
|
+
def _from_responce(responce)
|
119
|
+
@message_id = responce.message_id
|
120
|
+
@md5_of_body = responce.md5_of_message_body
|
121
|
+
end
|
122
|
+
|
123
|
+
def _from_hash(message)
|
124
|
+
attributes = message.delete('Attribute')
|
125
|
+
attributes.each do |attr|
|
126
|
+
message[attr['Name']] = attr['Value']
|
127
|
+
end if attributes
|
128
|
+
message.each do |key, value|
|
129
|
+
key = key.gsub(/([A-Z]+)/, '_\1').downcase.gsub(/^_/, '')
|
130
|
+
instance_variable_set("@#{key}".to_sym, value) if respond_to?(key.to_sym)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,273 @@
|
|
1
|
+
module SimpleQS
|
2
|
+
class Queue
|
3
|
+
|
4
|
+
class MaxVisibilityError < StandardError; end
|
5
|
+
|
6
|
+
MAX_VISIBILITY_TIMEOUT = 43200
|
7
|
+
|
8
|
+
attr_accessor :queue_url
|
9
|
+
|
10
|
+
# Initializes new SimpleQS::Queue object
|
11
|
+
# Parameters:
|
12
|
+
# queue_url_or_name - String, either queue url or queue name
|
13
|
+
def initialize(queue_url_or_name)
|
14
|
+
begin
|
15
|
+
self.class.check_queue_name(queue_url_or_name)
|
16
|
+
@name = queue_url_or_name
|
17
|
+
@queue_url = "http://#{SimpleQS.host}/#{SimpleQS.account_id}/#{@name}"
|
18
|
+
rescue ArgumentError
|
19
|
+
@queue_url = queue_url_or_name
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Get queue name
|
24
|
+
# Returns:
|
25
|
+
# String - queue name
|
26
|
+
def name
|
27
|
+
@name ||= @queue_url.split(/\//).last
|
28
|
+
end
|
29
|
+
|
30
|
+
# Is this queue deleted?
|
31
|
+
# Returns:
|
32
|
+
# Bool - true if queue is deleted
|
33
|
+
def deleted
|
34
|
+
@deleted ||= false
|
35
|
+
end
|
36
|
+
alias_method :deleted?, :deleted
|
37
|
+
|
38
|
+
|
39
|
+
# Deletes this queue
|
40
|
+
# Returns:
|
41
|
+
# Bool - true, if successful
|
42
|
+
# Raises:
|
43
|
+
# SimpleQS::Responce::Error
|
44
|
+
def delete
|
45
|
+
params = {
|
46
|
+
'Action' => 'DeleteQueue'
|
47
|
+
}
|
48
|
+
|
49
|
+
request = build_request(:get, params)
|
50
|
+
responce = request.perform
|
51
|
+
|
52
|
+
raise responce.to_error unless responce.successful?
|
53
|
+
|
54
|
+
@deleted = true
|
55
|
+
end
|
56
|
+
alias_method :destroy, :delete
|
57
|
+
|
58
|
+
def send_message(body)
|
59
|
+
SimpleQS::Message.send(self, body)
|
60
|
+
end
|
61
|
+
|
62
|
+
def receive_messages(attributes = nil, max_number_of_messages = nil, visibility_timeout = nil)
|
63
|
+
SimpleQS::Message.receive(self, attributes, max_number_of_messages, visibility_timeout)
|
64
|
+
end
|
65
|
+
|
66
|
+
def get_attributes(attribute_or_array = nil)
|
67
|
+
params = {
|
68
|
+
'Action' => 'GetQueueAttributes'
|
69
|
+
}
|
70
|
+
|
71
|
+
if attribute_or_array
|
72
|
+
attribute_or_array = [attribute_or_array] unless attribute_or_array.class == Array
|
73
|
+
attribute_or_array.uniq!
|
74
|
+
unless (attribute_or_array - [:All, :ApproximateNumberOfMessages, :ApproximateNumberOfMessagesNotVisible,
|
75
|
+
:VisibilityTimeout, :CreatedTimestamp, :LastModifiedTimestamp, :Policy]).empty?
|
76
|
+
raise ArgumentError,\
|
77
|
+
"Allowed attributes: :All, :ApproximateNumberOfMessages, :ApproximateNumberOfMessagesNotVisible, " <<
|
78
|
+
":VisibilityTimeout, :CreatedTimestamp, :LastModifiedTimestamp, :Policy"
|
79
|
+
end
|
80
|
+
attribute_or_array.each_index do |i|
|
81
|
+
params["AttributeName.#{i + 1}"] = attribute_or_array[i]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
request = build_request(:get, params)
|
86
|
+
responce = request.perform
|
87
|
+
raise responce.to_error unless responce.successful?
|
88
|
+
|
89
|
+
if responce.respond_to?(:attribute)
|
90
|
+
responce.attribute.inject({}) do |result, key_value|
|
91
|
+
result[key_value['Name']] = key_value['Value']
|
92
|
+
result
|
93
|
+
end
|
94
|
+
else
|
95
|
+
{}
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def set_visibility_timeout(visibility_timeout)
|
100
|
+
set_attributes({:VisibilityTimeout => visibility_timeout})
|
101
|
+
end
|
102
|
+
|
103
|
+
def set_policy(policy)
|
104
|
+
set_attributes({:Policy => policy})
|
105
|
+
end
|
106
|
+
|
107
|
+
def set_attributes(attributes)
|
108
|
+
raise ArgumentError, "Allowed attributes: :VisibilityTimeout, :Policy" unless (attributes.keys - [:VisibilityTimeout, :Policy]).empty?
|
109
|
+
self.class.check_visibility_timeout(attributes[:VisibilityTimeout]) if attributes[:VisibilityTimeout]
|
110
|
+
|
111
|
+
params = {
|
112
|
+
'Action' => 'SetQueueAttributes'
|
113
|
+
}
|
114
|
+
|
115
|
+
i = 1
|
116
|
+
attributes.each do |key, value|
|
117
|
+
params["Attribute.#{i}.Name"] = key.to_s
|
118
|
+
params["Attribute.#{i}.Value"] = value
|
119
|
+
i += 1
|
120
|
+
end
|
121
|
+
|
122
|
+
request = build_request(:get, params)
|
123
|
+
responce = request.perform
|
124
|
+
|
125
|
+
raise responce.to_error unless responce.successful?
|
126
|
+
end
|
127
|
+
|
128
|
+
# label - String, identifier for permissions
|
129
|
+
# permissions - Array of Hashes, [{:account_id => '125074342641', :action => 'SendMessage'}, ...]
|
130
|
+
def add_permissions(label, permissions)
|
131
|
+
self.class.check_queue_name(label)
|
132
|
+
|
133
|
+
params = {
|
134
|
+
'Action' => 'AddPermission',
|
135
|
+
'Label' => label
|
136
|
+
}
|
137
|
+
|
138
|
+
i = 1
|
139
|
+
permissions.each do |permission_hash|
|
140
|
+
raise ArgumentError, "invalid action: #{permission_hash[:action]}"\
|
141
|
+
unless ['*', 'SendMessage', 'ReceiveMessage', 'DeleteMessage',
|
142
|
+
'ChangeMessageVisibility', 'GetQueueAttributes'].include?(permission_hash[:action])
|
143
|
+
|
144
|
+
params["AWSAccountId.#{i}"] = permission_hash[:account_id]
|
145
|
+
params["ActionName.#{i}"] = permission_hash[:action]
|
146
|
+
i += 1
|
147
|
+
end
|
148
|
+
|
149
|
+
request = build_request(:post, params)
|
150
|
+
responce = request.perform
|
151
|
+
|
152
|
+
raise responce.to_error unless responce.successful?
|
153
|
+
end
|
154
|
+
|
155
|
+
def remove_permissions(label)
|
156
|
+
params = {
|
157
|
+
'Action' => 'RemovePermission',
|
158
|
+
'Label' => label
|
159
|
+
}
|
160
|
+
|
161
|
+
request = build_request(:get, params)
|
162
|
+
responce = request.perform
|
163
|
+
|
164
|
+
raise responce.to_error unless responce.successful?
|
165
|
+
end
|
166
|
+
|
167
|
+
def build_request(method, params)
|
168
|
+
request = SimpleQS::Request.build(method, params)
|
169
|
+
request.query_string = [SimpleQS.account_id, name]
|
170
|
+
request
|
171
|
+
end
|
172
|
+
|
173
|
+
class << self
|
174
|
+
# Create new queue
|
175
|
+
# Parameters:
|
176
|
+
# name - String, name of queue. Should be not longer than 80 chars and contain only a-zA-Z0-9\-\_
|
177
|
+
# default_visibility_timeout - Fixnum, visibility timeout [Optional]. In range 0 to MAX_VISIBILITY_TIMEOUT.
|
178
|
+
# If nil, then default is used (30 seconds).
|
179
|
+
# &block - if given, then newly created queue object passed to it
|
180
|
+
# Returns:
|
181
|
+
# SimpleQS::Queue
|
182
|
+
# Raises:
|
183
|
+
# ArgumentError
|
184
|
+
# SimpleQS::Queue::MaxVisibilityError
|
185
|
+
# SimpleQS::Responce::Error
|
186
|
+
def create(name, default_visibility_timeout = nil, &block)
|
187
|
+
|
188
|
+
check_queue_name(name)
|
189
|
+
check_visibility_timeout(default_visibility_timeout)
|
190
|
+
|
191
|
+
params = {
|
192
|
+
'Action' => 'CreateQueue',
|
193
|
+
'QueueName' => name
|
194
|
+
}
|
195
|
+
params['DefaultVisibilityTimeout'] = default_visibility_timeout if default_visibility_timeout
|
196
|
+
|
197
|
+
request = SimpleQS::Request.build(:get, params)
|
198
|
+
responce = request.perform
|
199
|
+
if responce.successful?
|
200
|
+
queue = new(responce.queue_url)
|
201
|
+
yield queue if block_given?
|
202
|
+
queue
|
203
|
+
else
|
204
|
+
raise responce.to_error
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
# List queues
|
209
|
+
# Parameters:
|
210
|
+
# pattern - String, first letterns of queues names [Optional].
|
211
|
+
# Returns:
|
212
|
+
# Array - array of SimpleQS::Queue or empty if nothing found
|
213
|
+
# Rises:
|
214
|
+
# ArgumentError
|
215
|
+
# SimpleQS::Responce::Error
|
216
|
+
def list(pattern = nil)
|
217
|
+
params = {
|
218
|
+
'Action' => 'ListQueues'
|
219
|
+
}
|
220
|
+
|
221
|
+
check_queue_name(pattern) if pattern
|
222
|
+
params['QueueNamePrefix'] = pattern if pattern
|
223
|
+
|
224
|
+
request = SimpleQS::Request.build(:get, params)
|
225
|
+
responce = request.perform
|
226
|
+
raise responce.to_error unless responce.successful?
|
227
|
+
|
228
|
+
begin
|
229
|
+
queues = responce.queue_url.class == Array ? responce.queue_url : [responce.queue_url]
|
230
|
+
queues.map {|queue_url| new(queue_url)}
|
231
|
+
rescue NoMethodError
|
232
|
+
[]
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def exists?(queue_name)
|
237
|
+
list(queue_name).any? {|queue| queue.name == queue_name}
|
238
|
+
end
|
239
|
+
|
240
|
+
def delete(queue_name)
|
241
|
+
new(queue_name).delete
|
242
|
+
end
|
243
|
+
alias_method :destroy, :delete
|
244
|
+
|
245
|
+
# Performs checks on queue name. Raises ArgumentError with message in case of
|
246
|
+
# constraint violation
|
247
|
+
def check_queue_name(name)
|
248
|
+
raise ArgumentError, "expected to be String, but got #{name.class.to_s}" unless name.class == String
|
249
|
+
raise(
|
250
|
+
ArgumentError,
|
251
|
+
"should be maximum 80 characters long"
|
252
|
+
) if name.length > 80
|
253
|
+
raise(
|
254
|
+
ArgumentError,
|
255
|
+
"should contain only alphanumeric characters, hyphens (-), and underscores (_)"
|
256
|
+
) if name =~ /[^a-zA-Z0-9\_\-]/
|
257
|
+
end
|
258
|
+
|
259
|
+
# Performs checks on visibility timeout. Raises ArgumentError or SimpleQS::Queue::MaxVisibilityError
|
260
|
+
# with message in case of constraint violation
|
261
|
+
def check_visibility_timeout(default_visibility_timeout)
|
262
|
+
raise(
|
263
|
+
ArgumentError,
|
264
|
+
"expected visibility timeout to respon to to_i, but #{default_visibility_timeout.class.to_s} doesn't"
|
265
|
+
) if default_visibility_timeout && default_visibility_timeout.class != Fixnum
|
266
|
+
raise(
|
267
|
+
MaxVisibilityError,
|
268
|
+
"expected visibility timeout to be in 0...#{MAX_VISIBILITY_TIMEOUT} range, got #{default_visibility_timeout}"
|
269
|
+
) if default_visibility_timeout && !(0...MAX_VISIBILITY_TIMEOUT).include?(default_visibility_timeout)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
@@ -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,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,38 @@
|
|
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 InvalidHttpRequestError < Error; end
|
19
|
+
class InvalidParameterCombinationError < Error; end
|
20
|
+
class InvalidParameterValueError < Error; end
|
21
|
+
class InvalidQueryParameterError < Error; end
|
22
|
+
class InvalidRequestError < Error; end
|
23
|
+
class InvalidSecurityError < Error; end
|
24
|
+
class InvalidSecurityTokenError < Error; end
|
25
|
+
class MalformedVersionError < Error; end
|
26
|
+
class MissingClientTokenIdError < Error; end
|
27
|
+
class MissingCredentialsError < Error; end
|
28
|
+
class MissingParameterError < Error; end
|
29
|
+
class NoSuchVersionError < Error; end
|
30
|
+
class NotAuthorizedToUseVersionError < Error; end
|
31
|
+
class OptInRequiredError < Error; end
|
32
|
+
class RequestExpiredError < Error; end
|
33
|
+
class RequestThrottledError < Error; end
|
34
|
+
class ServiceUnavailableError < Error; end
|
35
|
+
class SignatureDoesNotMatchError < Error; end
|
36
|
+
class X509ParseError < Error; end
|
37
|
+
end
|
38
|
+
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
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
8
|
+
- 2
|
9
|
+
version: 0.1.2
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- !binary |
|
@@ -17,7 +17,7 @@ autorequire:
|
|
17
17
|
bindir: bin
|
18
18
|
cert_chain: []
|
19
19
|
|
20
|
-
date: 2010-08-
|
20
|
+
date: 2010-08-17 00:00:00 +03:00
|
21
21
|
default_executable:
|
22
22
|
dependencies:
|
23
23
|
- !ruby/object:Gem::Dependency
|
@@ -48,6 +48,18 @@ dependencies:
|
|
48
48
|
version: 0.3.2
|
49
49
|
type: :runtime
|
50
50
|
version_requirements: *id002
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
name: rspec
|
53
|
+
prerelease: false
|
54
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :development
|
62
|
+
version_requirements: *id003
|
51
63
|
description: SimpleQS library fully wraps Amazon SQS REST API. It allows you perform all kind of calls on queues and messages.
|
52
64
|
email: krekoten@gmail.com
|
53
65
|
executables: []
|
@@ -56,8 +68,23 @@ extensions: []
|
|
56
68
|
|
57
69
|
extra_rdoc_files: []
|
58
70
|
|
59
|
-
files:
|
60
|
-
|
71
|
+
files:
|
72
|
+
- lib/simple_qs/message.rb
|
73
|
+
- lib/simple_qs/queue.rb
|
74
|
+
- lib/simple_qs/request/base.rb
|
75
|
+
- lib/simple_qs/request/get.rb
|
76
|
+
- lib/simple_qs/request/post.rb
|
77
|
+
- lib/simple_qs/request.rb
|
78
|
+
- lib/simple_qs/responce/exceptions.rb
|
79
|
+
- lib/simple_qs/responce/failure_builder.rb
|
80
|
+
- lib/simple_qs/responce/successful_builder.rb
|
81
|
+
- lib/simple_qs/responce.rb
|
82
|
+
- lib/simple_qs.rb
|
83
|
+
- lib/version.rb
|
84
|
+
- LICENSE
|
85
|
+
- README.md
|
86
|
+
- CHANGELOG.md
|
87
|
+
- TODO.md
|
61
88
|
has_rdoc: true
|
62
89
|
homepage: http://github.com/krekoten/SimpleQS
|
63
90
|
licenses: []
|