simple_qs 0.1.1 → 0.1.2

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,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: []