aws-sdb 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+