simple_qs 0.1.1 → 0.1.2

This diff has not been reviewed by any users.
Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ ## 0.1.2
2
+
3
+ Features:
4
+ - Add exists? class method to SimpleQS::Queue
5
+ - Add delete class method to SimpleQS::Queue
6
+
7
+ Bugfixes:
8
+ - Fix gemspec, gem now works as it should :)
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.
@@ -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
@@ -0,0 +1,3 @@
1
+ * Add interface for policy
2
+ * RDoc
3
+ * Handle big messages ( > 8 Kb) via S3 (send special message marker with bucket and object name where real message stored)
@@ -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,12 @@
1
+ module SimpleQS
2
+ module Request
3
+ class Get < Base
4
+ http_method :get
5
+
6
+ def perform
7
+ sign!
8
+ SimpleQS::Responce.new Net::HTTP.get(URI.parse(uri(true)))
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module SimpleQS
2
+ module Request
3
+ class Post < Base
4
+ http_method :post
5
+
6
+ def perform
7
+ sign!
8
+ SimpleQS::Responce.new Net::HTTP.post_form(URI.parse(uri), query_params).body
9
+ end
10
+ end
11
+ end
12
+ 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
@@ -0,0 +1,3 @@
1
+ module SimpleQS
2
+ VERSION = '0.1.2'
3
+ end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 1
8
- - 1
9
- version: 0.1.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-16 00:00:00 +03:00
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: []