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.
@@ -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
@@ -0,0 +1,5 @@
1
+ require 'rubygems'
2
+ require 'simplydb/extensions'
3
+ require 'simplydb/error'
4
+ require 'simplydb/client'
5
+ require 'simplydb/interface'
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
+
@@ -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
@@ -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