simplydb 0.0.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 +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +37 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/examples/interface.rb +19 -0
- data/examples/record.rb +38 -0
- data/lib/simplydb/client.rb +66 -0
- data/lib/simplydb/clients/typhoeus.rb +35 -0
- data/lib/simplydb/error.rb +60 -0
- data/lib/simplydb/extensions.rb +21 -0
- data/lib/simplydb/interface.rb +207 -0
- data/lib/simplydb/record/base.rb +253 -0
- data/lib/simplydb.rb +5 -0
- data/simplydb.gemspec +82 -0
- data/spec/client_spec.rb +55 -0
- data/spec/error_spec.rb +9 -0
- data/spec/extensions_spec.rb +15 -0
- data/spec/interface_spec.rb +223 -0
- data/spec/record/base_spec.rb +198 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +24 -0
- metadata +160 -0
@@ -0,0 +1,253 @@
|
|
1
|
+
require 'simplydb'
|
2
|
+
require 'simplydb/extensions'
|
3
|
+
require 'uuidtools'
|
4
|
+
|
5
|
+
module SimplyDB
|
6
|
+
module Record
|
7
|
+
class MissingItemName < RuntimeError; end
|
8
|
+
class ItemNotFound < RuntimeError; end
|
9
|
+
|
10
|
+
class Base
|
11
|
+
include SimplyDB::Extensions
|
12
|
+
|
13
|
+
class << self
|
14
|
+
include SimplyDB::Extensions
|
15
|
+
|
16
|
+
# This setups up the parameters for the connection for SimpleDB.
|
17
|
+
#
|
18
|
+
# ==== Parameters
|
19
|
+
# +access_key+: AWS access key.
|
20
|
+
# +secret_key+: AWS secret key.
|
21
|
+
# +force+: Force the creation of the domain. (Not yet implemented)
|
22
|
+
def establish_connection(options = {})
|
23
|
+
@connection = {
|
24
|
+
:access_key => options[:aws_access_key] || options[:access_key] || ENV['AWS_ACCESS_KEY'] || ENV['ACCESS_KEY'],
|
25
|
+
:secret_key => options[:aws_secret_key] || options[:secret_key] || ENV['AWS_SECRET_KEY'] || ENV['SECRET_KEY'],
|
26
|
+
:force => options[:force] == true ? true : false
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns the connection's paramters.
|
31
|
+
attr_accessor :connection
|
32
|
+
|
33
|
+
# Returns the interface(+SimplyDB::Interface+) the model uses to communicate with SimpleDB.
|
34
|
+
def interface
|
35
|
+
@interface ||= (establish_connection; SimplyDB::Interface.new(@connection))
|
36
|
+
end
|
37
|
+
|
38
|
+
# Set the name of the domain to associate the record with. This should be set
|
39
|
+
# to the name as it appears on SimpleDB.
|
40
|
+
# ====Paremeters
|
41
|
+
# +name+: The domain name the model will use when saving data to SimpleDB.
|
42
|
+
def set_domain_name(name)
|
43
|
+
@domain_name = name
|
44
|
+
@domain_name ||= domain_name
|
45
|
+
end
|
46
|
+
alias_method :domain_name=, :set_domain_name
|
47
|
+
|
48
|
+
# Return the domain name the model has been assigned. Default is the model
|
49
|
+
# name underscored.
|
50
|
+
#
|
51
|
+
# Person.domain_name == "person"
|
52
|
+
# MyDomain.domain_name == "my_domain"
|
53
|
+
def domain_name; @domain_name ||= underscore(name); end
|
54
|
+
|
55
|
+
# Creates the domain associated with the model based on the +domain_name+.
|
56
|
+
def create_domain
|
57
|
+
return interface.create_domain(domain_name)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Deletes the domain associated with the model based on the +domain_name+.
|
61
|
+
def delete_domain
|
62
|
+
return interface.delete_domain(domain_name)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Checks to see if the domain associated with the model exists.
|
66
|
+
def domain_exists?
|
67
|
+
interface.domain_metadata(domain_name)
|
68
|
+
return true
|
69
|
+
rescue SimplyDB::Error::NoSuchDomain => e
|
70
|
+
return false
|
71
|
+
end
|
72
|
+
|
73
|
+
# Creates an object (or multiple objects) and saves it to the database, if validations pass.
|
74
|
+
# The resulting object is returned whether the object was saved successfully to the database or not.
|
75
|
+
#
|
76
|
+
# The +attributes+ parameter can be either be a Hash or an Array of Hashes. These Hashes describe the
|
77
|
+
# attributes on the objects that are to be created.
|
78
|
+
#
|
79
|
+
# ==== Examples
|
80
|
+
# # Create a single new object
|
81
|
+
# User.create(:first_name => 'Jamie')
|
82
|
+
#
|
83
|
+
# # Create an Array of new objects
|
84
|
+
# User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }])
|
85
|
+
#
|
86
|
+
# # Create a single object and pass it into a block to set other attributes.
|
87
|
+
# User.create(:first_name => 'Jamie') do |u|
|
88
|
+
# u.is_admin = false
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
# # Creating an Array of new objects using a block, where the block is executed for each object:
|
92
|
+
# User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }]) do |u|
|
93
|
+
# u.is_admin = false
|
94
|
+
# end
|
95
|
+
def create(attributes = {}, &block)
|
96
|
+
if attributes.is_a?(Array)
|
97
|
+
attributes.collect { |attr| create(attr, &block) }
|
98
|
+
else
|
99
|
+
object = new(attributes)
|
100
|
+
yield(object) if block_given?
|
101
|
+
object.item_name ||= attributes.delete(:item_name) || UUIDTools::UUID.random_create #TODO: not depend on UUID
|
102
|
+
object.save
|
103
|
+
return object
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def find(item_names)
|
108
|
+
if item_names.is_a?(Array)
|
109
|
+
return item_names.collect {|item_name| find(item_name)}
|
110
|
+
else
|
111
|
+
attributes = interface.get_attributes(domain_name, item_names)
|
112
|
+
unless attributes.empty?
|
113
|
+
object = new(attributes)
|
114
|
+
object.item_name = item_names
|
115
|
+
object.instance_variable_set('@new_record', false)
|
116
|
+
return object
|
117
|
+
else
|
118
|
+
raise SimplyDB::Record::ItemNotFound
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def find_by_select(statement = nil, consistant_read = false)
|
124
|
+
return [] unless statement
|
125
|
+
items = interface.select(statement, consistant_read)
|
126
|
+
objects = items.collect {|item_name, attributes|
|
127
|
+
object = new(attributes)
|
128
|
+
object.item_name = item_name
|
129
|
+
object.instance_variable_set('@new_record', false)
|
130
|
+
object
|
131
|
+
}
|
132
|
+
return objects
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# New objects can be instantiated with attributes pass as a hash with teh key being the
|
137
|
+
# column name and the value being column value.
|
138
|
+
#
|
139
|
+
# ==== Parameters
|
140
|
+
# +attributes+: A hash of the attributes columns and their values.
|
141
|
+
def initialize(attributes = nil)
|
142
|
+
@attributes = {}
|
143
|
+
@new_record = true
|
144
|
+
@destroyed = false
|
145
|
+
self.attributes = attributes unless attributes.nil?
|
146
|
+
end
|
147
|
+
|
148
|
+
# Returns true if the record has not been created yet.
|
149
|
+
def new_record?
|
150
|
+
return @new_record
|
151
|
+
end
|
152
|
+
|
153
|
+
# Returns the value of the attribute identified by <tt>attr_name</tt>.
|
154
|
+
# (Alias for the protected read_attribute method).
|
155
|
+
def [](attribute_name)
|
156
|
+
return read_attribute(attribute_name)
|
157
|
+
end
|
158
|
+
|
159
|
+
# Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
|
160
|
+
# (Alias for the protected write_attribute method).
|
161
|
+
def []=(attribute_name, value)
|
162
|
+
write_attribute(attribute_name, value)
|
163
|
+
end
|
164
|
+
|
165
|
+
# Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
|
166
|
+
def attributes
|
167
|
+
attrs = {}
|
168
|
+
attribute_names.each { |name| attrs[name] = read_attribute(name) }
|
169
|
+
return attrs
|
170
|
+
end
|
171
|
+
|
172
|
+
# Allows you to set all the attributes at once by passing in a hash with keys
|
173
|
+
# matching the attribute names (which again matches the column names).
|
174
|
+
def attributes=(new_attributes)
|
175
|
+
return if new_attributes.nil?
|
176
|
+
new_attributes.each do |name, value|
|
177
|
+
if respond_to?(:"#{name}=")
|
178
|
+
send(:"#{name}=", value)
|
179
|
+
else
|
180
|
+
write_attribute(name, value)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# Checks that an attribute has been assigned a value.
|
186
|
+
def has_attribute?(attribute_name)
|
187
|
+
return @attributes.has_key?(attribute_name)
|
188
|
+
end
|
189
|
+
|
190
|
+
# Returns an array of names for the attributes available on this object sorted alphabetically.
|
191
|
+
def attribute_names
|
192
|
+
return @attributes.keys.sort
|
193
|
+
end
|
194
|
+
|
195
|
+
# Save the record to SimpleDB. Requires that an <tt>item_name</tt> has been set for the record.
|
196
|
+
def save(replace = false)
|
197
|
+
raise SimplyDB::Record::MissingItemName if @item_name.nil? || @item_name.empty?
|
198
|
+
if interface.put_attributes(domain_name, item_name, attributes, {}, replace)
|
199
|
+
@new_record = false
|
200
|
+
return true
|
201
|
+
end
|
202
|
+
return false
|
203
|
+
end
|
204
|
+
|
205
|
+
# Freeze the attributes hash such they are still accessible, even on destroyed records.
|
206
|
+
def freeze
|
207
|
+
@attributes.freeze
|
208
|
+
return self
|
209
|
+
end
|
210
|
+
|
211
|
+
# Returns +true+ if the attributes hash has been frozen.
|
212
|
+
def frozen?
|
213
|
+
@attributes.frozen?
|
214
|
+
end
|
215
|
+
|
216
|
+
#Deletes the record in the SimpleDB and freezes this instance to reflect that no changes should be made (since they can’t be persisted).
|
217
|
+
def destroy
|
218
|
+
unless new_record?
|
219
|
+
interface.delete_attributes(domain_name, item_name)
|
220
|
+
end
|
221
|
+
|
222
|
+
@destroyed = true
|
223
|
+
return freeze
|
224
|
+
end
|
225
|
+
|
226
|
+
def connection; return self.class.connection; end
|
227
|
+
def interface; return self.class.interface; end
|
228
|
+
def domain_name; return self.class.domain_name; end
|
229
|
+
|
230
|
+
def method_missing(method, *args) #:nodoc:
|
231
|
+
method = method.to_s
|
232
|
+
case method
|
233
|
+
when /=$/
|
234
|
+
return write_attribute(method.sub(/\=$/,''), args[0])
|
235
|
+
else
|
236
|
+
return read_attribute(method) if has_attribute?(method)
|
237
|
+
end
|
238
|
+
raise NoMethodError.new("Could not find method #{method}", method, *args)
|
239
|
+
end
|
240
|
+
|
241
|
+
attr_accessor :item_name
|
242
|
+
|
243
|
+
private
|
244
|
+
def read_attribute(name) #:nodoc:
|
245
|
+
return @attributes[name.to_s]
|
246
|
+
end
|
247
|
+
|
248
|
+
def write_attribute(name, value) #:nodoc:
|
249
|
+
@attributes[name.to_s] = value
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
data/lib/simplydb.rb
ADDED
data/simplydb.gemspec
ADDED
@@ -0,0 +1,82 @@
|
|
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{simplydb}
|
8
|
+
s.version = "0.0.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["JT Archie"]
|
12
|
+
s.date = %q{2010-06-25}
|
13
|
+
s.description = %q{A minimal interface to Amazon SimpleDB that has separation of interfaces. From the low level HTTP request access to high level Ruby abstraction ORM.}
|
14
|
+
s.email = %q{jtarchie@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"examples/interface.rb",
|
27
|
+
"examples/record.rb",
|
28
|
+
"lib/simplydb.rb",
|
29
|
+
"lib/simplydb/client.rb",
|
30
|
+
"lib/simplydb/clients/typhoeus.rb",
|
31
|
+
"lib/simplydb/error.rb",
|
32
|
+
"lib/simplydb/extensions.rb",
|
33
|
+
"lib/simplydb/interface.rb",
|
34
|
+
"lib/simplydb/record/base.rb",
|
35
|
+
"simplydb.gemspec",
|
36
|
+
"spec/client_spec.rb",
|
37
|
+
"spec/error_spec.rb",
|
38
|
+
"spec/extensions_spec.rb",
|
39
|
+
"spec/interface_spec.rb",
|
40
|
+
"spec/record/base_spec.rb",
|
41
|
+
"spec/spec.opts",
|
42
|
+
"spec/spec_helper.rb"
|
43
|
+
]
|
44
|
+
s.homepage = %q{http://github.com/jtarchie/simplydb}
|
45
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
46
|
+
s.require_paths = ["lib"]
|
47
|
+
s.rubygems_version = %q{1.3.7}
|
48
|
+
s.summary = %q{A minimal interface to Amazon SimpleDB.}
|
49
|
+
s.test_files = [
|
50
|
+
"spec/client_spec.rb",
|
51
|
+
"spec/error_spec.rb",
|
52
|
+
"spec/extensions_spec.rb",
|
53
|
+
"spec/interface_spec.rb",
|
54
|
+
"spec/record/base_spec.rb",
|
55
|
+
"spec/spec_helper.rb",
|
56
|
+
"examples/interface.rb",
|
57
|
+
"examples/record.rb"
|
58
|
+
]
|
59
|
+
|
60
|
+
if s.respond_to? :specification_version then
|
61
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
62
|
+
s.specification_version = 3
|
63
|
+
|
64
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
65
|
+
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
66
|
+
s.add_runtime_dependency(%q<typhoeus>, [">= 0.1.27"])
|
67
|
+
s.add_runtime_dependency(%q<nokogiri>, [">= 1.4.2"])
|
68
|
+
s.add_runtime_dependency(%q<uuidtools>, [">= 2.1.1"])
|
69
|
+
else
|
70
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
71
|
+
s.add_dependency(%q<typhoeus>, [">= 0.1.27"])
|
72
|
+
s.add_dependency(%q<nokogiri>, [">= 1.4.2"])
|
73
|
+
s.add_dependency(%q<uuidtools>, [">= 2.1.1"])
|
74
|
+
end
|
75
|
+
else
|
76
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
77
|
+
s.add_dependency(%q<typhoeus>, [">= 0.1.27"])
|
78
|
+
s.add_dependency(%q<nokogiri>, [">= 1.4.2"])
|
79
|
+
s.add_dependency(%q<uuidtools>, [">= 2.1.1"])
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
data/spec/client_spec.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe SimplyDB::Client do
|
4
|
+
before do
|
5
|
+
@client = SimplyDB::Client.new(
|
6
|
+
:access_key => access_key,
|
7
|
+
:secret_key => secret_key
|
8
|
+
)
|
9
|
+
@params = {
|
10
|
+
'Action' => 'PutAttributes',
|
11
|
+
'DomainName' => 'MyDomain',
|
12
|
+
'ItemName' => 'Item123',
|
13
|
+
'Attribute.1.Name' => 'Color',
|
14
|
+
'Attribute.1.Value' => 'Blue',
|
15
|
+
'Attribute.2.Name' => 'Size',
|
16
|
+
'Attribute.2.Value' => 'Med',
|
17
|
+
'Attribute.3.Name' => 'Price',
|
18
|
+
'Attribute.3.Value' => '0014.99',
|
19
|
+
'Version' => '2009-04-15',
|
20
|
+
'Timestamp' => "2010-01-25T15:01:28-07:00",
|
21
|
+
'SignatureVersion' => 2,
|
22
|
+
'SignatureMethod' => 'HmacSHA256',
|
23
|
+
'AWSAccessKeyId' => access_key
|
24
|
+
}
|
25
|
+
@query_string = "AWSAccessKeyId=#{access_key}&Action=PutAttributes&Attribute.1.Name=Color&Attribute.1.Value=Blue&Attribute.2.Name=Size&Attribute.2.Value=Med&Attribute.3.Name=Price&Attribute.3.Value=0014.99&DomainName=MyDomain&ItemName=Item123&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2010-01-25T15%3A01%3A28-07%3A00&Version=2009-04-15"
|
26
|
+
@string_to_sign = "GET\nsdb.amazonaws.com\n/\n#{@query_string}"
|
27
|
+
@post_body = '<PutAttributesResponse><ResponseMetadata><StatusCode>Success</StatusCode><RequestId>f6820318-9658-4a9d-89f8-b067c90904fc</RequestId><BoxUsage>0.0000219907</BoxUsage></ResponseMetadata></PutAttributesResponse>'
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should generate full URL" do
|
31
|
+
@client.base_url.should == "https://sdb.amazonaws.com:443/"
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should create the signed string for a POST" do
|
35
|
+
#ie: http://docs.amazonwebservices.com/AmazonSimpleDB/latest/DeveloperGuide/REST_RESTAuth.html
|
36
|
+
@client.string_to_sign(:get, @params).should == @string_to_sign
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should generate a correct signature" do
|
40
|
+
@client.generate_signature(:get, @params).should == Base64.encode64(
|
41
|
+
OpenSSL::HMAC.digest(
|
42
|
+
OpenSSL::Digest::Digest.new('sha256'),
|
43
|
+
secret_key,
|
44
|
+
@string_to_sign
|
45
|
+
)
|
46
|
+
).chomp
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should be able to make an HTTP request" do
|
50
|
+
@client.http_client.hydra.stub(:post, /http/).and_return(Typhoeus::Response.new(:body=>'This is a test.'))
|
51
|
+
@client.call(:post, {:url=>'http://www.example.com'}) do |body|
|
52
|
+
body.should == 'This is a test.'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/spec/error_spec.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe SimplyDB::Error do
|
4
|
+
it "should contain SignatureDoesNotMatch" do
|
5
|
+
lambda {
|
6
|
+
raise SimplyDB::Error::SignatureDoesNotMatch
|
7
|
+
}.should raise_exception(SimplyDB::Error::SignatureDoesNotMatch)
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
require 'simplydb/extensions'
|
4
|
+
|
5
|
+
describe SimplyDB::Extensions do
|
6
|
+
include SimplyDB::Extensions
|
7
|
+
|
8
|
+
it "should underscore a string" do
|
9
|
+
underscore("MyDomain").should == 'my_domain'
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should correctly escape a string for URI encoding" do
|
13
|
+
escape_value("select color from MyDomain where color = 'blue'").should == 'select%20color%20from%20MyDomain%20where%20color%20%3D%20%27blue%27'
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,223 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
require 'typhoeus'
|
4
|
+
|
5
|
+
describe SimplyDB::Interface do
|
6
|
+
before do
|
7
|
+
@interface = SimplyDB::Interface.new(
|
8
|
+
:access_key => access_key,
|
9
|
+
:secret_key => secret_key
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
def set_response_body(body)
|
14
|
+
@interface.client.http_client.hydra.stub(:post, /http/).and_return(Typhoeus::Response.new(:body=>body))
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "#define_attributes" do
|
18
|
+
it "should define attributes" do
|
19
|
+
@interface.send(:define_attributes, {'color'=>'red','size'=>'medium'}).should == {
|
20
|
+
'Attribute.0.Name' => 'color',
|
21
|
+
'Attribute.0.Value' => 'red',
|
22
|
+
'Attribute.1.Name' => 'size',
|
23
|
+
'Attribute.1.Value' => 'medium'
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should define attributes with arrays" do
|
28
|
+
@interface.send(:define_attributes, {'color'=>['red','brick','garnet']}).should == {
|
29
|
+
'Attribute.0.Name' => 'color',
|
30
|
+
'Attribute.0.Value' => 'red',
|
31
|
+
'Attribute.1.Name' => 'color',
|
32
|
+
'Attribute.1.Value' => 'brick',
|
33
|
+
'Attribute.2.Name' => 'color',
|
34
|
+
'Attribute.2.Value' => 'garnet'
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should define attributes to be replaced" do
|
39
|
+
@interface.send(:define_attributes, {'color'=>'red'}, {}, true).should == {
|
40
|
+
'Attribute.0.Name' => 'color',
|
41
|
+
'Attribute.0.Value' => 'red',
|
42
|
+
'Attribute.0.Replace' => '1',
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should define expected states of attributes" do
|
47
|
+
@interface.send(:define_attributes, {}, {'quantity' => '0'}).should == {
|
48
|
+
'Expected.0.Name' => 'quantity',
|
49
|
+
'Expected.0.Value' => '0'
|
50
|
+
}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "successful API calls" do
|
55
|
+
|
56
|
+
it "should set stats for a request" do
|
57
|
+
set_response_body '<DeleteDomainResponse><ResponseMetadata><RequestId>c522638b-31a2-4d69-b376-8c5428744704</RequestId><BoxUsage>0.0000219907</BoxUsage></ResponseMetadata></DeleteDomainResponse>'
|
58
|
+
@interface.delete_domain('MyDomain')
|
59
|
+
@interface.request_id.should == 'c522638b-31a2-4d69-b376-8c5428744704'
|
60
|
+
@interface.box_usage.should == 0.0000219907
|
61
|
+
end
|
62
|
+
|
63
|
+
# http://docs.amazonwebservices.com/AmazonSimpleDB/latest/DeveloperGuide/SDB_API_Operations.html
|
64
|
+
describe "for attribute actions" do
|
65
|
+
describe "#get_attributes" do
|
66
|
+
it "should get attributes from an item" do
|
67
|
+
set_response_body '<GetAttributesResponse><GetAttributesResult><Attribute><Name>Color</Name><Value>Blue</Value></Attribute><Attribute><Name>Size</Name><Value>Med</Value></Attribute><Attribute><Name>Price</Name><Value>14</Value></Attribute></GetAttributesResult><ResponseMetadata><RequestId>b1e8f1f7-42e9-494c-ad09-2674e557526d</RequestId><BoxUsage>0.0000219907</BoxUsage></ResponseMetadata></GetAttributesResponse>'
|
68
|
+
@interface.get_attributes('MyDomain', 'Item123') do |attributes|
|
69
|
+
attributes.should == {
|
70
|
+
'Color' => 'Blue',
|
71
|
+
'Size' => 'Med',
|
72
|
+
'Price' => '14'
|
73
|
+
}
|
74
|
+
end
|
75
|
+
@interface.get_attributes('MyDomain', 'Item123').should == {
|
76
|
+
'Color' => 'Blue',
|
77
|
+
'Size' => 'Med',
|
78
|
+
'Price' => '14'
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should multiple values for an attribute" do
|
83
|
+
set_response_body '<GetAttributesResponse><GetAttributesResult><Attribute><Name>Color</Name><Value>Blue</Value></Attribute><Attribute><Name>Color</Name><Value>Red</Value></Attribute><Attribute><Name>Size</Name><Value>Med</Value></Attribute><Attribute><Name>Price</Name><Value>14</Value></Attribute></GetAttributesResult><ResponseMetadata><RequestId>b1e8f1f7-42e9-494c-ad09-2674e557526d</RequestId><BoxUsage>0.0000219907</BoxUsage></ResponseMetadata></GetAttributesResponse>'
|
84
|
+
@interface.get_attributes('MyDomain', 'Item123') do |attributes|
|
85
|
+
attributes.should == {
|
86
|
+
'Color' => ['Blue','Red'],
|
87
|
+
'Size' => 'Med',
|
88
|
+
'Price' => '14'
|
89
|
+
}
|
90
|
+
end
|
91
|
+
@interface.get_attributes('MyDomain', 'Item123').should == {
|
92
|
+
'Color' => ['Blue', 'Red'],
|
93
|
+
'Size' => 'Med',
|
94
|
+
'Price' => '14'
|
95
|
+
}
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should only get specific attributes" do
|
99
|
+
set_response_body '<GetAttributesResponse><GetAttributesResult><Attribute><Name>Color</Name><Value>Blue</Value></Attribute><Attribute><Name>Size</Name><Value>Med</Value></GetAttributesResult><ResponseMetadata><RequestId>b1e8f1f7-42e9-494c-ad09-2674e557526d</RequestId><BoxUsage>0.0000219907</BoxUsage></ResponseMetadata></GetAttributesResponse>'
|
100
|
+
@interface.get_attributes('MyDomain', 'Item123', ['Color','Size']) do |attributes|
|
101
|
+
attributes.should == {
|
102
|
+
'Color' => 'Blue',
|
103
|
+
'Size' => 'Med'
|
104
|
+
}
|
105
|
+
end
|
106
|
+
@interface.get_attributes('MyDomain', 'Item123', ['Color','Size']).should == {
|
107
|
+
'Color' => 'Blue',
|
108
|
+
'Size' => 'Med'
|
109
|
+
}
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should set a batch of attributes for items" do |variable|
|
114
|
+
set_response_body '<BatchPutAttributesResponse><ResponseMetadata><RequestId>490206ce-8292-456c-a00f-61b335eb202b</RequestId><BoxUsage>0.0000219907</BoxUsage></ResponseMetadata></BatchPutAttributesResponse>'
|
115
|
+
@interface.batch_put_attributes('MyDomain',{
|
116
|
+
'Shirt1' => {
|
117
|
+
'Color' => 'Blue',
|
118
|
+
'Size' => 'Med',
|
119
|
+
'Price' => '0014.99'
|
120
|
+
},
|
121
|
+
'Shirt2' => {
|
122
|
+
'Color' => 'Red',
|
123
|
+
'Size' => 'Large',
|
124
|
+
'Price' => '0019.99'
|
125
|
+
}
|
126
|
+
}) do |success|
|
127
|
+
success.should == true
|
128
|
+
end
|
129
|
+
@interface.batch_put_attributes('MyDomain',{
|
130
|
+
'Shirt1' => {
|
131
|
+
'Color' => 'Blue',
|
132
|
+
'Size' => 'Med',
|
133
|
+
'Price' => '0014.99'
|
134
|
+
},
|
135
|
+
'Shirt2' => {
|
136
|
+
'Color' => 'Red',
|
137
|
+
'Size' => 'Large',
|
138
|
+
'Price' => '0019.99'
|
139
|
+
}
|
140
|
+
}).should == true
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should be able to perform a select query" do
|
144
|
+
set_response_body '<SelectResponse><SelectResult><Item><Name>Item_03</Name><Attribute><Name>Category</Name><Value>Clothes</Value></Attribute><Attribute><Name>Subcategory</Name><Value>Pants</Value></Attribute><Attribute><Name>Name</Name><Value>Sweatpants</Value></Attribute><Attribute><Name>Color</Name><Value>Blue</Value></Attribute><Attribute><Name>Color</Name><Value>Yellow</Value></Attribute><Attribute><Name>Color</Name><Value>Pink</Value></Attribute><Attribute><Name>Size</Name><Value>Large</Value></Attribute></Item><Item><Name>Item_06</Name><Attribute><Name>Category</Name><Value>Motorcycle Parts</Value></Attribute><Attribute><Name>Subcategory</Name><Value>Bodywork</Value></Attribute><Attribute><Name>Name</Name><Value>Fender Eliminator</Value></Attribute><Attribute><Name>Color</Name><Value>Blue</Value></Attribute><Attribute><Name>Make</Name><Value>Yamaha</Value></Attribute><Attribute><Name>Model</Name><Value>R1</Value></Attribute></Item></SelectResult><ResponseMetadata><RequestId>b1e8f1f7-42e9-494c-ad09-2674e557526d</RequestId><BoxUsage>0.0000219907</BoxUsage></ResponseMetadata></SelectResponse>'
|
145
|
+
@interface.select("select Color from MyDomain where Color like 'Blue%'") do |items|
|
146
|
+
items.should == {
|
147
|
+
'Item_03' => {
|
148
|
+
'Category' => 'Clothes',
|
149
|
+
'Subcategory' => 'Pants',
|
150
|
+
'Name' => 'Sweatpants',
|
151
|
+
'Color' => ['Blue','Yellow','Pink'],
|
152
|
+
'Size' => 'Large'
|
153
|
+
},
|
154
|
+
'Item_06' => {
|
155
|
+
'Category' => 'Motorcycle Parts',
|
156
|
+
'Subcategory' => 'Bodywork',
|
157
|
+
'Name' => 'Fender Eliminator',
|
158
|
+
'Color' => 'Blue',
|
159
|
+
'Make' => 'Yamaha',
|
160
|
+
'Model' => 'R1'
|
161
|
+
}
|
162
|
+
}
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
it "should delete attributes from an item" do
|
167
|
+
set_response_body '<DeleteAttributesResponse><ResponseMetadata><RequestId>05ae667c-cfac-41a8-ab37-a9c897c4c3ca</RequestId><BoxUsage>0.0000219907</BoxUsage></ResponseMetadata></DeleteAttributesResponse>'
|
168
|
+
@interface.delete_attributes('MyDomain', 'JumboFez', {'color'=>['red','brick','garnet']}) do |success|
|
169
|
+
success.should == true
|
170
|
+
end
|
171
|
+
@interface.delete_attributes('MyDomain', 'JumboFez', {'color'=>['red','brick','garnet']}).should == true
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should be able to put attribues on an item" do
|
175
|
+
set_response_body '<PutAttributesResponse><ResponseMetadata><RequestId>490206ce-8292-456c-a00f-61b335eb202b</RequestId><BoxUsage>0.0000219907</BoxUsage></ResponseMetadata></PutAttributesResponse>'
|
176
|
+
@interface.put_attributes('MyDomain', 'Item123', {'color'=>['red','brick','garnet']}) do |success|
|
177
|
+
success.should == true
|
178
|
+
end
|
179
|
+
@interface.put_attributes('MyDomain', 'Item123', {'color'=>['red','brick','garnet']}).should == true
|
180
|
+
end
|
181
|
+
end
|
182
|
+
describe "for domain actions" do
|
183
|
+
it "should be able to create a domain" do
|
184
|
+
set_response_body '<CreateDomainResponse><ResponseMetadata><RequestId>2a1305a2-ed1c-43fc-b7c4-e6966b5e2727</RequestId><BoxUsage>0.0000219907</BoxUsage></ResponseMetadata></CreateDomainResponse>'
|
185
|
+
@interface.create_domain('MyDomain') do |success|
|
186
|
+
success.should == true
|
187
|
+
end
|
188
|
+
@interface.create_domain('MyDomain').should == true
|
189
|
+
end
|
190
|
+
|
191
|
+
it "should be able to delete a domain" do
|
192
|
+
set_response_body '<DeleteDomainResponse><ResponseMetadata><RequestId>c522638b-31a2-4d69-b376-8c5428744704</RequestId><BoxUsage>0.0000219907</BoxUsage></ResponseMetadata></DeleteDomainResponse>'
|
193
|
+
@interface.delete_domain('MyDomain') do |success|
|
194
|
+
success.should == true
|
195
|
+
end
|
196
|
+
@interface.delete_domain('MyDomain').should == true
|
197
|
+
end
|
198
|
+
|
199
|
+
it "should be able to list domains" do
|
200
|
+
set_response_body '<ListDomainsResponse><ListDomainsResult><DomainName>Domain1-200706011651</DomainName><DomainName>Domain2-200706011652</DomainName><NextToken>TWV0ZXJpbmdUZXN0RG9tYWluMS0yMDA3MDYwMTE2NTY=</NextToken></ListDomainsResult><ResponseMetadata><RequestId>eb13162f-1b95-4511-8b12-489b86acfd28</RequestId><BoxUsage>0.0000219907</BoxUsage></ResponseMetadata></ListDomainsResponse>'
|
201
|
+
@interface.list_domains do |domains|
|
202
|
+
domains.should == ['Domain1-200706011651','Domain2-200706011652']
|
203
|
+
end
|
204
|
+
@interface.list_domains.should == ['Domain1-200706011651','Domain2-200706011652']
|
205
|
+
end
|
206
|
+
|
207
|
+
it "should be able to get the metadeta for a domain" do
|
208
|
+
set_response_body '<DomainMetadataResponse xmlns="http://sdb.amazonaws.com/doc/2009-04-15/"><DomainMetadataResult><ItemCount>195078</ItemCount><ItemNamesSizeBytes>2586634</ItemNamesSizeBytes><AttributeNameCount >12</AttributeNameCount ><AttributeNamesSizeBytes>120</AttributeNamesSizeBytes><AttributeValueCount>3690416</AttributeValueCount><AttributeValuesSizeBytes>50149756</AttributeValuesSizeBytes><Timestamp>1225486466</Timestamp></DomainMetadataResult><ResponseMetadata><RequestId>b1e8f1f7-42e9-494c-ad09-2674e557526d</RequestId><BoxUsage>0.0000219907</BoxUsage></ResponseMetadata></DomainMetadataResponse>'
|
209
|
+
@interface.domain_metadata('Test') do |attributes|
|
210
|
+
attributes.should == {
|
211
|
+
'ItemCount' => '195078',
|
212
|
+
'ItemNamesSizeBytes' => '2586634',
|
213
|
+
'AttributeNameCount' => '12',
|
214
|
+
'AttributeNamesSizeBytes' => '120',
|
215
|
+
'AttributeValueCount' => '3690416',
|
216
|
+
'AttributeValuesSizeBytes' => '50149756',
|
217
|
+
'Timestamp' => '1225486466'
|
218
|
+
}
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|