simplydb 0.0.2 → 0.0.3

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/.gitignore CHANGED
@@ -1,21 +1,5 @@
1
- ## MAC OS
2
- .DS_Store
3
-
4
- ## TEXTMATE
5
- *.tmproj
6
- tmtags
7
-
8
- ## EMACS
9
- *~
10
- \#*
11
- .\#*
12
-
13
- ## VIM
14
- *.swp
15
-
16
- ## PROJECT::GENERAL
17
- coverage
18
- rdoc
19
- pkg
20
-
21
- ## PROJECT::SPECIFIC
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ Gemfile.lock
5
+ .idea/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in simplydb.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # simplydb
2
+
3
+ A minimal interface to Amazon SimpleDB that has separation of interfaces. Build to support a Sinatra app that can be used as a JSON <-> SimpleDB proxy.
4
+
5
+ require 'rubygems'
6
+ require 'simplydb'
7
+
8
+ interface = SimplyDB::Interface.new({
9
+ :access_key => ENV['AWS_ACCESS_KEY'],
10
+ :secret_key => ENV['AWS_SECRET_KEY']
11
+ })
12
+
13
+ if interface.create_domain("MyDomain")
14
+ interface.put_attributes('MyDomain', 'Item123', {'color'=>['red','brick','garnet']})
15
+
16
+ attributes = interface.get_attributes('MyDomain', 'Item123')
17
+ puts "Item123 = #{attributes.inspect}"
18
+
19
+ items = interface.select("select color from MyDomain where color = 'brick'")
20
+ puts "Items = #{items.inspect}"
21
+
22
+ interface.delete_domain("MyDomain")
23
+ end
24
+
25
+ ## Note on Patches/Pull Requests
26
+
27
+ * Fork the project.
28
+ * Make your feature addition or bug fix.
29
+ * Add tests for it. This is important so I don't break it in a
30
+ future version unintentionally.
31
+ * Commit, do not mess with rakefile, version, or history.
32
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
33
+ * Send me a pull request. Bonus points for topic branches.
34
+
35
+ ## Copyright
36
+
37
+ Copyright (c) 2010 JT Archie. See LICENSE for details.
data/Rakefile CHANGED
@@ -1,54 +1,14 @@
1
- require 'rubygems'
2
- require 'rake'
1
+ require 'bundler'
2
+ require 'rspec/core/rake_task'
3
3
 
4
- begin
5
- require 'jeweler'
6
- Jeweler::Tasks.new do |gem|
7
- gem.name = "simplydb"
8
- gem.summary = %Q{A minimal interface to Amazon SimpleDB.}
9
- gem.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.}
10
- gem.email = "jtarchie@gmail.com"
11
- gem.homepage = "http://github.com/jtarchie/simplydb"
12
- gem.authors = ["JT Archie"]
13
- gem.add_development_dependency "rspec", ">= 1.2.9"
14
- gem.add_dependency "typhoeus", ">= 0.1.27"
15
- gem.add_dependency "nokogiri", ">= 1.4.2"
16
- gem.add_dependency "uuidtools", ">= 2.1.1"
17
- # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
18
- end
19
- Jeweler::GemcutterTasks.new
20
- rescue LoadError
21
- puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
22
- end
23
-
24
- require 'spec/rake/spectask'
25
- Spec::Rake::SpecTask.new(:spec) do |spec|
26
- spec.libs << 'lib' << 'spec'
27
- spec.spec_files = FileList['spec/**/*_spec.rb']
28
- end
29
-
30
- Spec::Rake::SpecTask.new(:rcov) do |spec|
31
- spec.libs << 'lib' << 'spec'
32
- spec.pattern = 'spec/**/*_spec.rb'
33
- spec.rcov = true
34
- end
4
+ Bundler::GemHelper.install_tasks
35
5
 
36
- task :spec => :check_dependencies
37
-
38
- task :default => :spec
39
-
40
- desc "irb loaded with SimplyDB (rubygems not required)"
41
- task :irb do
42
- exec("irb -I lib/ -r 'simplydb' -r 'simplydb/record/base'")
6
+ desc "Run specs"
7
+ task :spec do
8
+ RSpec::Core::RakeTask.new(:spec) do |t|
9
+ t.rspec_opts = %w{--colour --format progress}
10
+ t.pattern = 'spec/*_spec.rb'
11
+ end
43
12
  end
