aws_sdb_bare 1.2.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/.document +5 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.rdoc +88 -0
- data/Rakefile +64 -0
- data/VERSION.yml +4 -0
- data/aws_sdb_bare.gemspec +58 -0
- data/lib/aws_sdb_bare/request.rb +318 -0
- data/lib/aws_sdb_bare/response.rb +163 -0
- data/lib/aws_sdb_bare.rb +7 -0
- data/test/aws_sdb_request_template_test.rb +75 -0
- data/test/aws_sdb_request_test.rb +45 -0
- data/test/aws_sdb_response_test.rb +111 -0
- data/test/samples/responses.rb +27 -0
- data/test/test_helper.rb +27 -0
- metadata +74 -0
data/.document
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Paolo Negri
|
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 PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
= aws_sdb_bare
|
2
|
+
|
3
|
+
AwsSdb Bare is a ruby gem intended to provide a basic modular interface to use
|
4
|
+
the SimpleDB service provided by Amazon.
|
5
|
+
|
6
|
+
== Concept
|
7
|
+
|
8
|
+
The aim of the project is to not commit to any http handling strategy and to
|
9
|
+
not enforce any response parsing solution by default.
|
10
|
+
|
11
|
+
The idea behind this project is to provide a basic layer on top of which
|
12
|
+
more high level libraries can be built.
|
13
|
+
|
14
|
+
The library provides XML response parsing through Hpricot or Nokogiri to
|
15
|
+
run the test against a specific parser
|
16
|
+
|
17
|
+
rake nokogiri test
|
18
|
+
|
19
|
+
Or
|
20
|
+
|
21
|
+
rake hpricot test
|
22
|
+
|
23
|
+
|
24
|
+
== Project sources
|
25
|
+
|
26
|
+
the project is hosted on github http://github.com/hungryblank/aws_sdb_bare
|
27
|
+
|
28
|
+
blog posts explaining the usage can be found on http://assertbuggy.blogspot.com
|
29
|
+
|
30
|
+
== Support and bug tracking
|
31
|
+
|
32
|
+
issues can be posted on github http://github.com/hungryblank/aws_sdb_bare
|
33
|
+
forking and patch proposal throguh github are encouraged
|
34
|
+
|
35
|
+
== Installation
|
36
|
+
|
37
|
+
sudo gem install hungryblank-aws_sdb_bare -s http://gems.github.com
|
38
|
+
|
39
|
+
== Usage - Requests
|
40
|
+
|
41
|
+
The most basic primitive is the request, as a sample here's how a ListDomains
|
42
|
+
request is generated
|
43
|
+
|
44
|
+
require 'rubygems'
|
45
|
+
require 'aws_sdb_bare'
|
46
|
+
|
47
|
+
ENV['AMAZON_ACCESS_KEY_ID'] = 'your_amazon_access_key'
|
48
|
+
ENV['AMAZON_SECRET_ACCESS_KEY'] = 'your_amazon_secret'
|
49
|
+
|
50
|
+
#create the request object
|
51
|
+
request = AwsSdb::Request::Base.new('GET', 'Action' => 'ListDomains')
|
52
|
+
|
53
|
+
#using curl we can send the request as http get and print out the xml
|
54
|
+
#response with the domains under your account
|
55
|
+
puts `curl #{request.uri}`
|
56
|
+
|
57
|
+
The gem provides classes to create directly all the requests listed in
|
58
|
+
Amazon's 2007 11 07 specs so you can do
|
59
|
+
|
60
|
+
request = AwsSdb::Request::ListDomains.new
|
61
|
+
|
62
|
+
Instead of using the Base class like the previous example.
|
63
|
+
|
64
|
+
Please refer to the gem rdocs and to the amazon documentation to see
|
65
|
+
a list of all the available requests and their parameters
|
66
|
+
|
67
|
+
== Usage - Responses
|
68
|
+
|
69
|
+
To parse a response you simply call AwsSdb::Response.parse(xml_doc) example:
|
70
|
+
|
71
|
+
require 'open-uri'
|
72
|
+
require 'aws_sdb_bare'
|
73
|
+
#the following line could require nokogiri instead
|
74
|
+
require 'hpricot'
|
75
|
+
|
76
|
+
request = AwsSdb::Request::ListDomains.new
|
77
|
+
xml_response = open request.uri
|
78
|
+
response = AwsSdb::Response.parse(xml_response)
|
79
|
+
puts response.domains
|
80
|
+
puts response.metadata.box_usage
|
81
|
+
|
82
|
+
== Documentation
|
83
|
+
|
84
|
+
Rdoc are available at http://rdoc.elastastic.com/aws_sdb_bare
|
85
|
+
|
86
|
+
== Copyright
|
87
|
+
|
88
|
+
Copyright (c) 2009 Paolo Negri. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "aws_sdb_bare"
|
8
|
+
gem.summary = %Q{Amazon AWS SDB SimpleDB client library, low level and http lib agnostic}
|
9
|
+
gem.email = "hungryblank@gmail.com"
|
10
|
+
gem.homepage = "http://github.com/hungryblank/aws_sdb_bare"
|
11
|
+
gem.authors = ["Paolo Negri"]
|
12
|
+
|
13
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
14
|
+
end
|
15
|
+
rescue LoadError
|
16
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'rake/testtask'
|
20
|
+
Rake::TestTask.new(:test) do |test|
|
21
|
+
test.libs << 'lib' << 'test'
|
22
|
+
test.pattern = 'test/**/*_test.rb'
|
23
|
+
test.verbose = true
|
24
|
+
end
|
25
|
+
|
26
|
+
begin
|
27
|
+
require 'rcov/rcovtask'
|
28
|
+
Rcov::RcovTask.new do |test|
|
29
|
+
test.libs << 'test'
|
30
|
+
test.pattern = 'test/**/*_test.rb'
|
31
|
+
test.verbose = true
|
32
|
+
end
|
33
|
+
rescue LoadError
|
34
|
+
task :rcov do
|
35
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
task :default => :test
|
41
|
+
|
42
|
+
task :hpricot do
|
43
|
+
ENV['PARSER'] = 'hpricot'
|
44
|
+
end
|
45
|
+
|
46
|
+
task :nokogiri do
|
47
|
+
ENV['PARSER'] = 'nokogiri'
|
48
|
+
end
|
49
|
+
|
50
|
+
require 'rake/rdoctask'
|
51
|
+
Rake::RDocTask.new do |rdoc|
|
52
|
+
if File.exist?('VERSION.yml')
|
53
|
+
config = YAML.load(File.read('VERSION.yml'))
|
54
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
55
|
+
else
|
56
|
+
version = ""
|
57
|
+
end
|
58
|
+
|
59
|
+
rdoc.rdoc_dir = 'rdoc'
|
60
|
+
rdoc.title = "aws_sdb_bare #{version}"
|
61
|
+
rdoc.rdoc_files.include('README*')
|
62
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
63
|
+
end
|
64
|
+
|
data/VERSION.yml
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{aws_sdb_bare}
|
8
|
+
s.version = "1.2.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Paolo Negri"]
|
12
|
+
s.date = %q{2010-02-20}
|
13
|
+
s.email = %q{hungryblank@gmail.com}
|
14
|
+
s.extra_rdoc_files = [
|
15
|
+
"LICENSE",
|
16
|
+
"README.rdoc"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
".document",
|
20
|
+
".gitignore",
|
21
|
+
"LICENSE",
|
22
|
+
"README.rdoc",
|
23
|
+
"Rakefile",
|
24
|
+
"VERSION.yml",
|
25
|
+
"aws_sdb_bare.gemspec",
|
26
|
+
"lib/aws_sdb_bare.rb",
|
27
|
+
"lib/aws_sdb_bare/request.rb",
|
28
|
+
"lib/aws_sdb_bare/response.rb",
|
29
|
+
"test/aws_sdb_request_template_test.rb",
|
30
|
+
"test/aws_sdb_request_test.rb",
|
31
|
+
"test/aws_sdb_response_test.rb",
|
32
|
+
"test/samples/responses.rb",
|
33
|
+
"test/test_helper.rb"
|
34
|
+
]
|
35
|
+
s.homepage = %q{http://github.com/hungryblank/aws_sdb_bare}
|
36
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
37
|
+
s.require_paths = ["lib"]
|
38
|
+
s.rubygems_version = %q{1.3.5}
|
39
|
+
s.summary = %q{Amazon AWS SDB SimpleDB client library, low level and http lib agnostic}
|
40
|
+
s.test_files = [
|
41
|
+
"test/test_helper.rb",
|
42
|
+
"test/aws_sdb_request_test.rb",
|
43
|
+
"test/samples/responses.rb",
|
44
|
+
"test/aws_sdb_response_test.rb",
|
45
|
+
"test/aws_sdb_request_template_test.rb"
|
46
|
+
]
|
47
|
+
|
48
|
+
if s.respond_to? :specification_version then
|
49
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
50
|
+
s.specification_version = 3
|
51
|
+
|
52
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
53
|
+
else
|
54
|
+
end
|
55
|
+
else
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
@@ -0,0 +1,318 @@
|
|
1
|
+
module AwsSdb
|
2
|
+
|
3
|
+
module Request
|
4
|
+
|
5
|
+
class Base
|
6
|
+
|
7
|
+
HOST = 'sdb.amazonaws.com'
|
8
|
+
|
9
|
+
attr_accessor :account, :secret, :params
|
10
|
+
|
11
|
+
def initialize(method, params, opts={})
|
12
|
+
@account = opts[:account] || ENV['AMAZON_ACCESS_KEY_ID']
|
13
|
+
@secret = opts[:secret] || ENV['AMAZON_SECRET_ACCESS_KEY']
|
14
|
+
raise <<-end_msg unless @account && @secret
|
15
|
+
Amazon AWS account or access key not defined
|
16
|
+
Please pass {:account => 'your account', :secret => 'your secret'}
|
17
|
+
as a last argument or define the following environment variables
|
18
|
+
ENV['AMAZON_ACCESS_KEY_ID']
|
19
|
+
ENV['AMAZON_SECRET_ACCESS_KEY']
|
20
|
+
end_msg
|
21
|
+
@method = method
|
22
|
+
@params = params
|
23
|
+
add_req_data_to_params
|
24
|
+
end
|
25
|
+
|
26
|
+
#Hostname for the request
|
27
|
+
def host
|
28
|
+
HOST
|
29
|
+
end
|
30
|
+
|
31
|
+
#Uri path
|
32
|
+
def path
|
33
|
+
'/'
|
34
|
+
end
|
35
|
+
|
36
|
+
#The full uri for the request, it takes the protocol as argument
|
37
|
+
def uri(protocol = 'http')
|
38
|
+
"#{protocol}://" + host + path + '?' + uri_query
|
39
|
+
end
|
40
|
+
|
41
|
+
#Only the query part of the uri
|
42
|
+
def uri_query
|
43
|
+
params_query + '&Signature=' + signature
|
44
|
+
end
|
45
|
+
|
46
|
+
alias :to_s :uri
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def add_req_data_to_params
|
51
|
+
@params.update({'Version' => '2007-11-07',
|
52
|
+
'SignatureVersion' => '2',
|
53
|
+
'SignatureMethod' => 'HmacSHA256',
|
54
|
+
'AWSAccessKeyId' => @account,
|
55
|
+
'Timestamp' => Time.now.gmtime.iso8601})
|
56
|
+
end
|
57
|
+
|
58
|
+
def params_query
|
59
|
+
@params_query ||= @params.keys.sort.map do |key|
|
60
|
+
key + '=' + escape(@params[key].to_s)
|
61
|
+
end.join('&')
|
62
|
+
end
|
63
|
+
|
64
|
+
def data_to_sign
|
65
|
+
[@method, host, path, params_query].join("\n")
|
66
|
+
end
|
67
|
+
|
68
|
+
def signature
|
69
|
+
@signature ||= begin
|
70
|
+
digest = OpenSSL::Digest::Digest.new('sha256')
|
71
|
+
hmac = OpenSSL::HMAC.digest(digest, @secret, data_to_sign)
|
72
|
+
escape(Base64.encode64(hmac).strip)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def escape(string)
|
77
|
+
URI.escape(string, /[^-_.~a-zA-Z\d]/)
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
#Is the parent of all the request templates, defines some methods
|
83
|
+
class Template
|
84
|
+
|
85
|
+
class << self
|
86
|
+
|
87
|
+
#Generates method to allow shorter expressions in parameter
|
88
|
+
#definition takes an hash
|
89
|
+
#
|
90
|
+
# shortcuts({'LongUrlQueryParams' => :abbr})
|
91
|
+
#
|
92
|
+
#will cause :abbr params to be expanded in LongUrlQueryParams
|
93
|
+
def shortcuts(shortcuts_hash)
|
94
|
+
shortcuts_hash.each do |full_key, shortcut|
|
95
|
+
define_method("#{shortcut}=") do |value|
|
96
|
+
@params[full_key] = value
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
#takes the params for the request and an optional options hash
|
104
|
+
#params for the request are request specific, options accepted are only
|
105
|
+
#AWS credentials in form of
|
106
|
+
#
|
107
|
+
# {:account => 'your account', :secret => 'your secret'}
|
108
|
+
def initialize(params={}, opts={})
|
109
|
+
@params, @opts = params, opts
|
110
|
+
end
|
111
|
+
|
112
|
+
def token=(value)
|
113
|
+
@params['NextToken'] = value
|
114
|
+
end
|
115
|
+
|
116
|
+
def attributes=(attributes)
|
117
|
+
attributes.each_with_index do |attribute, index|
|
118
|
+
@params["AttributeName.#{index}"] = attribute.to_s
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def request
|
123
|
+
expand!
|
124
|
+
@request ||= begin
|
125
|
+
Base.new('GET', @params.merge('Action' => self.class.to_s.split(':')[-1]), @opts)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
[:to_s, :uri, :uri_query].each do |method|
|
130
|
+
define_method(method) do
|
131
|
+
request.send(method)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
def expand!
|
138
|
+
@params.keys.each do |key|
|
139
|
+
send("#{key}=", @params.delete(key)) if respond_to?("#{key}=")
|
140
|
+
end
|
141
|
+
@params
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
#List the domains
|
147
|
+
#
|
148
|
+
# ListDomains.new({:max => 4}, {:account => 'account', :secret => 'secret'})
|
149
|
+
#
|
150
|
+
#Shortcut in params:
|
151
|
+
#
|
152
|
+
# :max - the maximum number of domain that can be returned
|
153
|
+
# :token - the token for follow up requests
|
154
|
+
class ListDomains < Template
|
155
|
+
shortcuts({'MaxNumberOfDomains' => :max})
|
156
|
+
end
|
157
|
+
|
158
|
+
#Creates a new domain
|
159
|
+
#
|
160
|
+
# CreateDomain.new({:name => 'my_domain'})
|
161
|
+
#
|
162
|
+
#Shortcut in params:
|
163
|
+
#
|
164
|
+
# :name - the name for the new domain
|
165
|
+
class CreateDomain < Template
|
166
|
+
shortcuts({'DomainName' => :name})
|
167
|
+
end
|
168
|
+
|
169
|
+
#Deletes a domain
|
170
|
+
#
|
171
|
+
# DeleteDomain.new({:name => 'my_domain'})
|
172
|
+
#
|
173
|
+
#Shortcut in params:
|
174
|
+
#
|
175
|
+
# :name - the name for domain
|
176
|
+
class DeleteDomain < Template
|
177
|
+
shortcuts({'DomainName' => :name})
|
178
|
+
end
|
179
|
+
|
180
|
+
#Provides informations for a domain
|
181
|
+
#
|
182
|
+
# DomainMetadata.new({:name => 'my_domain'})
|
183
|
+
#
|
184
|
+
#Shortcut in params:
|
185
|
+
#
|
186
|
+
# :name - the name for domain
|
187
|
+
class DomainMetadata < Template
|
188
|
+
shortcuts({'DomainName' => :name})
|
189
|
+
end
|
190
|
+
|
191
|
+
#Get the attributes for an item
|
192
|
+
#
|
193
|
+
# GetAttributes.new({:name => 'my_item', :attributes => [:first, :second]})
|
194
|
+
#
|
195
|
+
#Shortcut in params:
|
196
|
+
#
|
197
|
+
# :name - the item name
|
198
|
+
# :attributes - the list of the attributes to fetch, if not specified
|
199
|
+
# fetches all the attributes
|
200
|
+
class GetAttributes < Template
|
201
|
+
shortcuts({'ItemName' => :name, 'DomainName' => :domain})
|
202
|
+
end
|
203
|
+
|
204
|
+
#Add attributes to an item or create a new item whith the given attributes
|
205
|
+
#
|
206
|
+
# req = PutAttributes.new({:name => 'my_item'})
|
207
|
+
# req.attributes = {:color => :black, :shape => {:value => :square, :replace => true}}
|
208
|
+
#
|
209
|
+
#Note: in this request the value 'black' will be added to other color
|
210
|
+
#values if they already exist, while the shape value 'square' will replace
|
211
|
+
#pre existing values of shape
|
212
|
+
#the request, adding the :attributes to the params hash in the new method
|
213
|
+
#would have the same effect
|
214
|
+
#
|
215
|
+
#Note: in the sample above attributes are defined after initializing
|
216
|
+
#the request, adding the :attributes to the params hash in the new method
|
217
|
+
#would have the same effect
|
218
|
+
class PutAttributes < Template
|
219
|
+
|
220
|
+
attr_accessor :attributes
|
221
|
+
|
222
|
+
class << self
|
223
|
+
|
224
|
+
#multiple PutAttributes requests can be batched in one BatchPutAttributes
|
225
|
+
#just define the single PutAttributes and then pass them as args to
|
226
|
+
#the PutAttributes::batch method and you'll be returned the batched
|
227
|
+
#request.
|
228
|
+
#At the moment of writing the maximum number of requests you can batch
|
229
|
+
#are 25
|
230
|
+
def batch(*requests)
|
231
|
+
items_params = requests.map { |r| r.send(:expand!) }
|
232
|
+
first_request = requests.first.request
|
233
|
+
account = first_request.account
|
234
|
+
secret = first_request.secret
|
235
|
+
batch_params = {}
|
236
|
+
batch_params['DomainName'] ||= first_request.params['DomainName']
|
237
|
+
items_params.each_with_index do |params, index|
|
238
|
+
params.keys.each do |key|
|
239
|
+
batch_params["Item.#{index.to_s + '.' + key}"] = params[key] if key =~ /^ItemName|Attribute/
|
240
|
+
end
|
241
|
+
end
|
242
|
+
BatchPutAttributes.new(batch_params, {:account => account, :secret => secret})
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
shortcuts({'ItemName' => :name, 'DomainName' => :domain})
|
247
|
+
|
248
|
+
def expand_attributes!
|
249
|
+
index = 0
|
250
|
+
@attributes.keys.each do |attr_name|
|
251
|
+
replace = nil
|
252
|
+
values = case @attributes[attr_name]
|
253
|
+
when Hash
|
254
|
+
replace = @attributes[attr_name][:replace].to_s
|
255
|
+
@attributes[attr_name][:value].to_s
|
256
|
+
else
|
257
|
+
@attributes[attr_name].to_s
|
258
|
+
end
|
259
|
+
values = [values] unless values.is_a?(Array)
|
260
|
+
values.each do |value|
|
261
|
+
@params["Attribute.#{index}.Replace"] = replace if replace
|
262
|
+
@params["Attribute.#{index}.Name"] = attr_name.to_s
|
263
|
+
@params["Attribute.#{index}.Value"] = value
|
264
|
+
index += 1
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
private
|
270
|
+
|
271
|
+
def expand!
|
272
|
+
super
|
273
|
+
expand_attributes!
|
274
|
+
end
|
275
|
+
|
276
|
+
end
|
277
|
+
|
278
|
+
class BatchPutAttributes < Template
|
279
|
+
shortcuts({'DomainName' => :domain})
|
280
|
+
end
|
281
|
+
|
282
|
+
#delete some attributes from the given item, if no attributes
|
283
|
+
#are provided the whole item is deleted
|
284
|
+
#
|
285
|
+
# DeleteAttributes.new(:domain => 'test_domain', :name => 'item_1'
|
286
|
+
# :attributes => [:color, :shape])
|
287
|
+
class DeleteAttributes < Template
|
288
|
+
shortcuts({'ItemName' => :name, 'DomainName' => :domain})
|
289
|
+
end
|
290
|
+
|
291
|
+
#execute a select statement
|
292
|
+
# Select.new(:query => "SELECT * FROM test_domain")
|
293
|
+
class Select < Template
|
294
|
+
shortcuts({'SelectExpression' => :query})
|
295
|
+
end
|
296
|
+
|
297
|
+
#execute a query statement returns the array of the item names
|
298
|
+
#satisfying the statment
|
299
|
+
#
|
300
|
+
# Query.new(:domain => 'test_domain', :query => "['shape' = 'square']")
|
301
|
+
class Query < Template
|
302
|
+
shortcuts({'DomainName' => :domain,
|
303
|
+
'QueryExpression' => :query,
|
304
|
+
'MaxNumberOfItems' => :max})
|
305
|
+
end
|
306
|
+
|
307
|
+
#execute a query with attributes statement returns the items
|
308
|
+
#satisfying statement
|
309
|
+
#
|
310
|
+
# QueryWithAttributes.new(:domain => 'test_domain', :query => "['shape' = 'square']")
|
311
|
+
class QueryWithAttributes < Template
|
312
|
+
shortcuts({'DomainName' => :domain,
|
313
|
+
'QueryExpression' => :query,
|
314
|
+
'MaxNumberOfItems' => :max})
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
module AwsSdb
|
2
|
+
|
3
|
+
module Response
|
4
|
+
|
5
|
+
MEMBERS = {}
|
6
|
+
|
7
|
+
class UnknownResponse < ArgumentError
|
8
|
+
end
|
9
|
+
|
10
|
+
#returns an instance of the response, the class of the response is determined
|
11
|
+
#by the response content
|
12
|
+
def self.parse(doc)
|
13
|
+
@@parser ||= Hpricot if defined?(Hpricot)
|
14
|
+
@@parser ||= Nokogiri if defined?(Nokogiri)
|
15
|
+
parsed_doc = @@parser.XML(doc)
|
16
|
+
begin
|
17
|
+
MEMBERS[parsed_doc.root.name].new(parsed_doc)
|
18
|
+
rescue
|
19
|
+
raise UnknownResponse, "unkwonw response #{doc.inspect}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module XmlUtils
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def xml_attr_reader(*attributes)
|
28
|
+
attributes.each do |attr|
|
29
|
+
define_method(attr.gsub(/[A-Z]+/,'\1_\0').downcase[1..-1]) do
|
30
|
+
value = @doc.at(attr).inner_html
|
31
|
+
value = yield(value) if block_given?
|
32
|
+
value
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
class Base
|
40
|
+
|
41
|
+
class << self
|
42
|
+
|
43
|
+
def register
|
44
|
+
Response::MEMBERS[to_s.split(':').pop + 'Response'] = self
|
45
|
+
end
|
46
|
+
|
47
|
+
def has_items
|
48
|
+
define_method :items do
|
49
|
+
@items ||= begin
|
50
|
+
(@doc / 'Item').inject({}) do |items_hash, item|
|
51
|
+
items_hash[item.at('Name').inner_html] = attributes_hash(item)
|
52
|
+
items_hash
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
def initialize(doc)
|
61
|
+
@doc = doc
|
62
|
+
end
|
63
|
+
|
64
|
+
def metadata
|
65
|
+
@metadata ||= Metadata.new(@doc.at('ResponseMetadata'))
|
66
|
+
end
|
67
|
+
|
68
|
+
#the token to get the following chunk of response in case the response
|
69
|
+
#is broken in parts due to the size limit being exceeded
|
70
|
+
def token
|
71
|
+
if token_element = @doc.at('NextToken')
|
72
|
+
token_element.inner_html
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def attributes_hash(element)
|
79
|
+
attr_hash = Hash.new() { |h, k| h[k] = [] }
|
80
|
+
(element / 'Attribute').each do |attribute|
|
81
|
+
attr_hash[attribute.at('Name').inner_html] << attribute.at('Value').inner_html
|
82
|
+
end
|
83
|
+
attr_hash
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
%w(BatchPutAttributesResponse)
|
89
|
+
|
90
|
+
class GetAttributes < Base
|
91
|
+
|
92
|
+
register
|
93
|
+
|
94
|
+
#hash of item attributes
|
95
|
+
def attributes
|
96
|
+
@attributes ||= attributes_hash(@doc)
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
class Select < Base
|
102
|
+
register
|
103
|
+
has_items
|
104
|
+
end
|
105
|
+
|
106
|
+
class QueryWithAttributes < Base
|
107
|
+
register
|
108
|
+
has_items
|
109
|
+
end
|
110
|
+
|
111
|
+
class Query < Base
|
112
|
+
|
113
|
+
register
|
114
|
+
|
115
|
+
def item_names
|
116
|
+
@items ||= (@doc / 'ItemName').map { |i| i.inner_html }
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
class ListDomains < Base
|
122
|
+
|
123
|
+
register
|
124
|
+
|
125
|
+
def domains
|
126
|
+
@domains ||= (@doc / 'DomainName').map { |d| d.inner_html }
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
class DomainMetadata < Base
|
132
|
+
|
133
|
+
extend XmlUtils
|
134
|
+
register
|
135
|
+
xml_attr_reader('ItemCount', 'ItemNamesSizeBytes', 'AttributeNameCount',
|
136
|
+
'AttributeNamesSizeBytes', 'AttributeValueCount',
|
137
|
+
'AttributeValuesSizeBytes', 'Timestamp') { |value| value.to_i }
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
%w(CreateDomain DeleteDomain PutAttributes BatchPutAttributes DeleteAttributes).each do |klass|
|
142
|
+
module_eval <<-eval_end
|
143
|
+
class #{klass} < Base
|
144
|
+
register
|
145
|
+
end
|
146
|
+
eval_end
|
147
|
+
end
|
148
|
+
|
149
|
+
class Metadata
|
150
|
+
|
151
|
+
extend XmlUtils
|
152
|
+
|
153
|
+
xml_attr_reader 'RequestId', 'BoxUsage'
|
154
|
+
|
155
|
+
def initialize(doc)
|
156
|
+
@doc = doc
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
data/lib/aws_sdb_bare.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
|
+
|
3
|
+
class AwsSdbRequestTemplateTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
include AwsSdb::Request
|
6
|
+
|
7
|
+
def setup
|
8
|
+
ENV['AMAZON_ACCESS_KEY_ID'] = 'a'
|
9
|
+
ENV['AMAZON_SECRET_ACCESS_KEY'] = 'a'
|
10
|
+
end
|
11
|
+
|
12
|
+
should "add the token using shortcut" do
|
13
|
+
req = Template.new({'a' => '1', :token => 'this_is_the_token'})
|
14
|
+
assert_attribute({'NextToken' => 'this_is_the_token'}, req.uri_query)
|
15
|
+
end
|
16
|
+
|
17
|
+
should "add attributes and number them" do
|
18
|
+
req = Template.new({'a' => 1, :attributes => [:attr_1, :attr_2]}, {:account => 'account', :secret => 'secret'})
|
19
|
+
assert_attribute({'AttributeName.0' => 'attr_1'}, req.uri_query)
|
20
|
+
assert_attribute({'AttributeName.1' => 'attr_2'}, req.uri_query)
|
21
|
+
end
|
22
|
+
|
23
|
+
should "generate all the parameters for a ListDomains" do
|
24
|
+
req = ListDomains.new({:max => 4}, {:account => 'account', :secret => 'secret'})
|
25
|
+
assert_attribute({'Action' => 'ListDomains'}, req.uri_query)
|
26
|
+
assert_attribute({'MaxNumberOfDomains' => '4'}, req.uri_query)
|
27
|
+
end
|
28
|
+
|
29
|
+
should "generate all the parameters for an CreateDomain" do
|
30
|
+
req = CreateDomain.new({:name => 'my_domain'}, {:account => 'account', :secret => 'secret'})
|
31
|
+
assert_attribute({'Action' => 'CreateDomain'}, req.uri_query)
|
32
|
+
assert_attribute({'DomainName' => 'my_domain'}, req.uri_query)
|
33
|
+
end
|
34
|
+
|
35
|
+
should "generate all the parameters for a DeleteDomain" do
|
36
|
+
req = DeleteDomain.new({:name => 'my_domain'}, {:account => 'account', :secret => 'secret'})
|
37
|
+
assert_attribute({'Action' => 'DeleteDomain'}, req.uri_query)
|
38
|
+
assert_attribute({'DomainName' => 'my_domain'}, req.uri_query)
|
39
|
+
end
|
40
|
+
|
41
|
+
should "generate all the parameters for a GetAttributes" do
|
42
|
+
req = GetAttributes.new({:name => 'my_item', :attributes => [:first, :second]}, {:account => 'account', :secret => 'secret'})
|
43
|
+
assert_attribute({'Action' => 'GetAttributes'}, req.uri_query)
|
44
|
+
assert_attribute({'ItemName' => 'my_item'}, req.uri_query)
|
45
|
+
end
|
46
|
+
|
47
|
+
should "generate all the parameters for a PutAttributes" do
|
48
|
+
req = PutAttributes.new({:name => 'my_domain', :name => 'my_item'},
|
49
|
+
{:account => 'account', :secret => 'secret'})
|
50
|
+
req.attributes = {:color => :black, :shape => {:value => :square, :replace => true}}
|
51
|
+
assert_attribute({'Action' => 'PutAttributes'}, req.uri_query)
|
52
|
+
assert_attribute({'ItemName' => 'my_item'}, req.uri_query)
|
53
|
+
shape_index = query_hash(req.uri_query)['Attribute.0.Name'] == 'shape' ? 0 : 1
|
54
|
+
assert_attribute({"Attribute.#{shape_index}.Name" => 'shape'}, req.uri_query)
|
55
|
+
assert_attribute({"Attribute.#{shape_index}.Value" => 'square'}, req.uri_query)
|
56
|
+
assert_attribute({"Attribute.#{shape_index}.Replace" => 'true'}, req.uri_query)
|
57
|
+
color_index = (shape_index - 1).abs
|
58
|
+
assert_attribute({"Attribute.#{color_index}.Name" => 'color'}, req.uri_query)
|
59
|
+
assert_attribute({"Attribute.#{color_index}.Value" => 'black'}, req.uri_query)
|
60
|
+
assert_attribute({"Attribute.#{color_index}.Replace" => nil}, req.uri_query)
|
61
|
+
end
|
62
|
+
|
63
|
+
should "generate all the parameters for a Select" do
|
64
|
+
req = Select.new({:query => 'Select * from my_domain'}, {:account => 'account', :secret => 'secret'})
|
65
|
+
assert_attribute({'Action' => 'Select'}, req.uri_query)
|
66
|
+
assert_attribute({'SelectExpression' => /^Select/}, req.uri_query)
|
67
|
+
end
|
68
|
+
|
69
|
+
should "generate all the parameters for a Query" do
|
70
|
+
req = Query.new({:query => "['Color' = 'blue']", :domain => 'my_domain', :max => 10 }, {:account => 'account', :secret => 'secret'})
|
71
|
+
assert_attribute({'Action' => 'Query'}, req.uri_query)
|
72
|
+
assert_attribute({'QueryExpression' => /Color.*blue/}, req.uri_query)
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
|
+
|
3
|
+
class AwsSdbRequestTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
include AwsSdb::Request
|
6
|
+
|
7
|
+
def setup
|
8
|
+
ENV['AMAZON_ACCESS_KEY_ID'] = 'a'
|
9
|
+
ENV['AMAZON_SECRET_ACCESS_KEY'] = 'a'
|
10
|
+
end
|
11
|
+
|
12
|
+
should "prepend the verb and host to the params for signing" do
|
13
|
+
req = Base.new('GET', 'Action' => 'ListDomains')
|
14
|
+
assert_match /^GET\n#{AwsSdb::Request::Base::HOST}/, req.send(:data_to_sign)
|
15
|
+
end
|
16
|
+
|
17
|
+
should "build the query and sort alphabetically the parameters" do
|
18
|
+
req = Base.new('GET', 'b' => '2', 'x' => '1', 'a' => '1')
|
19
|
+
assert_match /a=1&b=2&x=1/, req.send(:params_query)
|
20
|
+
end
|
21
|
+
|
22
|
+
should "url encode the param values" do
|
23
|
+
safe_chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.~"
|
24
|
+
some_unsafe_chars = "+*!@£$%^&*()"
|
25
|
+
req = Base.new('GET', 'SafeChars' => safe_chars, 'UnsafeChars' => some_unsafe_chars)
|
26
|
+
assert_attribute({'SafeChars' => safe_chars}, req.send(:params_query))
|
27
|
+
assert_attribute({'UnsafeChars' => '%2B%2A%21%40%C2%A3%24%25%5E%26%2A%28%29'}, req.send(:params_query))
|
28
|
+
end
|
29
|
+
|
30
|
+
should "append the signature method to the request" do
|
31
|
+
req = Base.new('GET', {'a' => '1'}, {:account => 'account', :secret => 'secret'})
|
32
|
+
assert_match /&SignatureMethod=HmacSHA256&SignatureVersion=2/, req.uri_query
|
33
|
+
end
|
34
|
+
|
35
|
+
should "add the actual signature" do
|
36
|
+
req = Base.new('GET', {'a' => '1'}, {:account => 'account', :secret => 'secret'})
|
37
|
+
assert_attribute({'Signature' => /^[0-9a-zA-Z%]+$/}, req.uri_query)
|
38
|
+
end
|
39
|
+
|
40
|
+
should "build a correct uri" do
|
41
|
+
req = Base.new('GET', {'a' => '1'}, {:account => 'account', :secret => 'secret'})
|
42
|
+
assert URI.parse(req.uri)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
|
+
require File.join(File.dirname(__FILE__), 'samples/responses')
|
3
|
+
|
4
|
+
ENV['PARSER'] ||= 'hpricot'
|
5
|
+
|
6
|
+
require ENV['PARSER']
|
7
|
+
|
8
|
+
class AwsSdbRequestTest < Test::Unit::TestCase
|
9
|
+
|
10
|
+
include AwsSdb
|
11
|
+
|
12
|
+
context "parsing a GetAttributes" do
|
13
|
+
|
14
|
+
should "generate the attributes hash" do
|
15
|
+
response = Response.parse(SAMPLE_RESPONSES['GetAttributes'])
|
16
|
+
assert_same_elements ["20:45:22", "22:50:25", "22:51:06", "20:44:30", "20:44:19",
|
17
|
+
"22:50:55", "22:49:55", "20:30:08", "22:51:02"], response.attributes['last_visit']
|
18
|
+
assert_equal ['62'], response.attributes['code']
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
context "parsing a Select" do
|
24
|
+
|
25
|
+
should "generate the items hash" do
|
26
|
+
response = Response.parse(SAMPLE_RESPONSES['Select'])
|
27
|
+
assert_same_elements ["bar81", "bar83", "bar98", "my_entity5"], response.items.keys
|
28
|
+
end
|
29
|
+
|
30
|
+
should "generate the attributes for each item" do
|
31
|
+
response = Response.parse(SAMPLE_RESPONSES['Select'])
|
32
|
+
assert_equal({"code" => ["48"], "foo" => ["value"]}, response.items['bar98'])
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
context "parsing a Query" do
|
38
|
+
|
39
|
+
should "generate the item names array" do
|
40
|
+
response = Response.parse(SAMPLE_RESPONSES['Query'])
|
41
|
+
assert_same_elements ["bar81", "bar83", "bar98", "my_entity5"], response.item_names
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
context "parsing a QueryWithAttributes" do
|
47
|
+
|
48
|
+
should "generate the items hash" do
|
49
|
+
response = Response.parse(SAMPLE_RESPONSES['QueryWithAttributes'])
|
50
|
+
assert_equal({"code" => ["48"], "foo" => ["value"]}, response.items['bar98'])
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
context "parsing a response with token" do
|
56
|
+
|
57
|
+
should "find and return the token" do
|
58
|
+
response = Response.parse(SAMPLE_RESPONSES['ResWithToken'])
|
59
|
+
assert_equal "rO0ABXNyACdjb20uYW1hem9uLnNkcy5RdWVyeVByb2Nlc3Nvci5Nb3JlVG9rZW7racXLnINNqwMA\nCkkAFGluaXRpYWxDb25qdW5jdEluZGV4WgAOaXNQYWdlQm91bmRhcnlKAAxsYXN0RW50aXR5SURa\nAApscnFFbmFibGVkSQAPcXVlcnlDb21wbGV4aXR5SgATcXVlcnlTdHJpbmdDaGVja3N1bUkACnVu\naW9uSW5kZXhaAA11c2VRdWVyeUluZGV4TAASbGFzdEF0dHJpYnV0ZVZhbHVldAASTGphdmEvbGFu\nZy9TdHJpbmc7TAAJc29ydE9yZGVydAAvTGNvbS9hbWF6b24vc2RzL1F1ZXJ5UHJvY2Vzc29yL1F1\nZXJ5JFNvcnRPcmRlcjt4cAAAAAAATZbLYbTOwAAAAAAAAAAAAAArd5WtAAAAAAF0AAI0M35yAC1j\nb20uYW1hem9uLnNkcy5RdWVyeVByb2Nlc3Nvci5RdWVyeSRTb3J0T3JkZXIAAAAAAAAAABIAAHhy\nAA5qYXZhLmxhbmcuRW51bQAAAAAAAAAAEgAAeHB0AAlBU0NFTkRJTkd4", response.token
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
context "parsing ListDomains" do
|
65
|
+
|
66
|
+
should "return the list of domains" do
|
67
|
+
response = Response.parse(SAMPLE_RESPONSES['ListDomains'])
|
68
|
+
assert_same_elements ['myapp_development', 'myapp_production', 'myapp_test', 'test_domain'], response.domains
|
69
|
+
end
|
70
|
+
%w(ListDomains DomainMetadata)
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
context "parsing a request" do
|
75
|
+
|
76
|
+
should "return the metadata" do
|
77
|
+
response = Response.parse(SAMPLE_RESPONSES['ListDomains'])
|
78
|
+
assert_equal '0.0000071759', response.metadata.box_usage
|
79
|
+
end
|
80
|
+
%w(ListDomains DomainMetadata)
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
context "parsing DomainMetadata" do
|
85
|
+
|
86
|
+
should "provide direct acceess to attributes" do
|
87
|
+
response = Response.parse(SAMPLE_RESPONSES['DomainMetadata'])
|
88
|
+
assert_equal 282, response.item_count
|
89
|
+
assert_equal 7237, response.item_names_size_bytes
|
90
|
+
assert_equal 13, response.attribute_name_count
|
91
|
+
assert_equal 92, response.attribute_names_size_bytes
|
92
|
+
assert_equal 1095, response.attribute_value_count
|
93
|
+
assert_equal 5689, response.attribute_values_size_bytes
|
94
|
+
assert_equal 1241131496, response.timestamp
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
context "parsing simple responses" do
|
100
|
+
|
101
|
+
%w(CreateDomain DeleteDomain PutAttributes BatchPutAttributes DeleteAttributes).each do |simple_request|
|
102
|
+
|
103
|
+
should "recognize #{simple_request}" do
|
104
|
+
assert_nothing_raised { Response.parse(SAMPLE_RESPONSES[simple_request]) }
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
SAMPLE_RESPONSES = {
|
2
|
+
'GetAttributes' =>
|
3
|
+
"<?xml version=\"1.0\"?>\n<GetAttributesResponse xmlns=\"http://sdb.amazonaws.com/doc/2007-11-07/\"><GetAttributesResult><Attribute><Name>code</Name><Value>62</Value></Attribute><Attribute><Name>last_visit</Name><Value>20:30:08</Value></Attribute><Attribute><Name>last_visit</Name><Value>20:44:19</Value></Attribute><Attribute><Name>last_visit</Name><Value>20:44:30</Value></Attribute><Attribute><Name>last_visit</Name><Value>20:45:22</Value></Attribute><Attribute><Name>last_visit</Name><Value>22:49:55</Value></Attribute><Attribute><Name>last_visit</Name><Value>22:50:25</Value></Attribute><Attribute><Name>last_visit</Name><Value>22:50:55</Value></Attribute><Attribute><Name>last_visit</Name><Value>22:51:02</Value></Attribute><Attribute><Name>last_visit</Name><Value>22:51:06</Value></Attribute></GetAttributesResult><ResponseMetadata><RequestId>0d5da91d-e2c4-1de0-7106-cc1d08bf0e8f</RequestId><BoxUsage>0.0000093282</BoxUsage></ResponseMetadata></GetAttributesResponse>",
|
4
|
+
'Select' =>
|
5
|
+
"<?xml version=\"1.0\"?>\n<SelectResponse xmlns=\"http://sdb.amazonaws.com/doc/2007-11-07/\"><SelectResult><Item><Name>my_entity5</Name><Attribute><Name>last_visit</Name><Value>12:58:53</Value></Attribute><Attribute><Name>last_visit</Name><Value>13:13:05</Value></Attribute><Attribute><Name>last_visit</Name><Value>13:13:22</Value></Attribute><Attribute><Name>last_visit</Name><Value>21:45:29</Value></Attribute><Attribute><Name>last_visit</Name><Value>22:11:18</Value></Attribute><Attribute><Name>code</Name><Value>16</Value></Attribute></Item><Item><Name>bar83</Name><Attribute><Name>code</Name><Value>43</Value></Attribute></Item><Item><Name>bar81</Name><Attribute><Name>foo</Name><Value>value</Value></Attribute><Attribute><Name>code</Name><Value>44</Value></Attribute></Item><Item><Name>bar98</Name><Attribute><Name>foo</Name><Value>value</Value></Attribute><Attribute><Name>code</Name><Value>48</Value></Attribute></Item></SelectResult><ResponseMetadata><RequestId>0201e630-dea7-8c4e-88ad-d76cc297388c</RequestId><BoxUsage>0.0000503747</BoxUsage></ResponseMetadata></SelectResponse>",
|
6
|
+
'Query' =>
|
7
|
+
"<?xml version=\"1.0\"?>\n<QueryResponse xmlns=\"http://sdb.amazonaws.com/doc/2007-11-07/\"><QueryResult><ItemName>my_entity5</ItemName><ItemName>bar83</ItemName><ItemName>bar81</ItemName><ItemName>bar98</ItemName></QueryResult><ResponseMetadata><RequestId>48de733e-b08d-7aeb-b855-e08e376da538</RequestId><BoxUsage>0.0000140320</BoxUsage></ResponseMetadata></QueryResponse>",
|
8
|
+
'QueryWithAttributes' =>
|
9
|
+
"<?xml version=\"1.0\"?>\n<QueryWithAttributesResponse xmlns=\"http://sdb.amazonaws.com/doc/2007-11-07/\"><QueryWithAttributesResult><Item><Name>my_entity5</Name><Attribute><Name>last_visit</Name><Value>12:58:53</Value></Attribute><Attribute><Name>last_visit</Name><Value>13:13:05</Value></Attribute><Attribute><Name>last_visit</Name><Value>13:13:22</Value></Attribute><Attribute><Name>last_visit</Name><Value>21:45:29</Value></Attribute><Attribute><Name>last_visit</Name><Value>22:11:18</Value></Attribute><Attribute><Name>code</Name><Value>16</Value></Attribute></Item><Item><Name>bar83</Name><Attribute><Name>code</Name><Value>43</Value></Attribute></Item><Item><Name>bar81</Name><Attribute><Name>foo</Name><Value>value</Value></Attribute><Attribute><Name>code</Name><Value>44</Value></Attribute></Item><Item><Name>bar98</Name><Attribute><Name>foo</Name><Value>value</Value></Attribute><Attribute><Name>code</Name><Value>48</Value></Attribute></Item></QueryWithAttributesResult><ResponseMetadata><RequestId>d849662b-b02b-f0b8-45d2-e13685d10026</RequestId><BoxUsage>0.0000503747</BoxUsage></ResponseMetadata></QueryWithAttributesResponse>",
|
10
|
+
'CreateDomain' =>
|
11
|
+
"<?xml version=\"1.0\"?>\n<CreateDomainResponse xmlns=\"http://sdb.amazonaws.com/doc/2007-11-07/\"><ResponseMetadata><RequestId>606100ad-9b94-9e55-09f5-21de864591f2</RequestId><BoxUsage>0.0055590278</BoxUsage></ResponseMetadata></CreateDomainResponse>",
|
12
|
+
'DeleteDomain' =>
|
13
|
+
"<?xml version=\"1.0\"?>\n<DeleteDomainResponse xmlns=\"http://sdb.amazonaws.com/doc/2007-11-07/\"><ResponseMetadata><RequestId>ba116912-8684-41fd-b60f-87dda3a6d301</RequestId><BoxUsage>0.0055590278</BoxUsage></ResponseMetadata></DeleteDomainResponse>",
|
14
|
+
'PutAttributes' =>
|
15
|
+
"<?xml version=\"1.0\"?>\n<PutAttributesResponse xmlns=\"http://sdb.amazonaws.com/doc/2007-11-07/\"><ResponseMetadata><RequestId>9583937f-4127-20c8-d34c-bd44a676afad</RequestId><BoxUsage>0.0000219923</BoxUsage></ResponseMetadata></PutAttributesResponse>",
|
16
|
+
'BatchPutAttributes' =>
|
17
|
+
"<?xml version=\"1.0\"?>\n<BatchPutAttributesResponse xmlns=\"http://sdb.amazonaws.com/doc/2007-11-07/\"><ResponseMetadata><RequestId>ab252f42-e5f5-dca5-9aa9-f695cc1fb113</RequestId><BoxUsage>0.0000307892</BoxUsage></ResponseMetadata></BatchPutAttributesResponse>",
|
18
|
+
'DeleteAttributes' =>
|
19
|
+
"<?xml version=\"1.0\"?>\n<DeleteAttributesResponse xmlns=\"http://sdb.amazonaws.com/doc/2007-11-07/\"><ResponseMetadata><RequestId>cafe7877-cc29-8c41-fa68-f14101665b3a</RequestId><BoxUsage>0.0000219907</BoxUsage></ResponseMetadata></DeleteAttributesResponse>",
|
20
|
+
'ResWithToken' =>
|
21
|
+
"<?xml version=\"1.0\"?>\n<QueryWithAttributesResponse xmlns=\"http://sdb.amazonaws.com/doc/2007-11-07/\"><QueryWithAttributesResult><Item><Name>bar83</Name><Attribute><Name>code</Name><Value>43</Value></Attribute></Item><NextToken>rO0ABXNyACdjb20uYW1hem9uLnNkcy5RdWVyeVByb2Nlc3Nvci5Nb3JlVG9rZW7racXLnINNqwMA\nCkkAFGluaXRpYWxDb25qdW5jdEluZGV4WgAOaXNQYWdlQm91bmRhcnlKAAxsYXN0RW50aXR5SURa\nAApscnFFbmFibGVkSQAPcXVlcnlDb21wbGV4aXR5SgATcXVlcnlTdHJpbmdDaGVja3N1bUkACnVu\naW9uSW5kZXhaAA11c2VRdWVyeUluZGV4TAASbGFzdEF0dHJpYnV0ZVZhbHVldAASTGphdmEvbGFu\nZy9TdHJpbmc7TAAJc29ydE9yZGVydAAvTGNvbS9hbWF6b24vc2RzL1F1ZXJ5UHJvY2Vzc29yL1F1\nZXJ5JFNvcnRPcmRlcjt4cAAAAAAATZbLYbTOwAAAAAAAAAAAAAArd5WtAAAAAAF0AAI0M35yAC1j\nb20uYW1hem9uLnNkcy5RdWVyeVByb2Nlc3Nvci5RdWVyeSRTb3J0T3JkZXIAAAAAAAAAABIAAHhy\nAA5qYXZhLmxhbmcuRW51bQAAAAAAAAAAEgAAeHB0AAlBU0NFTkRJTkd4</NextToken></QueryWithAttributesResult><ResponseMetadata><RequestId>5f1cd665-843a-17df-bb07-8c3a3bbbcbdd</RequestId><BoxUsage>0.0000228636</BoxUsage></ResponseMetadata></QueryWithAttributesResponse>",
|
22
|
+
'DomainMetadata' =>
|
23
|
+
"<?xml version=\"1.0\"?>\n<DomainMetadataResponse xmlns=\"http://sdb.amazonaws.com/doc/2007-11-07/\"><DomainMetadataResult><ItemCount>282</ItemCount><ItemNamesSizeBytes>7237</ItemNamesSizeBytes><AttributeNameCount>13</AttributeNameCount><AttributeNamesSizeBytes>92</AttributeNamesSizeBytes><AttributeValueCount>1095</AttributeValueCount><AttributeValuesSizeBytes>5689</AttributeValuesSizeBytes><Timestamp>1241131496</Timestamp></DomainMetadataResult><ResponseMetadata><RequestId>fdca49d6-5d0c-1006-5571-648d29e7f183</RequestId><BoxUsage>0.0000071759</BoxUsage></ResponseMetadata></DomainMetadataResponse>",
|
24
|
+
'ListDomains' =>
|
25
|
+
"<?xml version=\"1.0\"?>\n<ListDomainsResponse xmlns=\"http://sdb.amazonaws.com/doc/2007-11-07/\"><ListDomainsResult><DomainName>myapp_development</DomainName><DomainName>myapp_production</DomainName><DomainName>myapp_test</DomainName><DomainName>test_domain</DomainName></ListDomainsResult><ResponseMetadata><RequestId>f79cd7ab-5f1e-4a32-5a90-de630ed679b8</RequestId><BoxUsage>0.0000071759</BoxUsage></ResponseMetadata></ListDomainsResponse>"
|
26
|
+
|
27
|
+
}
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'shoulda'
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
6
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
7
|
+
require 'aws_sdb_bare'
|
8
|
+
|
9
|
+
class Test::Unit::TestCase
|
10
|
+
|
11
|
+
def assert_attribute(attribute_hash, query_string)
|
12
|
+
query_hash = query_hash(query_string)
|
13
|
+
attribute_hash.keys.each do |key|
|
14
|
+
method = attribute_hash[key].is_a?(Regexp) ? :assert_match : :assert_equal
|
15
|
+
send(method, attribute_hash[key], query_hash[key])
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def query_hash(query_string)
|
20
|
+
query_hash = query_string.split('&').inject({}) do |hash, token|
|
21
|
+
key, value = *token.split('=')
|
22
|
+
hash[key] = value
|
23
|
+
hash
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: aws_sdb_bare
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Paolo Negri
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-02-20 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: hungryblank@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- LICENSE
|
24
|
+
- README.rdoc
|
25
|
+
files:
|
26
|
+
- .document
|
27
|
+
- .gitignore
|
28
|
+
- LICENSE
|
29
|
+
- README.rdoc
|
30
|
+
- Rakefile
|
31
|
+
- VERSION.yml
|
32
|
+
- aws_sdb_bare.gemspec
|
33
|
+
- lib/aws_sdb_bare.rb
|
34
|
+
- lib/aws_sdb_bare/request.rb
|
35
|
+
- lib/aws_sdb_bare/response.rb
|
36
|
+
- test/aws_sdb_request_template_test.rb
|
37
|
+
- test/aws_sdb_request_test.rb
|
38
|
+
- test/aws_sdb_response_test.rb
|
39
|
+
- test/samples/responses.rb
|
40
|
+
- test/test_helper.rb
|
41
|
+
has_rdoc: true
|
42
|
+
homepage: http://github.com/hungryblank/aws_sdb_bare
|
43
|
+
licenses: []
|
44
|
+
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options:
|
47
|
+
- --charset=UTF-8
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: "0"
|
55
|
+
version:
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: "0"
|
61
|
+
version:
|
62
|
+
requirements: []
|
63
|
+
|
64
|
+
rubyforge_project:
|
65
|
+
rubygems_version: 1.3.5
|
66
|
+
signing_key:
|
67
|
+
specification_version: 3
|
68
|
+
summary: Amazon AWS SDB SimpleDB client library, low level and http lib agnostic
|
69
|
+
test_files:
|
70
|
+
- test/test_helper.rb
|
71
|
+
- test/aws_sdb_request_test.rb
|
72
|
+
- test/samples/responses.rb
|
73
|
+
- test/aws_sdb_response_test.rb
|
74
|
+
- test/aws_sdb_request_template_test.rb
|