aws-sdb 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2007, 2008 Tim Dysinger
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README ADDED
@@ -0,0 +1 @@
1
+ Amazon SDB API
data/Rakefile ADDED
@@ -0,0 +1,30 @@
1
+ require 'rubygems'
2
+ require 'spec/rake/spectask'
3
+ require 'rake/gempackagetask'
4
+
5
+ Spec::Rake::SpecTask.new
6
+
7
+ gem_spec = Gem::Specification.new do |s|
8
+ s.name = "aws-sdb"
9
+ s.version = "0.1.0"
10
+ s.platform = Gem::Platform::RUBY
11
+ s.has_rdoc = true
12
+ s.extra_rdoc_files = ["README", "LICENSE"]
13
+ s.summary = "Amazon SDB API"
14
+ s.description = s.summary
15
+ s.author = "Tim Dysinger"
16
+ s.email = "tim@dysinger.net"
17
+ s.homepage = "http://dysinger.net"
18
+ s.add_dependency "needle"
19
+ s.require_path = 'lib'
20
+ s.autorequire = s.name.gsub('-','/')
21
+ s.files = %w(LICENSE README Rakefile) + Dir.glob("{lib,spec}/**/*")
22
+ end
23
+
24
+ Rake::GemPackageTask.new(gem_spec) do |pkg|
25
+ pkg.gem_spec = gem_spec
26
+ end
27
+
28
+ task :install => [:package] do
29
+ sh %{sudo gem install pkg/#{gem_spec.name}-#{gem_spec.version}}
30
+ end
data/lib/aws_sdb.rb ADDED
@@ -0,0 +1,23 @@
1
+ # require needle
2
+ require 'rubygems'
3
+ require 'needle'
4
+
5
+ # require modules
6
+ require 'aws_sdb/error'
7
+ require 'aws_sdb/service'
8
+
9
+ module AwsSdb
10
+
11
+ def self.container
12
+ @@container ||= Needle::Registry.new(
13
+ :logs => { :filename => "aws_sdb.log" }
14
+ ).namespace!(:aws_sdb) do
15
+
16
+ service(:model => :multiton) do | c, p, access, secret |
17
+ Service.new(c.log_for(p), access, secret)
18
+ end
19
+
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,40 @@
1
+ module AwsSdb
2
+
3
+ class Error < RuntimeError ; end
4
+
5
+ class RequestError < Error
6
+ attr_reader :request_id
7
+
8
+ def initialize(message, request_id=nil)
9
+ super(message)
10
+ @request_id = request_id
11
+ end
12
+ end
13
+
14
+ class InvalidDomainNameError < RequestError ; end
15
+ class InvalidParameterValueError < RequestError ; end
16
+ class InvalidNextTokenError < RequestError ; end
17
+ class InvalidNumberPredicatesError < RequestError ; end
18
+ class InvalidNumberValueTestsError < RequestError ; end
19
+ class InvalidQueryExpressionError < RequestError ; end
20
+ class MissingParameterError < RequestError ; end
21
+ class NoSuchDomainError < RequestError ; end
22
+ class NumberDomainsExceededError < RequestError ; end
23
+ class NumberDomainAttributesExceededError < RequestError ; end
24
+ class NumberDomainBytesExceededError < RequestErrror ; end
25
+ class NumberItemAttributesExceededError < RequestError ; end
26
+ class RequestTimeoutError < RequestError ; end
27
+
28
+ class ConnectionError < Error
29
+ attr_reader :response
30
+
31
+ def initialize(response)
32
+ super(
33
+ "#{response.code} \
34
+ #{response.message if response.respond_to?(:message)}"
35
+ )
36
+ @response = response
37
+ end
38
+ end
39
+
40
+ end
@@ -0,0 +1,160 @@
1
+ require 'time'
2
+ require 'cgi'
3
+ require 'uri'
4
+ require 'net/http'
5
+
6
+ require 'base64'
7
+ require 'openssl'
8
+
9
+ require 'rexml/document'
10
+ require 'rexml/xpath'
11
+
12
+ module AwsSdb
13
+
14
+ class Service
15
+ def initialize(logger, access_key_id, secret_access_key)
16
+ @logger = logger
17
+ @access_key_id = access_key_id
18
+ @secret_access_key = secret_access_key
19
+ end
20
+
21
+ # TODO Make this options hash not a param list
22
+ def list_domains(max = nil, token = nil)
23
+ params = { 'Action' => 'ListDomains' }
24
+ params['MoreToken'] =
25
+ token unless token.nil? || token.empty?
26
+ params['MaxResults'] =
27
+ max.to_s unless max.nil? || max.to_i == 0
28
+ doc = call(:get, params)
29
+ results = []
30
+ REXML::XPath.each(doc, '//DomainName/text()') do |domain|
31
+ results << domain.to_s
32
+ end
33
+ return results, REXML::XPath.each(doc, '/MoreToken/text()').to_s
34
+ end
35
+
36
+ def create_domain(domain)
37
+ call(:post, { 'Action' => 'CreateDomain', 'DomainName'=> domain.to_s })
38
+ nil
39
+ end
40
+
41
+ def delete_domain(domain)
42
+ call(
43
+ :delete,
44
+ { 'Action' => 'DeleteDomain', 'DomainName' => domain.to_s }
45
+ )
46
+ nil
47
+ end
48
+
49
+ def query(domain, query, sort = nil, max = nil, token = nil)
50
+ params = {
51
+ 'Action' => 'Query',
52
+ 'QueryExpression' => query,
53
+ 'DomainName' => domain.to_s
54
+ }
55
+ params['Sort'] = sort unless sort.nil? || sort.empty?
56
+ params['MoreToken'] =
57
+ token unless token.nil? || token.empty?
58
+ params['MaxResults'] =
59
+ max.to_s unless max.nil? || max.to_i != 0
60
+ doc = call(:get, params)
61
+ results = []
62
+ REXML::XPath.each(doc, '//ItemName/text()') do |item|
63
+ results << item.to_s
64
+ end
65
+ return results, REXML::XPath.each(doc, '/MoreToken/text()').to_s
66
+ end
67
+
68
+ def put_attributes(domain, item, attributes, replace = true)
69
+ params = {
70
+ 'Action' => 'PutAttributes',
71
+ 'DomainName' => domain.to_s,
72
+ 'ItemName' => item.to_s,
73
+ 'Replace' => replace
74
+ }
75
+ count = 0
76
+ attributes.each do | key, values |
77
+ ([]<<values).flatten.each do |value|
78
+ params["Attribute.#{count}.Name"] = key.to_s
79
+ params["Attribute.#{count}.Value"] = value.to_s
80
+ count += 1
81
+ end
82
+ end
83
+ call(:put, params)
84
+ nil
85
+ end
86
+
87
+ def get_attributes(domain, item)
88
+ doc = call(
89
+ :get,
90
+ {
91
+ 'Action' => 'GetAttributes',
92
+ 'DomainName' => domain.to_s,
93
+ 'ItemName' => item.to_s
94
+ }
95
+ )
96
+ attributes = {}
97
+ REXML::XPath.each(doc, "//Attribute") do |attr|
98
+ key = REXML::XPath.first(attr, './Name/text()').to_s
99
+ value = REXML::XPath.first(attr, './Value/text()').to_s
100
+ ( attributes[key] ||= [] ) << value
101
+ end
102
+ attributes
103
+ end
104
+
105
+ def delete_attributes(domain, item)
106
+ call(
107
+ :delete,
108
+ {
109
+ 'Action' => 'DeleteAttributes',
110
+ 'DomainName' => domain.to_s,
111
+ 'ItemName' => item.to_s
112
+ }
113
+ )
114
+ nil
115
+ end
116
+
117
+ protected
118
+
119
+ def call(method, params)
120
+ params.merge!( {
121
+ 'Version' => '2007-02-09',
122
+ 'SignatureVersion' => '1',
123
+ 'AWSAccessKeyId' => @access_key_id,
124
+ 'Timestamp' => Time.now.gmtime.iso8601
125
+ }
126
+ )
127
+ data = ''
128
+ query = []
129
+ params.keys.sort_by { |k| k.upcase }.each do |key|
130
+ data << "#{key}#{params[key].to_s}"
131
+ query << "#{key}=#{CGI::escape(params[key].to_s)}"
132
+ end
133
+ digest = OpenSSL::Digest::Digest.new('sha1')
134
+ hmac = OpenSSL::HMAC.digest(digest, @secret_access_key, data)
135
+ signature = Base64.encode64(hmac).strip
136
+ query << "Signature=#{CGI::escape(signature)}"
137
+ query = query.join('&')
138
+ url = "http://sds.amazonaws.com?#{query}"
139
+ uri = URI.parse(url)
140
+ response =
141
+ Net::HTTP.new(uri.host, uri.port).send_request(method, url)
142
+ @logger.debug("#{url} #{response.code} #{response.body}")
143
+ raise(ConnectionError.new(response)) unless (200..400).include?(
144
+ response.code.to_i
145
+ )
146
+ doc = REXML::Document.new(response.body)
147
+ error = doc.get_elements('*/Errors/Error')[0]
148
+ raise(
149
+ Module.class_eval(
150
+ "AwsSdb::#{error.get_elements('Code')[0].text}Error"
151
+ ).new(
152
+ error.get_elements('Message')[0].text,
153
+ doc.get_elements('*/RequestID')[0].text
154
+ )
155
+ ) unless error.nil?
156
+ doc
157
+ end
158
+ end
159
+
160
+ end
@@ -0,0 +1,399 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ require 'digest/sha1'
4
+ require 'net/http'
5
+ require 'rexml/document'
6
+
7
+ require 'rubygems'
8
+ require 'uuidtools'
9
+
10
+ include AwsSdb
11
+
12
+ describe Service, "when creating a new domain" do
13
+ before(:all) do
14
+ @logger = AwsSdb.container.logs.get(:rspec)
15
+ @service = AwsSdb.container.service(
16
+ ENV['AMAZON_ACCESS_KEY_ID'],
17
+ ENV['AMAZON_SECRET_ACCESS_KEY']
18
+ )
19
+ @domain = "test-#{UUID.random_create.to_s}"
20
+ # domains = @service.list_domains[0]
21
+ # @logger.debug("Domains #{domains.inspect}")
22
+ # domains.each do |d|
23
+ # @service.delete_domain(d)
24
+ # end
25
+ end
26
+
27
+ after(:all) do
28
+ # @service.delete_domain(@domain)
29
+ end
30
+
31
+ def stub_success
32
+ resp = mock(Net::HTTPResponse)
33
+ resp.stub!(:code).and_return("200")
34
+ resp.stub!(:body).and_return(
35
+ """
36
+ <CreateDomainResponse>
37
+ <ResponseStatus>
38
+ <StatusCode>Success</StatusCode>
39
+ <RequestID>#{UUID.random_create.to_s}</RequestID>
40
+ <BoxUsage>0.001</BoxUsage>
41
+ </ResponseStatus>
42
+ </CreateDomainResponse>
43
+ """
44
+ )
45
+ http = mock(Net::HTTP)
46
+ http.stub!(:send_request).and_return(resp)
47
+ Net::HTTP.stub!(:new).and_return(http)
48
+ end
49
+
50
+ def stub_error(code, type, message)
51
+ resp = mock(Net::HTTPResponse)
52
+ resp.stub!(:code).and_return(code)
53
+ resp.stub!(:body).and_return(
54
+ """
55
+ <Response>
56
+ <Errors>
57
+ <Error>
58
+ <Code>#{type}</Code>
59
+ <Message>#{message}</Message>
60
+ </Error>
61
+ </Errors>
62
+ <RequestID>#{UUID.random_create.to_s}</RequestID>
63
+ </Response>
64
+ """
65
+ )
66
+ http = mock(Net::HTTP)
67
+ http.stub!(:send_request).and_return(resp)
68
+ Net::HTTP.stub!(:new).and_return(http)
69
+ end
70
+
71
+ it "should not raise an error if a valid new domain name is given" do
72
+ stub_success
73
+ lambda {
74
+ @service.create_domain("test-#{UUID.random_create.to_s}")
75
+ }.should_not raise_error
76
+ end
77
+
78
+ it "should not raise an error if the domain name already exists" do
79
+ stub_success
80
+ domain = "test-#{UUID.random_create.to_s}"
81
+ lambda {
82
+ @service.create_domain(domain)
83
+ @service.create_domain(domain)
84
+ }.should_not raise_error
85
+ end
86
+
87
+ it "should raise an error if an a nil or '' domain name is given" do
88
+ stub_error(
89
+ 400, :InvalidDomainName, "The domain name '' is not valid."
90
+ )
91
+ lambda {
92
+ @service.create_domain('')
93
+ }.should raise_error(InvalidDomainNameError)
94
+ stub_error(
95
+ 400, :InvalidDomainName, "The domain name ' ' is not valid."
96
+ )
97
+ lambda {
98
+ @service.create_domain(' ')
99
+ }.should raise_error(InvalidDomainNameError)
100
+ stub_error(
101
+ 400, :InvalidDomainName, "The domain name '' is not valid."
102
+ )
103
+ lambda {
104
+ @service.create_domain(nil)
105
+ }.should raise_error(InvalidDomainNameError)
106
+ end
107
+
108
+ it "should raise an error if the domain name length is < 3 or > 255" do
109
+ stub_error(
110
+ 400, :InvalidDomainName, "The domain name 'xx' is not valid."
111
+ )
112
+ lambda {
113
+ @service.create_domain('xx')
114
+ }.should raise_error(InvalidDomainNameError)
115
+ stub_error(
116
+ 400,
117
+ :InvalidDomainName,
118
+ "The domain name '#{:x.to_s*256}' is not valid."
119
+ )
120
+ lambda {
121
+ @service.create_domain('x'*256)
122
+ }.should raise_error(InvalidDomainNameError)
123
+ end
124
+
125
+ it "should only accept domain names with a-z, A-Z, 0-9, '_', '-', and '.' " do
126
+ stub_error(
127
+ 400, :InvalidDomainName, "The domain name '@$^*()' is not valid."
128
+ )
129
+ lambda {
130
+ @service.create_domain('@$^&*()')
131
+ }.should raise_error(InvalidDomainNameError)
132
+ end
133
+
134
+ it "should only accept a maximum of 100 domain names" do
135
+ # TODO Implement this example
136
+ end
137
+
138
+ it "should not have to call amazon to determine domain name correctness" do
139
+ # TODO Implement this example
140
+ end
141
+ end
142
+
143
+ describe Service, "when listing domains" do
144
+ before(:all) do
145
+ @service = AwsSdb.container.service(
146
+ ENV['AMAZON_ACCESS_KEY_ID'],
147
+ ENV['AMAZON_SECRET_ACCESS_KEY']
148
+ )
149
+ @domain = "test-#{UUID.random_create.to_s}"
150
+ # @service.list_domains[0].each do |d|
151
+ # @service.delete_domain(d)
152
+ # end
153
+ # @service.create_domain(@domain)
154
+ end
155
+
156
+ after(:all) do
157
+ # @service.delete_domain(@domain)
158
+ end
159
+
160
+ it "should return a complete list" do
161
+ resp = mock(Net::HTTPResponse)
162
+ resp.stub!(:code).and_return("200")
163
+ resp.stub!(:body).and_return(
164
+ """
165
+ <ListDomainsResponse>
166
+ <ResponseStatus>
167
+ <StatusCode>Success</StatusCode>
168
+ <RequestID>#{UUID.random_create.to_s}</RequestID>
169
+ <BoxUsage/>
170
+ </ResponseStatus>
171
+ <DomainName>#{@domain}</DomainName>
172
+ </ListDomainsResponse>
173
+ """
174
+ )
175
+ http = mock(Net::HTTP)
176
+ http.stub!(:send_request).and_return(resp)
177
+ Net::HTTP.stub!(:new).and_return(http)
178
+
179
+ result = nil
180
+ lambda { result = @service.list_domains[0] }.should_not raise_error
181
+ result.should_not be_nil
182
+ result.should_not be_empty
183
+ result.size.should == 1
184
+ result.should_not be_nil
185
+ result.include?(@domain).should == true
186
+ end
187
+ end
188
+
189
+ describe Service, "when deleting domains" do
190
+ before(:all) do
191
+ @service = AwsSdb.container.service(
192
+ ENV['AMAZON_ACCESS_KEY_ID'],
193
+ ENV['AMAZON_SECRET_ACCESS_KEY']
194
+ )
195
+ @domain = "test-#{UUID.random_create.to_s}"
196
+ # @service.list_domains[0].each do |d|
197
+ # @service.delete_domain(d)
198
+ # end
199
+ # @service.create_domain(@domain)
200
+ end
201
+
202
+ after do
203
+ # @service.delete_domain(@domain)
204
+ end
205
+
206
+ def stub_success
207
+ resp = mock(Net::HTTPResponse)
208
+ resp.stub!(:code).and_return("200")
209
+ resp.stub!(:body).and_return(
210
+ """
211
+ <DeleteDomainResponse>
212
+ <ResponseStatus>
213
+ <StatusCode>Success</StatusCode>
214
+ <RequestID>#{UUID.random_create.to_s}</RequestID>
215
+ <BoxUsage/>
216
+ </ResponseStatus>
217
+ </DeleteDomainResponse>
218
+ """
219
+ )
220
+ http = mock(Net::HTTP)
221
+ http.stub!(:send_request).and_return(resp)
222
+ Net::HTTP.stub!(:new).and_return(http)
223
+ end
224
+
225
+ it "should be able to delete an existing domain" do
226
+ stub_success
227
+ lambda { @service.delete_domain(@domain) }.should_not raise_error
228
+ end
229
+
230
+ it "should not raise an error trying to delete a non-existing domain" do
231
+ stub_success
232
+ lambda {
233
+ @service.delete_domain(UUID.random_create.to_s)
234
+ }.should_not raise_error
235
+ end
236
+ end
237
+
238
+ describe Service, "when managing items" do
239
+ before(:all) do
240
+ @service = AwsSdb.container.service(
241
+ ENV['AMAZON_ACCESS_KEY_ID'],
242
+ ENV['AMAZON_SECRET_ACCESS_KEY']
243
+ )
244
+ @domain = "test-#{UUID.random_create.to_s}"
245
+ # @service.list_domains[0].each do |d|
246
+ # @service.delete_domain(d)
247
+ # end
248
+ # @service.create_domain(@domain)
249
+ @item = "test-#{UUID.random_create.to_s}"
250
+ @attributes = {
251
+ :question => 'What is the answer?',
252
+ :answer => [ true, 'testing123', 4.2, 42, 420 ]
253
+ }
254
+ end
255
+
256
+ after(:all) do
257
+ # @service.delete_domain(@domain)
258
+ end
259
+
260
+ def stub_put
261
+ resp = mock(Net::HTTPResponse)
262
+ resp.stub!(:code).and_return("200")
263
+ resp.stub!(:body).and_return(
264
+ """
265
+ <PutAttributesResponse>
266
+ <ResponseStatus><StatusCode>Success</StatusCode>
267
+ <RequestID>#{UUID.random_create.to_s}</RequestID>
268
+ <BoxUsage/>
269
+ </ResponseStatus>
270
+ </PutAttributesResponse>
271
+ """
272
+ )
273
+ http = mock(Net::HTTP)
274
+ http.stub!(:send_request).and_return(resp)
275
+ Net::HTTP.stub!(:new).and_return(http)
276
+ end
277
+
278
+ def stub_get
279
+ resp = mock(Net::HTTPResponse)
280
+ resp.stub!(:code).and_return("200")
281
+ resp.stub!(:body).and_return(
282
+ """
283
+ <GetAttributesResponse>
284
+ <ResponseStatus>
285
+ <StatusCode>Success</StatusCode>
286
+ <RequestID>#{UUID.random_create.to_s}</RequestID>
287
+ <BoxUsage/>
288
+ </ResponseStatus>
289
+ <Attribute>
290
+ <Name>question</Name>
291
+ <Value>What is the answer?</Value>
292
+ </Attribute>
293
+ <Attribute>
294
+ <Name>answer</Name>
295
+ <Value>true</Value>
296
+ </Attribute>
297
+ <Attribute>
298
+ <Name>answer</Name>
299
+ <Value>testing123</Value>
300
+ </Attribute>
301
+ <Attribute>
302
+ <Name>answer</Name>
303
+ <Value>4.2</Value>
304
+ </Attribute>
305
+ <Attribute>
306
+ <Name>answer</Name>
307
+ <Value>42</Value>
308
+ </Attribute>
309
+ <Attribute>
310
+ <Name>answer</Name>
311
+ <Value>420</Value>
312
+ </Attribute>
313
+ </GetAttributesResponse>
314
+ """
315
+ )
316
+ http = mock(Net::HTTP)
317
+ http.stub!(:send_request).and_return(resp)
318
+ Net::HTTP.stub!(:new).and_return(http)
319
+ end
320
+
321
+ def stub_query
322
+ resp = mock(Net::HTTPResponse)
323
+ resp.stub!(:code).and_return("200")
324
+ resp.stub!(:body).and_return(
325
+ """
326
+ <QueryResponse>
327
+ <ResponseStatus>
328
+ <StatusCode>Success</StatusCode>
329
+ <RequestID>#{UUID.random_create.to_s}</RequestID>
330
+ <BoxUsage/>
331
+ </ResponseStatus>
332
+ <ItemName>#{@item}</ItemName>
333
+ </QueryResponse>
334
+ """
335
+ )
336
+ http = mock(Net::HTTP)
337
+ http.stub!(:send_request).and_return(resp)
338
+ Net::HTTP.stub!(:new).and_return(http)
339
+ end
340
+
341
+ def stub_delete
342
+ resp = mock(Net::HTTPResponse)
343
+ resp.stub!(:code).and_return("200")
344
+ resp.stub!(:body).and_return(
345
+ """
346
+ <DeleteAttributesResponse>
347
+ <ResponseStatus>
348
+ <StatusCode>Success</StatusCode>
349
+ <RequestID>#{UUID.random_create.to_s}</RequestID>
350
+ <BoxUsage/>
351
+ </ResponseStatus>
352
+ </DeleteAttributesResponse>
353
+ """
354
+ )
355
+ http = mock(Net::HTTP)
356
+ http.stub!(:send_request).and_return(resp)
357
+ Net::HTTP.stub!(:new).and_return(http)
358
+ end
359
+
360
+ it "should be able to put attributes" do
361
+ stub_put
362
+ lambda {
363
+ @service.put_attributes(@domain, @item, @attributes)
364
+ }.should_not raise_error
365
+ end
366
+
367
+ it "should be able to get attributes" do
368
+ stub_get
369
+ result = nil
370
+ lambda {
371
+ result = @service.get_attributes(@domain, @item)
372
+ }.should_not raise_error
373
+ result.should_not be_nil
374
+ result.should_not be_empty
375
+ result.has_key?('answer').should == true
376
+ @attributes[:answer].each do |v|
377
+ result['answer'].include?(v.to_s).should == true
378
+ end
379
+ end
380
+
381
+ it "should be able to query" do
382
+ stub_query
383
+ result = nil
384
+ lambda {
385
+ result = @service.query(@domain, "[ 'answer' = '42' ]")[0]
386
+ }.should_not raise_error
387
+ result.should_not be_nil
388
+ result.should_not be_empty
389
+ result.should_not be_nil
390
+ result.include?(@item).should == true
391
+ end
392
+
393
+ it "should be able to delete attributes" do
394
+ stub_delete
395
+ lambda {
396
+ @service.delete_attributes(@domain, @item)
397
+ }.should_not raise_error
398
+ end
399
+ end
@@ -0,0 +1,4 @@
1
+ $TESTING=true
2
+ $:.push File.join(File.dirname(__FILE__), '..', 'lib')
3
+
4
+ require 'aws_sdb'
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: aws-sdb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Tim Dysinger
8
+ autorequire: aws/sdb
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-01-03 00:00:00 -10:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: needle
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: "0"
23
+ version:
24
+ description: Amazon SDB API
25
+ email: tim@dysinger.net
26
+ executables: []
27
+
28
+ extensions: []
29
+
30
+ extra_rdoc_files:
31
+ - README
32
+ - LICENSE
33
+ files:
34
+ - LICENSE
35
+ - README
36
+ - Rakefile
37
+ - lib/aws_sdb
38
+ - lib/aws_sdb/error.rb
39
+ - lib/aws_sdb/service.rb
40
+ - lib/aws_sdb.rb
41
+ - spec/aws_sdb
42
+ - spec/aws_sdb/service_spec.rb
43
+ - spec/spec_helper.rb
44
+ has_rdoc: true
45
+ homepage: http://dysinger.net
46
+ post_install_message:
47
+ rdoc_options: []
48
+
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: "0"
62
+ version:
63
+ requirements: []
64
+
65
+ rubyforge_project:
66
+ rubygems_version: 1.0.1
67
+ signing_key:
68
+ specification_version: 2
69
+ summary: Amazon SDB API
70
+ test_files: []
71
+