44
- task :console => :irb
45
13
 
46
- require 'rake/rdoctask'
47
- Rake::RDocTask.new do |rdoc|
48
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
49
-
50
- rdoc.rdoc_dir = 'rdoc'
51
- rdoc.title = "simplydb #{version}"
52
- rdoc.rdoc_files.include('README*')
53
- rdoc.rdoc_files.include('lib/**/*.rb')
54
- end
14
+ task :default => :spec
data/lib/simplydb.rb CHANGED
@@ -1,5 +1,4 @@
1
- require 'rubygems'
2
- require 'simplydb/extensions'
3
1
  require 'simplydb/error'
2
+ require 'simplydb/interface'
4
3
  require 'simplydb/client'
5
- require 'simplydb/interface'
4
+ require 'simplydb/server'
@@ -1,66 +1,79 @@
1
1
  require 'openssl'
2
2
  require 'digest/sha1'
3
3
  require 'base64'
4
- require 'uri'
5
- require 'simplydb/clients/typhoeus'
4
+ require 'rest_client'
6
5
 
7
6
  module SimplyDB
8
7
  class Client
9
- include SimplyDB::Extensions
10
-
11
- attr_accessor :options, :http_client
12
-
13
- def initialize(options = {})
14
- @options = {
15
- :async => false,
16
- :protocol => 'https://',
17
- :host => 'sdb.amazonaws.com',
18
- :port => 443,
19
- :path => "/",
20
- :signature_version => '2',
21
- :version => '2009-04-15',
22
- :signature_method => 'HmacSHA256',
23
- }.merge(options)
24
- @http_client = (@options[:client] || SimplyDB::Clients::Typhoeus).new(options)
25
- end
26
-
27
- def async?
28
- return options[:async] == true
29
- end
30
-
31
- def base_url
32
- "#{@options[:protocol]}#{@options[:host]}:#{@options[:port]}#{@options[:path]}"
33
- end
34
-
35
- def string_to_sign(method, params)
36
- return "#{method.to_s.upcase}\n#{options[:host]}\n/\n" + escape_hash(params)
37
- end
38
-
39
- def generate_signature(method, params)
40
- Base64.encode64(
41
- OpenSSL::HMAC.digest(
42
- OpenSSL::Digest::Digest.new('sha256'),
43
- @options[:secret_key],
44
- string_to_sign(method, params)
45
- )
46
- ).chomp
47
- end
48
-
49
- def params_with_signature(method, params)
50
- params.merge!({
51
- 'AWSAccessKeyId' => @options[:access_key],
52
- 'SignatureVersion' => @options[:signature_version],
53
- 'Timestamp' => Time.now.iso8601,
54
- 'Version' => @options[:version],
55
- 'SignatureMethod' => @options[:signature_method]
56
- })
57
- params['Signature'] = generate_signature(method, params)
58
- return params
59
- end
60
-
61
- def call(method, params ={}, &on_complete)
62
- options = {:method => method, :url => base_url, :params => params_with_signature(method, params)}
63
- @http_client.request(options, !async?) {|request| on_complete.call(request.body)}
8
+ attr_accessor :options, :http_client
9
+
10
+ def initialize(options = {})
11
+ @options = {
12
+ :protocol => 'https://',
13
+ :host => 'sdb.amazonaws.com',
14
+ :port => 443,
15
+ :path => "/",
16
+ :signature_version => '2',
17
+ :version => '2009-04-15',
18
+ :signature_method => 'HmacSHA256',
19
+ }.merge(options)
20
+ end
21
+
22
+ def base_url
23
+ "#{@options[:protocol]}#{@options[:host]}:#{@options[:port]}#{@options[:path]}"
24
+ end
25
+
26
+ def string_to_sign(method, params)
27
+ return "#{method.to_s.upcase}\n#{options[:host]}\n/\n" + escape_hash(params)
28
+ end
29
+
30
+ def generate_signature(method, params)
31
+ Base64.encode64(
32
+ OpenSSL::HMAC.digest(
33
+ OpenSSL::Digest::Digest.new('sha256'),
34
+ @options[:secret_key],
35
+ string_to_sign(method, params)
36
+ )
37
+ ).chomp
38
+ end
39
+
40
+ def params_with_signature(method, params)
41
+ params.merge!({
42
+ 'AWSAccessKeyId' => @options[:access_key],
43
+ 'SignatureVersion' => @options[:signature_version],
44
+ 'Timestamp' => Time.now.iso8601,
45
+ 'Version' => @options[:version],
46
+ 'SignatureMethod' => @options[:signature_method]
47
+ })
48
+ params['Signature'] = generate_signature(method, params)
49
+ params
50
+ end
51
+
52
+ def call(method, params, &block)
53
+ params = params_with_signature(method, params)
54
+ response = case method.to_sym
55
+ # when :get
56
+ # RestClient.get(base_url << "?#{escape_hash(params)}")
57
+ when :post
58
+ RestClient.post(base_url, params)
59
+ else
60
+ raise "Not support request method #{method}"
61
+ end
62
+ block.call(response.body)
63
+ rescue RestClient::BadRequest => e
64
+ block.call(e.response.body)
65
+ end
66
+
67
+ private
68
+
69
+ def escape_value(string)
70
+ string.gsub(/([^ a-zA-Z0-9_.-]+)/n) do
71
+ '%' + $1.unpack('H2' * $1.size).join('%').upcase
72
+ end.gsub(' ', '%20')
73
+ end
74
+
75
+ def escape_hash(params = {})
76
+ return params.collect{|k,v| [k.to_s, v.to_s]}.sort.collect { |key, value| [escape_value(key), escape_value(value)].join('=') }.join('&')
77
+ end
64
78
  end
