caring-aws-sdb 0.3.2.caring.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2007, 2008 Tim Dysinger
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README ADDED
@@ -0,0 +1 @@
1
+ Amazon SDB API
data/Rakefile ADDED
@@ -0,0 +1,57 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "aws-sdb"
8
+ gem.summary = "Amazon SDB API"
9
+ gem.description = gem.summary
10
+ gem.email = "ddollar@gmail.com"
11
+ gem.homepage = "http://github.com/ddollar/aws-sdb"
12
+ gem.authors = ["Tim Dysinger", "David Dollar"]
13
+ gem.add_runtime_dependency "uuidtools"
14
+ gem.add_development_dependency 'rspec'
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
19
+ end
20
+
21
+ require 'rake/testtask'
22
+ Rake::TestTask.new(:test) do |test|
23
+ test.libs << 'lib' << 'test'
24
+ test.pattern = 'test/**/*_test.rb'
25
+ test.verbose = true
26
+ end
27
+
28
+ begin
29
+ require 'rcov/rcovtask'
30
+ Rcov::RcovTask.new do |test|
31
+ test.libs << 'test'
32
+ test.pattern = 'test/**/*_test.rb'
33
+ test.verbose = true
34
+ end
35
+ rescue LoadError
36
+ task :rcov do
37
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
38
+ end
39
+ end
40
+
41
+ task :test => :check_dependencies
42
+
43
+ task :default => :test
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ if File.exist?('VERSION')
48
+ version = File.read('VERSION')
49
+ else
50
+ version = ""
51
+ end
52
+
53
+ rdoc.rdoc_dir = 'rdoc'
54
+ rdoc.title = "gem-github-stats #{version}"
55
+ rdoc.rdoc_files.include('README*')
56
+ rdoc.rdoc_files.include('lib/**/*.rb')
57
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.3.1
data/aws-sdb.gemspec ADDED
@@ -0,0 +1,56 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{caring-aws-sdb}
8
+ s.version = "0.3.2.caring.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Tim Dysinger", "David Dollar"]
12
+ s.date = %q{2009-10-02}
13
+ s.description = %q{Amazon SDB API}
14
+ s.email = %q{ddollar@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README"
18
+ ]
19
+ s.files = [
20
+ "LICENSE",
21
+ "README",
22
+ "Rakefile",
23
+ "VERSION",
24
+ "aws-sdb.gemspec",
25
+ "lib/aws_sdb.rb",
26
+ "lib/aws_sdb/error.rb",
27
+ "lib/aws_sdb/service.rb",
28
+ "spec/aws_sdb/service_spec.rb",
29
+ "spec/spec_helper.rb"
30
+ ]
31
+ s.homepage = %q{http://github.com/ddollar/aws-sdb}
32
+ s.rdoc_options = ["--charset=UTF-8"]
33
+ s.require_paths = ["lib"]
34
+ s.rubygems_version = %q{1.3.5}
35
+ s.summary = %q{Amazon SDB API}
36
+ s.test_files = [
37
+ "spec/aws_sdb/service_spec.rb",
38
+ "spec/spec_helper.rb"
39
+ ]
40
+
41
+ if s.respond_to? :specification_version then
42
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
43
+ s.specification_version = 3
44
+
45
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
46
+ s.add_runtime_dependency(%q<uuidtools>, [">= 0"])
47
+ s.add_development_dependency(%q<rspec>, [">= 0"])
48
+ else
49
+ s.add_dependency(%q<uuidtools>, [">= 0"])
50
+ s.add_dependency(%q<rspec>, [">= 0"])
51
+ end
52
+ else
53
+ s.add_dependency(%q<uuidtools>, [">= 0"])
54
+ s.add_dependency(%q<rspec>, [">= 0"])
55
+ end
56
+ end
data/lib/aws_sdb.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'aws_sdb/error'
2
+ require 'aws_sdb/service'
3
+
@@ -0,0 +1,42 @@
1
+ module AwsSdb
2
+
3
+ class Error < RuntimeError ; end
4
+
5
+ class RequestError < Error
6
+ attr_reader :request_id
7
+
8
+ def initialize(message, request_id=nil)
9
+ super(message)
10
+ @request_id = request_id
11
+ end
12
+ end
13
+
14
+ class InvalidDomainNameError < RequestError ; end
15
+ class InvalidParameterValueError < RequestError ; end
16
+ class InvalidNextTokenError < RequestError ; end
17
+ class InvalidNumberPredicatesError < RequestError ; end
18
+ class InvalidNumberValueTestsError < RequestError ; end
19
+ class InvalidQueryExpressionError < RequestError ; end
20
+ class MissingParameterError < RequestError ; end
21
+ class NoSuchDomainError < RequestError ; end
22
+ class NumberDomainsExceededError < RequestError ; end
23
+ class NumberDomainAttributesExceededError < RequestError ; end
24
+ class NumberDomainBytesExceededError < RequestError ; end
25
+ class NumberItemAttributesExceededError < RequestError ; end
26
+ class RequestTimeoutError < RequestError ; end
27
+
28
+ class FeatureDeprecatedError < RequestError ; end
29
+
30
+ class ConnectionError < Error
31
+ attr_reader :response
32
+
33
+ def initialize(response)
34
+ super(
35
+ "#{response.code} \
36
+ #{response.message if response.respond_to?(:message)}"
37
+ )
38
+ @response = response
39
+ end
40
+ end
41
+
42
+ end
@@ -0,0 +1,183 @@
1
+ require 'logger'
2
+ require 'time'
3
+ require 'cgi'
4
+ require 'uri'
5
+ require 'net/http'
6
+ require 'base64'
7
+ require 'openssl'
8
+ require 'rexml/document'
9
+ require 'rexml/xpath'
10
+
11
+ module AwsSdb
12
+
13
+ class Service
14
+ def initialize(options={})
15
+ @access_key_id = options[:access_key_id] || ENV['AMAZON_ACCESS_KEY_ID']
16
+ @secret_access_key = options[:secret_access_key] || ENV['AMAZON_SECRET_ACCESS_KEY']
17
+ @base_url = options[:url] || 'http://sdb.amazonaws.com'
18
+ @logger = options[:logger] || Logger.new("aws_sdb.log")
19
+ end
20
+
21
+ def list_domains(max = nil, token = nil)
22
+ params = { 'Action' => 'ListDomains' }
23
+ params['NextToken'] =
24
+ token unless token.nil? || token.empty?
25
+ params['MaxNumberOfDomains'] =
26
+ max.to_s unless max.nil? || max.to_i == 0
27
+ doc = call(:get, params)
28
+ results = []
29
+ REXML::XPath.each(doc, '//DomainName/text()') do |domain|
30
+ results << domain.to_s
31
+ end
32
+ return results, REXML::XPath.first(doc, '//NextToken/text()').to_s
33
+ end
34
+
35
+ def create_domain(domain)
36
+ call(:post, { 'Action' => 'CreateDomain', 'DomainName'=> domain.to_s })
37
+ nil
38
+ end
39
+
40
+ def delete_domain(domain)
41
+ call(
42
+ :delete,
43
+ { 'Action' => 'DeleteDomain', 'DomainName' => domain.to_s }
44
+ )
45
+ nil
46
+ end
47
+
48
+ def query(domain, query, max = nil, token = nil)
49
+ params = {
50
+ 'Action' => 'Query',
51
+ 'QueryExpression' => query,
52
+ 'DomainName' => domain.to_s
53
+ }
54
+ params['NextToken'] =
55
+ token unless token.nil? || token.empty?
56
+ params['MaxNumberOfItems'] =
57
+ max.to_s unless max.nil? || max.to_i == 0
58
+ doc = call(:get, params)
59
+ results = []
60
+ REXML::XPath.each(doc, '//ItemName/text()') do |item|
61
+ results << item.to_s
62
+ end
63
+ return results, REXML::XPath.first(doc, '//NextToken/text()').to_s
64
+ end
65
+
66
+ def query_with_attributes(domain, query, max = nil, token = nil)
67
+ params = {
68
+ 'Action' => 'QueryWithAttributes',
69
+ 'QueryExpression' => query,
70
+ 'DomainName' => domain.to_s
71
+ }
72
+ params['NextToken'] =
73
+ token unless token.nil? || token.empty?
74
+ params['MaxNumberOfItems'] =
75
+ max.to_s unless max.nil? || max.to_i == 0
76
+ doc = call(:get, params)
77
+ results = []
78
+ REXML::XPath.each(doc, '//Item') do |item_element|
79
+ item = { :name => item_element.get_text('Name').to_s }
80
+ attributes = {}
81
+ item_element.each_element('Attribute') do |attribute|
82
+ attributes[attribute.get_text('Name').to_s] = attribute.get_text('Value').to_s
83
+ end
84
+ item[:attributes] = attributes
85
+ results << item
86
+ end
87
+ return results, REXML::XPath.first(doc, '//NextToken/text()').to_s
88
+ end
89
+
90
+ def put_attributes(domain, item, attributes, replace = true)
91
+ params = {
92
+ 'Action' => 'PutAttributes',
93
+ 'DomainName' => domain.to_s,
94
+ 'ItemName' => item.to_s
95
+ }
96
+ count = 0
97
+ attributes.each do | key, values |
98
+ ([]<<values).flatten.each do |value|
99
+ params["Attribute.#{count}.Name"] = key.to_s
100
+ params["Attribute.#{count}.Value"] = value.to_s
101
+ params["Attribute.#{count}.Replace"] = replace
102
+ count += 1
103
+ end
104
+ end
105
+ call(:put, params)
106
+ nil
107
+ end
108
+
109
+ def get_attributes(domain, item)
110
+ doc = call(
111
+ :get,
112
+ {
113
+ 'Action' => 'GetAttributes',
114
+ 'DomainName' => domain.to_s,
115
+ 'ItemName' => item.to_s
116
+ }
117
+ )
118
+ attributes = {}
119
+ REXML::XPath.each(doc, "//Attribute") do |attr|
120
+ key = REXML::XPath.first(attr, './Name/text()').to_s
121
+ value = REXML::XPath.first(attr, './Value/text()').to_s
122
+ ( attributes[key] ||= [] ) << value
123
+ end
124
+ attributes
125
+ end
126
+
127
+ def delete_attributes(domain, item)
128
+ call(
129
+ :delete,
130
+ {
131
+ 'Action' => 'DeleteAttributes',
132
+ 'DomainName' => domain.to_s,
133
+ 'ItemName' => item.to_s
134
+ }
135
+ )
136
+ nil
137
+ end
138
+
139
+ protected
140
+
141
+ def call(method, params)
142
+ params.merge!( {
143
+ 'Version' => '2007-11-07',
144
+ 'SignatureVersion' => '1',
145
+ 'AWSAccessKeyId' => @access_key_id,
146
+ 'Timestamp' => Time.now.gmtime.iso8601
147
+ }
148
+ )
149
+ data = ''
150
+ query = []
151
+ params.keys.sort_by { |k| k.upcase }.each do |key|
152
+ data << "#{key}#{params[key].to_s}"
153
+ query << "#{key}=#{CGI::escape(params[key].to_s)}"
154
+ end
155
+ digest = OpenSSL::Digest::Digest.new('sha1')
156
+ hmac = OpenSSL::HMAC.digest(digest, @secret_access_key, data)
157
+ signature = Base64.encode64(hmac).strip
158
+ query << "Signature=#{CGI::escape(signature)}"
159
+ query = query.join('&')
160
+ url = "#{@base_url}?#{query}"
161
+ uri = URI.parse(url)
162
+ @logger.debug("#{url}") if @logger
163
+ response =
164
+ Net::HTTP.new(uri.host, uri.port).send_request(method, uri.request_uri)
165
+ @logger.debug("#{response.code}\n#{response.body}") if @logger
166
+ raise(ConnectionError.new(response)) unless (200..400).include?(
167
+ response.code.to_i
168
+ )
169
+ doc = REXML::Document.new(response.body)
170
+ error = doc.get_elements('*/Errors/Error')[0]
171
+ raise(
172
+ Module.class_eval(
173
+ "AwsSdb::#{error.get_elements('Code')[0].text}Error"
174
+ ).new(
175
+ error.get_elements('Message')[0].text,
176
+ doc.get_elements('*/RequestID')[0].text
177
+ )
178
+ ) unless error.nil?
179
+ doc
180
+ end
181
+ end
182
+
183
+ end
@@ -0,0 +1,185 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ require 'digest/sha1'
4
+ require 'net/http'
5
+ require 'rexml/document'
6
+
7
+ require 'rubygems'
8
+ require 'uuidtools'
9
+
10
+ include AwsSdb
11
+
12
+ describe Service, "when creating a new domain" do
13
+ before(:all) do
14
+ @service = AwsSdb::Service.new
15
+ @domain = "test-#{UUID.random_create.to_s}"
16
+ domains = @service.list_domains[0]
17
+ domains.each do |d|
18
+ @service.delete_domain(d) if d =~ /^test/
19
+ end
20
+ end
21
+
22
+ after(:all) do
23
+ @service.delete_domain(@domain)
24
+ end
25
+
26
+ it "should not raise an error if a valid new domain name is given" do
27
+ lambda {
28
+ @service.create_domain("test-#{UUID.random_create.to_s}")
29
+ }.should_not raise_error
30
+ end
31
+
32
+ it "should not raise an error if the domain name already exists" do
33
+ domain = "test-#{UUID.random_create.to_s}"
34
+ lambda {
35
+ @service.create_domain(domain)
36
+ @service.create_domain(domain)
37
+ }.should_not raise_error
38
+ end
39
+
40
+ it "should raise an error if an a nil or '' domain name is given" do
41
+ lambda {
42
+ @service.create_domain('')
43
+ }.should raise_error(InvalidParameterValueError)
44
+ lambda {
45
+ @service.create_domain(nil)
46
+ }.should raise_error(InvalidParameterValueError)
47
+ lambda {
48
+ @service.create_domain(' ')
49
+ }.should raise_error(InvalidParameterValueError)
50
+ end
51
+
52
+ it "should raise an error if the domain name length is < 3 or > 255" do
53
+ lambda {
54
+ @service.create_domain('xx')
55
+ }.should raise_error(InvalidParameterValueError)
56
+ lambda {
57
+ @service.create_domain('x'*256)
58
+ }.should raise_error(InvalidParameterValueError)
59
+ end
60
+
61
+ it "should only accept domain names with a-z, A-Z, 0-9, '_', '-', and '.' " do
62
+ lambda {
63
+ @service.create_domain('@$^*()')
64
+ }.should raise_error(InvalidParameterValueError)
65
+ end
66
+
67
+ it "should only accept a maximum of 100 domain names" do
68
+ # TODO Implement this example
69
+ end
70
+
71
+ it "should not have to call amazon to determine domain name correctness" do
72
+ # TODO Implement this example
73
+ end
74
+ end
75
+
76
+ describe Service, "when listing domains" do
77
+ before(:all) do
78
+ @service = AwsSdb::Service.new
79
+ @domain = "test-#{UUID.random_create.to_s}"
80
+ @service.list_domains[0].each do |d|
81
+ @service.delete_domain(d) if d =~ /^test/
82
+ end
83
+ @service.create_domain(@domain)
84
+ end
85
+
86
+ after(:all) do
87
+ @service.delete_domain(@domain)
88
+ end
89
+
90
+ it "should return a complete list" do
91
+ result = nil
92
+ lambda { result = @service.list_domains[0] }.should_not raise_error
93
+ result.should_not be_nil
94
+ result.should_not be_empty
95
+ result.include?(@domain).should == true
96
+ end
97
+ end
98
+
99
+ describe Service, "when deleting domains" do
100
+ before(:all) do
101
+ @service = AwsSdb::Service.new
102
+ @domain = "test-#{UUID.random_create.to_s}"
103
+ @service.list_domains[0].each do |d|
104
+ @service.delete_domain(d) if d =~ /^test/
105
+ end
106
+ @service.create_domain(@domain)
107
+ end
108
+
109
+ after do
110
+ @service.delete_domain(@domain)
111
+ end
112
+
113
+ it "should be able to delete an existing domain" do
114
+ lambda { @service.delete_domain(@domain) }.should_not raise_error
115
+ end
116
+
117
+ it "should not raise an error trying to delete a non-existing domain" do
118
+ lambda {
119
+ @service.delete_domain(UUID.random_create.to_s)
120
+ }.should_not raise_error
121
+ end
122
+ end
123
+
124
+ describe Service, "when managing items" do
125
+ before(:all) do
126
+ @service = AwsSdb::Service.new
127
+ @domain = "test-#{UUID.random_create.to_s}"
128
+ @service.list_domains[0].each do |d|
129
+ @service.delete_domain(d) if d =~ /^test/
130
+ end
131
+ @service.create_domain(@domain)
132
+ @item = "test-#{UUID.random_create.to_s}"
133
+ @attributes = {
134
+ :question => 'What is the answer?',
135
+ :answer => [ true, 'testing123', 4.2, 42, 420 ]
136
+ }
137
+ end
138
+
139
+ after(:all) do
140
+ @service.delete_domain(@domain)
141
+ end
142
+
143
+ it "should be able to put attributes" do
144
+ lambda {
145
+ @service.put_attributes(@domain, @item, @attributes)
146
+ }.should_not raise_error
147
+ end
148
+
149
+ it "should be able to get attributes" do
150
+ result = nil
151
+ lambda {
152
+ result = @service.get_attributes(@domain, @item)
153
+ }.should_not raise_error
154
+ result.should_not be_nil
155
+ result.should_not be_empty
156
+ result.has_key?('answer').should == true
157
+ @attributes[:answer].each do |v|
158
+ result['answer'].include?(v.to_s).should == true
159
+ end
160
+ end
161
+
162
+ it "should be able to query" do
163
+ result = nil
164
+ lambda {
165
+ result = @service.query(@domain, "[ 'answer' = '42' ]")[0]
166
+ }.should_not raise_error
167
+ result.should_not be_nil
168
+ result.should_not be_empty
169
+ result.should_not be_nil
170
+ result.include?(@item).should == true
171
+ end
172
+
173
+ it "should be able to delete attributes" do
174
+ lambda {
175
+ @service.delete_attributes(@domain, @item)
176
+ }.should_not raise_error
177
+ end
178
+ end
179
+
180
+ # TODO Pull the specs from the amazon docs and write more rspec
181
+ # 100 attributes per each call
182
+ # 256 total attribute name-value pairs per item
183
+ # 250 million attributes per domain
184
+ # 10 GB of total user data storage per domain
185
+ # ...etc...
@@ -0,0 +1,4 @@
1
+ $TESTING=true
2
+ $:.push File.join(File.dirname(__FILE__), '..', 'lib')
3
+
4
+ require 'aws_sdb'
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: caring-aws-sdb
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: true
5
+ segments:
6
+ - 0
7
+ - 3
8
+ - 2
9
+ - caring
10
+ - 1
11
+ version: 0.3.2.caring.1
12
+ platform: ruby
13
+ authors:
14
+ - Tim Dysinger
15
+ - David Dollar
16
+ autorequire:
17
+ bindir: bin
18
+ cert_chain: []
19
+
20
+ date: 2009-10-02 00:00:00 -07:00
21
+ default_executable:
22
+ dependencies:
23
+ - !ruby/object:Gem::Dependency
24
+ name: uuidtools
25
+ prerelease: false
26
+ requirement: &id001 !ruby/object:Gem::Requirement
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: rspec
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ segments:
43
+ - 0
44
+ version: "0"
45
+ type: :development
46
+ version_requirements: *id002
47
+ description: Amazon SDB API
48
+ email: ddollar@gmail.com
49
+ executables: []
50
+
51
+ extensions: []
52
+
53
+ extra_rdoc_files:
54
+ - LICENSE
55
+ - README
56
+ files:
57
+ - LICENSE
58
+ - README
59
+ - Rakefile
60
+ - VERSION
61
+ - aws-sdb.gemspec
62
+ - lib/aws_sdb.rb
63
+ - lib/aws_sdb/error.rb
64
+ - lib/aws_sdb/service.rb
65
+ - spec/aws_sdb/service_spec.rb
66
+ - spec/spec_helper.rb
67
+ has_rdoc: true
68
+ homepage: http://github.com/ddollar/aws-sdb
69
+ licenses: []
70
+
71
+ post_install_message:
72
+ rdoc_options:
73
+ - --charset=UTF-8
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ segments:
81
+ - 0
82
+ version: "0"
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ segments:
88
+ - 0
89
+ version: "0"
90
+ requirements: []
91
+
92
+ rubyforge_project:
93
+ rubygems_version: 1.3.6
94
+ signing_key:
95
+ specification_version: 3
96
+ summary: Amazon SDB API
97
+ test_files:
98
+ - spec/aws_sdb/service_spec.rb
99
+ - spec/spec_helper.rb