dm-adapter-simpledb 1.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,52 @@
1
+ require 'dm-types'
2
+
3
+ # NOTE: Do not try to clear SdbArray properties by assigning nil. Instead,
4
+ # assign an empty array:
5
+ #
6
+ # resource.array_prop = []
7
+ # resource.save
8
+ #
9
+ # The reason has to do with DataMapper's lazy-load handling - a lazy-loaded
10
+ # property has a value of nil until it is loaded. If you assign nil, DM thinks
11
+ # that
12
+ module DataMapper
13
+ module Types
14
+ class SdbArray < DataMapper::Type
15
+ primitive ::Object
16
+ lazy true
17
+
18
+ def self.load(value, property)
19
+ value
20
+ end
21
+
22
+ def self.dump(value, property)
23
+ dumped = ::Object.new
24
+ # This is a little screwy. DataMapper has a fixed list of values it
25
+ # considers primitives, and it insists that the value that comes out of
26
+ # a type's .dump() method MUST match one of these types. For SimpleDB
27
+ # Array is effectively a primitive because of the way it stores values,
28
+ # but DM doesn't include Array in it's list of valid primtive types. So
29
+ # we need to return an object which IS considered a primitive - in this
30
+ # case a plain 'ole Ruby Object. In order to convey the actual array
31
+ # value to the backend, we tack on a #to_ary method which returns the
32
+ # array data. RightAws calls Array() on all values before writing them,
33
+ # which in turn calls #to_ary(), and winds up with the correct data. In
34
+ # effect we are sneaking the array data through DataMapper inside a
35
+ # singleton method.
36
+ singleton_class = (class << dumped; self; end)
37
+ singleton_class.send(:define_method, :to_ary) do
38
+ value
39
+ end
40
+ singleton_class.send(:define_method, :to_s) do
41
+ value.to_s
42
+ end
43
+ dumped
44
+ end
45
+
46
+ def self.typecast(value, property)
47
+ value
48
+ end
49
+
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,84 @@
1
+ require 'pathname'
2
+ require Pathname(__FILE__).dirname.parent.expand_path + 'lib/simpledb_adapter'
3
+ require 'ruby-debug'
4
+ require 'benchmark'
5
+
6
+ access_key = ENV['AMAZON_ACCESS_KEY_ID']
7
+ secret_key = ENV['AMAZON_SECRET_ACCESS_KEY']
8
+
9
+ #For those that don't like to mess up their ENV
10
+ if access_key==nil && secret_key==nil
11
+ lines = File.readlines(File.join(File.dirname(__FILE__),'..','aws_config'))
12
+ access_key = lines[0].strip
13
+ secret_key = lines[1].strip
14
+ end
15
+
16
+ DataMapper.setup(:default, {
17
+ :adapter => 'simpledb',
18
+ :access_key => access_key,
19
+ :secret_key => secret_key,
20
+ :domain => 'benchmark'
21
+ })
22
+
23
+ class Person
24
+ include DataMapper::Resource
25
+
26
+ property :id, String, :key => true
27
+ property :name, String, :key => true
28
+ property :age, Integer
29
+ property :wealth, Float
30
+ property :birthday, Date
31
+ property :created_at, DateTime
32
+ end
33
+
34
+ @adapter = repository(:default).adapter
35
+
36
+
37
+ def rand_str(length = 10)
38
+ (0...length).map{65.+(rand(25)).chr}.join
39
+ end
40
+
41
+ def clean_up
42
+ #clean up by removing the benchmark storage model
43
+ ENV['destroy']='true'
44
+ @adapter.destroy_model_storage(repository(:default), Person)
45
+ ENV['destroy']='false'
46
+ end
47
+
48
+ #clean_up
49
+ #sleep(5)
50
+
51
+ Person.auto_migrate!
52
+ sleep(1)
53
+
54
+ Benchmark.bm do|b|
55
+ friends = []
56
+ number = 200
57
+
58
+ b.report("creating #{number} users") do
59
+ number.times do
60
+ friend_attrs = { :id => "person-#{rand_str}-#{Time.now.to_f.to_s}", :name => "name #{Time.now.to_f.to_s} #{rand_str}", :age => 25, :wealth => 25.00, :birthday => Date.today }
61
+ friend = Person.create(friend_attrs)
62
+ friends << friend
63
+ end
64
+ end
65
+
66
+ sleep(5.5) #let everything distribute through SDB
67
+ Person.all(:age => 25) #seems to make sure the first one gets the right amount
68
+
69
+ b.report("Finding all users age 25, 100 Times") do
70
+ 100.times do
71
+ people = Person.all(:age => 25, :limit => number)
72
+ if people.length!=number
73
+ puts "warning wrong number or users #{people.length}"
74
+ #clean_up
75
+ #raise "wrong amount of peoplefound"
76
+ end
77
+ end
78
+ end
79
+
80
+ end
81
+
82
+ clean_up
83
+ sleep(2)
84
+
@@ -0,0 +1,15 @@
1
+ require 'pathname'
2
+ require Pathname(__FILE__).dirname.expand_path + 'spec_helper'
3
+
4
+ describe 'associations' do
5
+ it 'should work with belongs_to associations'
6
+ it 'should work with has n associations'
7
+ end
8
+
9
+ describe 'STI' do
10
+ it 'should override default type'
11
+ it 'should load descendents on parent.all'
12
+ it 'should raise an error if you have a column named couchdb_type'
13
+ end
14
+
15
+
@@ -0,0 +1,18 @@
1
+ require 'pathname'
2
+ require Pathname(__FILE__).dirname.expand_path + 'spec_helper'
3
+
4
+ require 'dm-core/spec/adapter_shared_spec'
5
+
6
+ describe DataMapper::Adapters::SimpleDBAdapter do
7
+ before :all do
8
+ @adapter = DataMapper::Repository.adapters[:default]
9
+ @old_consistency_policy = @adapter.consistency_policy
10
+ @adapter.consistency_policy = :automatic
11
+ end
12
+
13
+ after :all do
14
+ @adapter.consistency_policy = @old_consistency_policy
15
+ end
16
+
17
+ it_should_behave_like 'An Adapter'
18
+ end
@@ -0,0 +1,51 @@
1
+ require 'pathname'
2
+ require Pathname(__FILE__).dirname.expand_path + 'spec_helper'
3
+
4
+ class Professor
5
+ include DataMapper::Resource
6
+
7
+ property :id, String, :key => true
8
+ property :name, String, :key => true
9
+ property :age, Integer
10
+ property :wealth, Float
11
+ property :birthday, Date
12
+ property :created_at, DateTime
13
+
14
+ end
15
+
16
+ describe 'with multiple records saved' do
17
+ before(:each) do
18
+ @adapter.wait_for_consistency
19
+ @person_attrs = { :id => "person-#{Time.now.to_f.to_s}", :name => 'Jeremy Boles', :age => 25, :wealth => 25.00, :birthday => Date.today }
20
+ @jeremy = Professor.create(@person_attrs.merge(:id => Time.now.to_f.to_s, :name => "Jeremy Boles", :age => 25))
21
+ @danielle = Professor.create(@person_attrs.merge(:id => Time.now.to_f.to_s, :name => "Danille Boles", :age => 26))
22
+ @keegan = Professor.create(@person_attrs.merge(:id => Time.now.to_f.to_s, :name => "Keegan Jones", :age => 20))
23
+ @adapter.wait_for_consistency
24
+ end
25
+
26
+ after(:each) do
27
+ @jeremy.destroy
28
+ @danielle.destroy
29
+ @keegan.destroy
30
+ end
31
+
32
+ it 'should handle DateTime' do
33
+ time = DateTime.civil(1970,1,1)
34
+ @jeremy.created_at = time
35
+ @jeremy.save
36
+ @adapter.wait_for_consistency
37
+ person = Professor.get!(@jeremy.id, @jeremy.name)
38
+ person.created_at.should == time
39
+ end
40
+
41
+ it 'should handle Date' do
42
+ person = Professor.get!(@jeremy.id, @jeremy.name)
43
+ person.birthday.should == @jeremy.birthday
44
+ end
45
+
46
+ it 'should match with Data' do
47
+ people = Professor.all(:birthday => Date.today)
48
+ people.length.should == 3
49
+ end
50
+
51
+ end
@@ -0,0 +1,110 @@
1
+ require 'pathname'
2
+ require Pathname(__FILE__).dirname.expand_path + 'spec_helper'
3
+
4
+ class Hero
5
+ include DataMapper::Resource
6
+
7
+ property :id, String, :key => true
8
+ property :name, String, :key => true
9
+ property :age, Integer
10
+ property :wealth, Float
11
+ property :birthday, Date
12
+ property :created_at, DateTime
13
+
14
+ end
15
+
16
+ describe 'with multiple records saved' do
17
+ before(:each) do
18
+ @person_attrs = { :id => "person-#{Time.now.to_f.to_s}", :name => 'Jeremy Boles', :age => 25, :wealth => 25.00, :birthday => Date.today }
19
+ @jeremy = Hero.create(@person_attrs.merge(:id => Time.now.to_f.to_s, :name => "Jeremy Boles", :age => 25))
20
+ @danielle = Hero.create(@person_attrs.merge(:id => Time.now.to_f.to_s, :name => "Danille Boles", :age => 26))
21
+ @keegan = Hero.create(@person_attrs.merge(:id => Time.now.to_f.to_s, :name => "Keegan Jones", :age => 20, :wealth => 15.00))
22
+ @adapter.wait_for_consistency
23
+ end
24
+
25
+ after(:each) do
26
+ @jeremy.destroy
27
+ @danielle.destroy
28
+ @keegan.destroy
29
+ end
30
+
31
+ it 'should handle limit one case' do
32
+ persons = Hero.all(:limit => 1)
33
+ persons.length.should ==1
34
+ end
35
+
36
+ it 'should handle max item limit case' do
37
+ persons = Hero.all(:limit => 3)
38
+ persons.length.should ==3
39
+ end
40
+
41
+ it 'should handle max item if limit is large case' do
42
+ persons = Hero.all(:limit => 150)
43
+ persons.length.should ==3
44
+ end
45
+
46
+ it 'should handle ordering asc results with a limit' do
47
+ persons = Hero.all(:order => [:age.asc], :limit => 2)
48
+ persons.inspect #can't access via array until loaded? Weird
49
+ persons.length.should ==2
50
+ persons[0].should == @keegan
51
+ persons[1].should == @jeremy
52
+ end
53
+
54
+ it 'should handle ordering asc results' do
55
+ persons = Hero.all(:order => [:age.asc])
56
+ persons.inspect #can't access via array until loaded? Weird
57
+ persons[0].should == @keegan
58
+ persons[1].should == @jeremy
59
+ persons[2].should == @danielle
60
+ end
61
+
62
+ it 'should handle ordering desc results' do
63
+ persons = Hero.all(:order => [:age.desc])
64
+ persons.inspect #can't access via array until loaded? Weird
65
+ persons[0].should == @danielle
66
+ persons[1].should == @jeremy
67
+ persons[2].should == @keegan
68
+ end
69
+
70
+ it 'should handle ordering results with multiple conditionss' do
71
+ persons = Hero.all(:age.gt => 20, :wealth.gt => 20, :order => [:age.desc])
72
+ persons.inspect #can't access via array until loaded? Weird
73
+ persons.length.should ==2
74
+ persons[0].should == @danielle
75
+ persons[1].should == @jeremy
76
+ end
77
+
78
+ it 'should handle ordering results with ordered by conditions' do
79
+ persons = Hero.all(:age.gt => 20, :order => [:age.desc])
80
+ persons.inspect #can't access via array until loaded? Weird
81
+ persons.length.should ==2
82
+ persons[0].should == @danielle
83
+ persons[1].should == @jeremy
84
+ end
85
+
86
+ it 'should handle ordering results with unorder by conditions' do
87
+ persons = Hero.all(:wealth.gt => 20.00, :order => [:age.desc])
88
+ persons.inspect #can't access via array until loaded? Weird
89
+ persons.length.should ==2
90
+ persons[0].should == @danielle
91
+ persons[1].should == @jeremy
92
+ end
93
+
94
+ context "with many entries" do
95
+ before :each do
96
+ resources = []
97
+ 111.times do |i|
98
+ resources << Hero.new(:id => i, :name => "Hero#{i}")
99
+ end
100
+ DataMapper.repository(:default).create(resources)
101
+ @adapter.wait_for_consistency
102
+ end
103
+
104
+ it "should support limits over 100" do
105
+ results = Hero.all(:limit => 110)
106
+ results.should have(110).entries
107
+ end
108
+ end
109
+
110
+ end
@@ -0,0 +1,41 @@
1
+ require 'pathname'
2
+ require Pathname(__FILE__).dirname.expand_path + 'spec_helper'
3
+ require 'dm-migrations'
4
+
5
+ describe 'support migrations' do
6
+
7
+ #TODO do this on different storage
8
+ class Person
9
+ include DataMapper::Resource
10
+
11
+ property :id, String, :key => true
12
+ property :name, String, :key => true
13
+ property :age, Integer
14
+ property :wealth, Float
15
+ property :birthday, Date
16
+ property :created_at, DateTime
17
+
18
+ end
19
+
20
+ # test can't be run simultanious make it delete a throwawaable storage model
21
+ # instead of the one used by all the tests
22
+ # it "should destroy model storage" do
23
+ # ENV['destroy']='true'
24
+ # @adapter.destroy_model_storage(repository(:default), Person)
25
+ # @adapter.storage_exists?("missionaries").should == false
26
+ # ENV['destroy']='false'
27
+ # @adapter.create_model_storage(repository(:default), Person)
28
+ # @adapter.storage_exists?("missionaries").should == true
29
+ # end
30
+
31
+ before :all do
32
+ @sdb.delete_domain(@domain)
33
+ end
34
+
35
+ it "should create model storage" do
36
+ DataMapper.auto_migrate!
37
+ @adapter.storage_exists?(@domain).should == true
38
+ end
39
+
40
+ end
41
+
@@ -0,0 +1,111 @@
1
+ require 'pathname'
2
+ require Pathname(__FILE__).dirname.expand_path + 'spec_helper'
3
+
4
+ class Person
5
+ include DataMapper::Resource
6
+
7
+ property :id, String, :key => true
8
+ property :name, String, :key => true
9
+ property :age, Integer
10
+ property :wealth, Float
11
+ property :birthday, Date
12
+ property :created_at, DateTime
13
+
14
+ belongs_to :company
15
+ end
16
+
17
+ #TODO write some tests with company or drop this
18
+ class Company
19
+ include DataMapper::Resource
20
+
21
+ property :id, String, :key => true
22
+ property :name, String, :key => true
23
+
24
+ has n, :people
25
+ end
26
+
27
+ describe 'with multiple records saved' do
28
+ before(:all) do
29
+ @person_attrs = { :id => "person-#{Time.now.to_f.to_s}", :name => 'Jeremy Boles', :age => 25, :wealth => 25.00, :birthday => Date.today }
30
+ @jeremy = Person.create(@person_attrs.merge(:id => Time.now.to_f.to_s, :name => "Jeremy Boles", :age => 25))
31
+ @danielle = Person.create(@person_attrs.merge(:id => Time.now.to_f.to_s, :name => "Danille Boles", :age => 26))
32
+ @keegan = Person.create(@person_attrs.merge(:id => Time.now.to_f.to_s, :name => "Keegan Jones", :age => 20))
33
+ @adapter.wait_for_consistency
34
+ end
35
+
36
+ after(:all) do
37
+ @jeremy.destroy
38
+ @danielle.destroy
39
+ @keegan.destroy
40
+ end
41
+
42
+ it 'should get all records' do
43
+ Person.all.length.should == 3
44
+ end
45
+
46
+ it 'should get records by eql matcher' do
47
+ people = Person.all(:age => 25)
48
+ people.length.should == 1
49
+ end
50
+
51
+ it 'should get record by eql matcher' do
52
+ person = Person.first(:conditions => {:age => 25})
53
+ person.should_not be_nil
54
+ end
55
+
56
+ it 'should get records by not matcher' do
57
+ people = Person.all(:age.not => 25)
58
+ people.should have(2).entries
59
+ end
60
+
61
+ it 'should get record by not matcher' do
62
+ person = Person.first(:age.not => 25)
63
+ person.should_not be_nil
64
+ end
65
+
66
+ it 'should get records by gt matcher' do
67
+ people = Person.all(:age.gt => 25)
68
+ people.length.should == 1
69
+ end
70
+
71
+ it 'should get records by gte matcher' do
72
+ people = Person.all(:age.gte => 25)
73
+ people.length.should == 2
74
+ end
75
+
76
+ it 'should get records by lt matcher' do
77
+ people = Person.all(:age.lt => 25)
78
+ people.length.should == 1
79
+ end
80
+
81
+ it 'should get records by lte matcher' do
82
+ people = Person.all(:age.lte => 25)
83
+ people.length.should == 2
84
+ end
85
+
86
+ it 'should get record by lte matcher' do
87
+ person = Person.first(:age.lte => 25)
88
+ person.should_not be_nil
89
+ end
90
+
91
+ it 'should get records with multiple matchers' do
92
+ people = Person.all(:birthday => Date.today, :age.lte => 25)
93
+ people.length.should == 2
94
+ end
95
+
96
+ it 'should get records by the like matcher' do
97
+ people = Person.all(:name.like => 'Jeremy%')
98
+ people.should == [@jeremy]
99
+ end
100
+
101
+ it 'should get records by the IN matcher' do
102
+ people = Person.all(:id => [@jeremy.id, @danielle.id])
103
+ people.should include(@jeremy)
104
+ people.should include(@danielle)
105
+ people.should_not include(@keegan)
106
+ end
107
+ it "should get no records if IN array is empty" do
108
+ people = Person.all(:id => [])
109
+ people.should be_empty
110
+ end
111
+ end