65
- end
66
79
  end
@@ -1,60 +1,65 @@
1
1
  module SimplyDB
2
- module Error
3
- # ref http://docs.amazonwebservices.com/AmazonSimpleDB/latest/DeveloperGuide/APIError.html
4
- class AccessFailure < RuntimeError; end
5
- class AttributeDoesNotExist < RuntimeError; end
6
- class AuthFailure < RuntimeError; end
7
- class AuthMissingFailure < RuntimeError; end
8
- class ConditionalCheckFailed < RuntimeError; end
9
- class ExistsAndExpectedValue < RuntimeError; end
10
- class FeatureDeprecated < RuntimeError; end
11
- class IncompleteExpectedExpression < RuntimeError; end
12
- class InternalError < RuntimeError; end
13
- class InvalidAction < RuntimeError; end
14
- class InvalidHTTPAuthHeader < RuntimeError; end
15
- class InvalidHttpRequest < RuntimeError; end
16
- class InvalidLiteral < RuntimeError; end
17
- class InvalidNextToken < RuntimeError; end
18
- class InvalidNumberPredicates < RuntimeError; end
19
- class InvalidNumberValueTests < RuntimeError; end
20
- class InvalidParameterCombination < RuntimeError; end
21
- class InvalidParameterValue < RuntimeError; end
22
- class InvalidQueryExpression < RuntimeError; end
23
- class InvalidResponseGroups < RuntimeError; end
24
- class InvalidService < RuntimeError; end
25
- class InvalidSOAPRequest < RuntimeError; end
26
- class InvalidSortExpression < RuntimeError; end
27
- class InvalidURI < RuntimeError; end
28
- class InvalidWSAddressingProperty < RuntimeError; end
29
- class InvalidWSDLVersion < RuntimeError; end
30
- class MalformedSOAPSignature < RuntimeError; end
31
- class MissingAction < RuntimeError; end
32
- class MissingParameter < RuntimeError; end
33
- class MissingWSAddressingProperty < RuntimeError; end
34
- class MultipleExistsConditions < RuntimeError; end
35
- class MultipleExpectedNames < RuntimeError; end
36
- class MultipleExpectedValues < RuntimeError; end
37
- class MultiValuedAttribute < RuntimeError; end
38
- class NoSuchDomain < RuntimeError; end
39
- class NoSuchVersion < RuntimeError; end
40
- class NotYetImplemented < RuntimeError; end
41
- class NumberDomainsExceeded < RuntimeError; end
42
- class NumberDomainAttributesExceeded < RuntimeError; end
43
- class NumberDomainBytesExceeded < RuntimeError; end
44
- class NumberItemAttributesExceeded < RuntimeError; end
45
- class NumberSubmittedAttributesExceeded < RuntimeError; end
46
- class NumberSubmittedItemsExceeded < RuntimeError; end
47
- class RequestExpired < RuntimeError; end
48
- class RequestTimeout < RuntimeError; end
49
- class ServiceUnavailable < RuntimeError; end
50
- class TooManyRequestedAttributes < RuntimeError; end
51
- class UnsupportedHttpVerb < RuntimeError; end
52
- class UnsupportedNextToken < RuntimeError; end
53
- class URITooLong < RuntimeError; end
54
-
55
- #Standard AWS errors
56
- class SignatureDoesNotMatch < RuntimeError; end
57
- class InvalidClientTokenId < RuntimeError; end
58
- class InvalidRequest < RuntimeError; end
2
+ class Error < RuntimeError
3
+ attr_accessor :http_status_code, :name
4
+ def initialize(name, http_status_code)
5
+ @name = name
6
+ @http_status_code = http_status_code
7
+ end
59
8
  end
9
+
10
+ AccessFailure = Error.new("AccessFailure",403)
11
+ AttributeDoesNotExist = Error.new("AttributeDoesNotExist",404)
12
+ AuthFailure = Error.new("AuthFailure",403)
13
+ AuthMissingFailure = Error.new("AuthMissingFailure",403)
14
+ ConditionalCheckFailed = Error.new("ConditionalCheckFailed",409)
15
+ ExistsAndExpectedValue = Error.new("ExistsAndExpectedValue",400)
16
+ FeatureDeprecated = Error.new("FeatureDeprecated",400)
17
+ IncompleteExpectedExpression = Error.new("IncompleteExpectedExpression",400)
18
+ InternalError = Error.new("InternalError",500)
19
+ InvalidAction = Error.new("InvalidAction",400)
20
+ InvalidHTTPAuthHeader = Error.new("InvalidHTTPAuthHeader",400)
21
+ InvalidHttpRequest = Error.new("InvalidHttpRequest",400)
22
+ InvalidLiteral = Error.new("InvalidLiteral",400)
23
+ InvalidNextToken = Error.new("InvalidNextToken",400)
24
+ InvalidNumberPredicates = Error.new("InvalidNumberPredicates",400)
25
+ InvalidNumberValueTests = Error.new("InvalidNumberValueTests",400)
26
+ InvalidParameterCombination = Error.new("InvalidParameterCombination",400)
27
+ InvalidParameterValue = Error.new("InvalidParameterValue",400)
28
+ InvalidQueryExpression = Error.new("InvalidQueryExpression",400)
29
+ InvalidResponseGroups = Error.new("InvalidResponseGroups",400)
30
+ InvalidService = Error.new("InvalidService",400)
31
+ InvalidSOAPRequest = Error.new("InvalidSOAPRequest",400)
32
+ InvalidSortExpression = Error.new("InvalidSortExpression",400)
33
+ InvalidURI = Error.new("InvalidURI",400)
34
+ InvalidWSAddressingProperty = Error.new("InvalidWSAddressingProperty",400)
35
+ InvalidWSDLVersion = Error.new("InvalidWSDLVersion",400)
36
+ MalformedSOAPSignature = Error.new("MalformedSOAPSignature",403)
37
+ MissingAction = Error.new("MissingAction",400)
38
+ MissingParameter = Error.new("MissingParameter",400)
39
+ MissingWSAddressingProperty = Error.new("MissingWSAddressingProperty",400)
40
+ MultipleExistsConditions = Error.new("MultipleExistsConditions",400)
41
+ MultipleExpectedNames = Error.new("MultipleExpectedNames",400)
42
+ MultipleExpectedValues = Error.new("MultipleExpectedValues",400)
43
+ MultiValuedAttribute = Error.new("MultiValuedAttribute",409)
44
+ NoSuchDomain = Error.new("NoSuchDomain",400)
45
+ NoSuchVersion = Error.new("NoSuchVersion",400)
46
+ NotYetImplemented = Error.new("NotYetImplemented",401)
47
+ NumberDomainsExceeded = Error.new("NumberDomainsExceeded",409)
48
+ NumberDomainAttributesExceeded = Error.new("NumberDomainAttributesExceeded",409)
49
+ NumberDomainBytesExceeded = Error.new("NumberDomainBytesExceeded",409)
50
+ NumberItemAttributesExceeded = Error.new("NumberItemAttributesExceeded",409)
51
+ NumberSubmittedAttributesExceeded = Error.new("NumberSubmittedAttributesExceeded",409)
52
+ NumberSubmittedItemsExceeded = Error.new("NumberSubmittedItemsExceeded",409)
53
+ RequestExpired = Error.new("RequestExpired",400)
54
+ RequestTimeout = Error.new("RequestTimeout",408)
55
+ ServiceUnavailable = Error.new("ServiceUnavailable",503)
56
+ TooManyRequestedAttributes = Error.new("TooManyRequestedAttributes",400)
57
+ UnsupportedHttpVerb = Error.new("UnsupportedHttpVerb",400)
58
+ UnsupportedNextToken = Error.new("UnsupportedNextToken",400)
59
+ URITooLong = Error.new("URITooLong",400)
60
+
61
+ #Standard AWS errors
62
+ SignatureDoesNotMatch = Error.new("SignatureDoesNotMatch",400)
63
+ InvalidClientTokenId = Error.new("InvalidClientTokenId",400)
64
+ InvalidRequest = Error.new("InvalidRequest",400)
60
65
  end
@@ -7,43 +7,35 @@ module SimplyDB
7
7
  @options = {}.merge(options)
8
8
  @client = SimplyDB::Client.new(options)
9
9
  end
10
-
11
- def create_domain(name, &block)
10
+
11
+ def create_domain(name)
12
12
  call({'Action' => 'CreateDomain', 'DomainName' => name}) do |doc|
13
- ret_val = doc.css("CreateDomainResponse").length > 0
14
- block.call(ret_val) if block_given?
15
- ret_val
13
+ doc.css("CreateDomainResponse").length > 0
16
14
  end
17
15
  end
18
-
19
- def delete_domain(name, &block)
16
+
17
+ def delete_domain(name)
20
18
  call({'Action' => 'DeleteDomain', 'DomainName' => name}) do |doc|
21
- ret_val = doc.css("DeleteDomainResponse").length > 0
22
- block.call(ret_val) if block_given?
23
- ret_val
19
+ doc.css("DeleteDomainResponse").length > 0
24
20
  end
25
21
  end
26
-
27
- def list_domains(&block)
22
+
23
+ def list_domains
28
24
  call({'Action' => 'ListDomains'}) do |doc|
29
- domains = doc.css('DomainName').collect{|d| d.text}
30
- block.call(domains) if block_given?
31
- domains
25
+ doc.css('DomainName').collect{|d| d.text}
32
26
  end
33
27
  end
34
-
35
- def domain_metadata(name, &block)
28
+
29
+ def domain_metadata(name)
36
30
  call({'Action' => 'DomainMetadata','DomainName' => name}) do |doc|
37
- attributes = doc.css("DomainMetadataResult").first.children.inject({}) do |memo, child|
31
+ doc.css("DomainMetadataResult").first.children.inject({}) do |memo, child|
38
32
  memo[child.name] = child.text
39
33
  memo
40
34
  end
41
- block.call(attributes) if block_given?
42
- attributes
43
35
  end
44
36
  end
45
-
46
- def put_attributes(name, id, attributes = {}, expected = {}, replace = false, &block)
37
+
38
+ def put_attributes(name, id, attributes = {}, expected = {}, replace = false)
47
39
  params = define_attributes(attributes, expected, replace)
48
40
  params.merge!({
49
41
  'DomainName' => name,
@@ -51,13 +43,11 @@ module SimplyDB
51
43
  'Action' => 'PutAttributes'
52
44
  })
53
45
  call(params) do |doc|
54
- ret_val = doc.css("PutAttributesResponse").length > 0
55
- block.call(ret_val) if block_given?
56
- ret_val
46
+ doc.css("PutAttributesResponse").length > 0
57
47
  end
58
48
  end
59
-
60
- def batch_put_attributes(name, items = {}, &block)
49
+
50
+ def batch_put_attributes(name, items = {})
61
51
  params = {'DomainName' => name, 'ActionName' => 'BatchPutAttributes'}
62
52
  items.keys.each_with_index do |key, i|
63
53
  params["Item.#{i}.ItemName"] = key
@@ -72,13 +62,11 @@ module SimplyDB
72
62
  end
73
63
  end
74
64
  call(params) do |doc|
75
- ret_val = doc.css("BatchPutAttributesResponse").length > 0
76
- block.call(ret_val) if block_given?
77
- ret_val
65
+ doc.css("BatchPutAttributesResponse").length > 0
78
66
  end
79
67
  end
80
-
81
- def delete_attributes(name, id, attributes = {}, expected = {}, &block)
68
+
69
+ def delete_attributes(name, id, attributes = {}, expected = {})
82
70
  params = define_attributes(attributes, expected)
83
71
  params.merge!({
84
72
  'DomainName' => name,
@@ -86,23 +74,21 @@ module SimplyDB
86
74
  'Action' => 'DeleteAttributes'
87
75
  })
88
76
  call(params) do |doc|
89
- ret_val = doc.css("DeleteAttributesResponse").length > 0
90
- block.call(ret_val) if block_given?
91
- ret_val
77
+ doc.css("DeleteAttributesResponse").length > 0
92
78
  end
93
79
  end
94
-
95
- def get_attributes(name, id, wanted_attributes = [], consistent_read = false, &block)
80
+
81
+ def get_attributes(name, id, wanted_attributes = [], consistent_read = false)
96
82
  params = {
97
83
  'Action' => 'GetAttributes',
98
84
  'DomainName' => name,
99
85
  'ItemName' => id,
100
- 'ConsistentRead' => consistent_read.to_s,
86
+ 'ConsistentRead' => consistent_read.to_s,
101
87
  }
102
88
  wanted_attributes.each_with_index {|name, index| params["AttributeName.#{index}"] = name}
103
89
 
104
90
  call(params) do |doc|
105
- attributes = doc.css("Attribute").inject({}) do |memo, attribute|
91
+ doc.css("Attribute").inject({}) do |memo, attribute|
106
92
  name = attribute.css("Name").first.text
107
93
  value = attribute.css("Value").first.text
108
94
  if memo.has_key?(name)
@@ -113,12 +99,10 @@ module SimplyDB
113
99
  end
114
100
  memo
115
101
  end
116
- block.call(attributes) if block_given?
117
- attributes
118
102
  end
119
103
  end
120
-
121
- def select(expression, consistent_read = false, next_token = nil, &block)
104
+
105
+ def select(expression, consistent_read = false, next_token = nil)
122
106
  params = {
123
107
  'Action' => 'Select',
124
108
  'SelectExpression' => expression,
@@ -126,9 +110,9 @@ module SimplyDB
126
110
  }
127
111
  params['NextToken'] = next_token unless next_token.nil?
128
112
  call(params) do |doc|
129
- ret_val = doc.css("SelectResponse SelectResult Item").inject({}) do |items, item|
130
- item_name = item.css("Name").first.text
131
- items[item_name] = item.css("Attribute").inject({}) do |attributes, attribute|
113
+ doc.css("SelectResponse SelectResult Item").collect do |element|
114
+ item_name = element.css("Name").first.text
115
+ item = element.css("Attribute").inject({}) do |attributes, attribute|
132
116
  attribute_name = attribute.css("Name").first.text
133
117
  attribute_value = attribute.css("Value").first.text
134
118
  if attributes.has_key?(attribute_name)
@@ -139,13 +123,11 @@ module SimplyDB
139
123
  end
140
124
  attributes
141
125
  end
142
- items
126
+ {item_name => item}
143
127
  end
144
- block.call(ret_val) if block_given?
145
- ret_val
146
128
  end
147
129
  end
148
-
130
+
149
131
  private
150
132
  def define_attributes(attributes = {}, expected = {}, replace = false)
151
133
  params = {}
@@ -154,7 +136,7 @@ module SimplyDB
154
136
  v.each do |value|
155
137
  params["Attribute.#{index}.Name"] = k
156
138
  params["Attribute.#{index}.Value"] = value
157
- params["Attribute.#{index}.Replace"] = "1" if replace
139
+ params["Attribute.#{index}.Replace"] = "true" if replace
158
140
  index += 1
159
141
  end
160
142
  index
@@ -178,9 +160,9 @@ module SimplyDB
178
160
  end
179
161
  index
180
162
  }
181
- return params
163
+ params
182
164
  end
183
-
165
+
184
166
  def call(params = {}, attempts = 3, &block)
185
167
  @client.call(:post, params) do |body|
186
168
  begin
@@ -194,7 +176,7 @@ module SimplyDB
194
176
  @next_token = doc.css("NextToken").first.text unless doc.css("NextToken").empty?
195
177
  block.call(doc)
196
178
  end
197
- rescue SimplyDB::Error::ServiceUnavailable => e
179
+ rescue SimplyDB::Error::ServiceUnavailable, RestClient::ServiceUnavailable => e
198
180
  if attempts > 0
199
181
  call(params, attempts - 1, &block)
200
182
  else