dynamoid-moda 0.7.1

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.
Files changed (136) hide show
  1. checksums.yaml +15 -0
  2. data/.document +5 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +7 -0
  5. data/Gemfile +24 -0
  6. data/Gemfile.lock +118 -0
  7. data/Gemfile_activemodel4 +24 -0
  8. data/Gemfile_activemodel4.lock +88 -0
  9. data/LICENSE.txt +20 -0
  10. data/README.markdown +360 -0
  11. data/Rakefile +93 -0
  12. data/VERSION +1 -0
  13. data/doc/.nojekyll +0 -0
  14. data/doc/Dynamoid.html +328 -0
  15. data/doc/Dynamoid/Adapter.html +1872 -0
  16. data/doc/Dynamoid/Adapter/AwsSdk.html +2101 -0
  17. data/doc/Dynamoid/Adapter/Local.html +1574 -0
  18. data/doc/Dynamoid/Associations.html +138 -0
  19. data/doc/Dynamoid/Associations/Association.html +847 -0
  20. data/doc/Dynamoid/Associations/BelongsTo.html +161 -0
  21. data/doc/Dynamoid/Associations/ClassMethods.html +766 -0
  22. data/doc/Dynamoid/Associations/HasAndBelongsToMany.html +167 -0
  23. data/doc/Dynamoid/Associations/HasMany.html +167 -0
  24. data/doc/Dynamoid/Associations/HasOne.html +161 -0
  25. data/doc/Dynamoid/Associations/ManyAssociation.html +1684 -0
  26. data/doc/Dynamoid/Associations/SingleAssociation.html +627 -0
  27. data/doc/Dynamoid/Components.html +242 -0
  28. data/doc/Dynamoid/Config.html +412 -0
  29. data/doc/Dynamoid/Config/Options.html +638 -0
  30. data/doc/Dynamoid/Criteria.html +138 -0
  31. data/doc/Dynamoid/Criteria/Chain.html +1471 -0
  32. data/doc/Dynamoid/Criteria/ClassMethods.html +105 -0
  33. data/doc/Dynamoid/Dirty.html +424 -0
  34. data/doc/Dynamoid/Dirty/ClassMethods.html +174 -0
  35. data/doc/Dynamoid/Document.html +1033 -0
  36. data/doc/Dynamoid/Document/ClassMethods.html +1116 -0
  37. data/doc/Dynamoid/Errors.html +125 -0
  38. data/doc/Dynamoid/Errors/ConditionalCheckFailedException.html +141 -0
  39. data/doc/Dynamoid/Errors/DocumentNotValid.html +221 -0
  40. data/doc/Dynamoid/Errors/Error.html +137 -0
  41. data/doc/Dynamoid/Errors/InvalidField.html +141 -0
  42. data/doc/Dynamoid/Errors/InvalidQuery.html +131 -0
  43. data/doc/Dynamoid/Errors/MissingRangeKey.html +141 -0
  44. data/doc/Dynamoid/Fields.html +686 -0
  45. data/doc/Dynamoid/Fields/ClassMethods.html +438 -0
  46. data/doc/Dynamoid/Finders.html +135 -0
  47. data/doc/Dynamoid/Finders/ClassMethods.html +943 -0
  48. data/doc/Dynamoid/IdentityMap.html +492 -0
  49. data/doc/Dynamoid/IdentityMap/ClassMethods.html +534 -0
  50. data/doc/Dynamoid/Indexes.html +321 -0
  51. data/doc/Dynamoid/Indexes/ClassMethods.html +369 -0
  52. data/doc/Dynamoid/Indexes/Index.html +1142 -0
  53. data/doc/Dynamoid/Middleware.html +115 -0
  54. data/doc/Dynamoid/Middleware/IdentityMap.html +264 -0
  55. data/doc/Dynamoid/Persistence.html +892 -0
  56. data/doc/Dynamoid/Persistence/ClassMethods.html +836 -0
  57. data/doc/Dynamoid/Validations.html +415 -0
  58. data/doc/_index.html +506 -0
  59. data/doc/class_list.html +53 -0
  60. data/doc/css/common.css +1 -0
  61. data/doc/css/full_list.css +57 -0
  62. data/doc/css/style.css +338 -0
  63. data/doc/file.LICENSE.html +73 -0
  64. data/doc/file.README.html +416 -0
  65. data/doc/file_list.html +58 -0
  66. data/doc/frames.html +28 -0
  67. data/doc/index.html +416 -0
  68. data/doc/js/app.js +214 -0
  69. data/doc/js/full_list.js +178 -0
  70. data/doc/js/jquery.js +4 -0
  71. data/doc/method_list.html +1144 -0
  72. data/doc/top-level-namespace.html +112 -0
  73. data/dynamoid-moda.gemspec +210 -0
  74. data/dynamoid.gemspec +208 -0
  75. data/lib/dynamoid.rb +46 -0
  76. data/lib/dynamoid/adapter.rb +267 -0
  77. data/lib/dynamoid/adapter/aws_sdk.rb +309 -0
  78. data/lib/dynamoid/associations.rb +106 -0
  79. data/lib/dynamoid/associations/association.rb +105 -0
  80. data/lib/dynamoid/associations/belongs_to.rb +44 -0
  81. data/lib/dynamoid/associations/has_and_belongs_to_many.rb +40 -0
  82. data/lib/dynamoid/associations/has_many.rb +39 -0
  83. data/lib/dynamoid/associations/has_one.rb +39 -0
  84. data/lib/dynamoid/associations/many_association.rb +191 -0
  85. data/lib/dynamoid/associations/single_association.rb +69 -0
  86. data/lib/dynamoid/components.rb +37 -0
  87. data/lib/dynamoid/config.rb +57 -0
  88. data/lib/dynamoid/config/options.rb +78 -0
  89. data/lib/dynamoid/criteria.rb +29 -0
  90. data/lib/dynamoid/criteria/chain.rb +326 -0
  91. data/lib/dynamoid/dirty.rb +47 -0
  92. data/lib/dynamoid/document.rb +199 -0
  93. data/lib/dynamoid/errors.rb +28 -0
  94. data/lib/dynamoid/fields.rb +138 -0
  95. data/lib/dynamoid/finders.rb +133 -0
  96. data/lib/dynamoid/identity_map.rb +96 -0
  97. data/lib/dynamoid/indexes.rb +69 -0
  98. data/lib/dynamoid/indexes/index.rb +103 -0
  99. data/lib/dynamoid/middleware/identity_map.rb +16 -0
  100. data/lib/dynamoid/persistence.rb +292 -0
  101. data/lib/dynamoid/validations.rb +36 -0
  102. data/spec/app/models/address.rb +13 -0
  103. data/spec/app/models/camel_case.rb +34 -0
  104. data/spec/app/models/car.rb +6 -0
  105. data/spec/app/models/magazine.rb +11 -0
  106. data/spec/app/models/message.rb +9 -0
  107. data/spec/app/models/nuclear_submarine.rb +5 -0
  108. data/spec/app/models/sponsor.rb +8 -0
  109. data/spec/app/models/subscription.rb +12 -0
  110. data/spec/app/models/tweet.rb +12 -0
  111. data/spec/app/models/user.rb +26 -0
  112. data/spec/app/models/vehicle.rb +7 -0
  113. data/spec/dynamoid/adapter/aws_sdk_spec.rb +376 -0
  114. data/spec/dynamoid/adapter_spec.rb +155 -0
  115. data/spec/dynamoid/associations/association_spec.rb +194 -0
  116. data/spec/dynamoid/associations/belongs_to_spec.rb +71 -0
  117. data/spec/dynamoid/associations/has_and_belongs_to_many_spec.rb +47 -0
  118. data/spec/dynamoid/associations/has_many_spec.rb +42 -0
  119. data/spec/dynamoid/associations/has_one_spec.rb +45 -0
  120. data/spec/dynamoid/associations_spec.rb +16 -0
  121. data/spec/dynamoid/config_spec.rb +27 -0
  122. data/spec/dynamoid/criteria/chain_spec.rb +210 -0
  123. data/spec/dynamoid/criteria_spec.rb +75 -0
  124. data/spec/dynamoid/dirty_spec.rb +57 -0
  125. data/spec/dynamoid/document_spec.rb +180 -0
  126. data/spec/dynamoid/fields_spec.rb +156 -0
  127. data/spec/dynamoid/finders_spec.rb +147 -0
  128. data/spec/dynamoid/identity_map_spec.rb +45 -0
  129. data/spec/dynamoid/indexes/index_spec.rb +104 -0
  130. data/spec/dynamoid/indexes_spec.rb +25 -0
  131. data/spec/dynamoid/persistence_spec.rb +301 -0
  132. data/spec/dynamoid/validations_spec.rb +36 -0
  133. data/spec/dynamoid_spec.rb +14 -0
  134. data/spec/spec_helper.rb +55 -0
  135. data/spec/support/with_partitioning.rb +15 -0
  136. metadata +363 -0
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+ module Dynamoid
3
+
4
+ # Provide ActiveModel validations to Dynamoid documents.
5
+ module Validations
6
+ extend ActiveSupport::Concern
7
+
8
+ include ActiveModel::Validations
9
+ include ActiveModel::Validations::Callbacks
10
+
11
+ # Override save to provide validation support.
12
+ #
13
+ # @since 0.2.0
14
+ def save(options = {})
15
+ options.reverse_merge!(:validate => true)
16
+ return false if options[:validate] and (not valid?)
17
+ super
18
+ end
19
+
20
+ # Is this object valid?
21
+ #
22
+ # @since 0.2.0
23
+ def valid?(context = nil)
24
+ context ||= (new_record? ? :create : :update)
25
+ super(context)
26
+ end
27
+
28
+ # Raise an error unless this object is valid.
29
+ #
30
+ # @since 0.2.0
31
+ def save!
32
+ raise Dynamoid::Errors::DocumentNotValid.new(self) unless valid?
33
+ save(:validate => false)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,13 @@
1
+ class Address
2
+ include Dynamoid::Document
3
+
4
+ field :city
5
+ field :options, :serialized
6
+ field :deliverable, :boolean
7
+
8
+ field :lock_version, :integer #Provides Optimistic Locking
9
+
10
+ def zip_code=(zip_code)
11
+ self.city = "Chicago"
12
+ end
13
+ end
@@ -0,0 +1,34 @@
1
+ class CamelCase
2
+ include Dynamoid::Document
3
+
4
+ field :color
5
+
6
+ belongs_to :magazine
7
+ has_many :users
8
+ has_one :sponsor
9
+ has_and_belongs_to_many :subscriptions
10
+
11
+ before_create :doing_before_create
12
+ after_create :doing_after_create
13
+ before_update :doing_before_update
14
+ after_update :doing_after_update
15
+
16
+ private
17
+
18
+ def doing_before_create
19
+ true
20
+ end
21
+
22
+ def doing_after_create
23
+ true
24
+ end
25
+
26
+ def doing_before_update
27
+ true
28
+ end
29
+
30
+ def doing_after_update
31
+ true
32
+ end
33
+
34
+ end
@@ -0,0 +1,6 @@
1
+ require_relative 'vehicle'
2
+
3
+ class Car < Vehicle
4
+
5
+ field :power_locks, :boolean
6
+ end
@@ -0,0 +1,11 @@
1
+ class Magazine
2
+ include Dynamoid::Document
3
+
4
+ field :title
5
+
6
+ has_many :subscriptions
7
+ has_many :camel_cases
8
+ has_one :sponsor
9
+
10
+ belongs_to :owner, :class_name => 'User', :inverse_of => :books
11
+ end
@@ -0,0 +1,9 @@
1
+ class Message
2
+ include Dynamoid::Document
3
+
4
+ table name: :messages, key: :message_id, read_capacity: 200, write_capacity: 200
5
+
6
+ range :time, :datetime
7
+
8
+ field :text
9
+ end
@@ -0,0 +1,5 @@
1
+ require_relative 'vehicle'
2
+ class NuclearSubmarine < Vehicle
3
+
4
+ field :torpedoes, :integer
5
+ end
@@ -0,0 +1,8 @@
1
+ class Sponsor
2
+ include Dynamoid::Document
3
+
4
+ belongs_to :magazine
5
+ has_many :subscriptions
6
+
7
+ belongs_to :camel_case
8
+ end
@@ -0,0 +1,12 @@
1
+ class Subscription
2
+ include Dynamoid::Document
3
+
4
+ field :length, :integer
5
+
6
+ belongs_to :magazine
7
+ has_and_belongs_to_many :users
8
+
9
+ belongs_to :customer, :class_name => 'User', :inverse_of => :monthly
10
+
11
+ has_and_belongs_to_many :camel_cases
12
+ end
@@ -0,0 +1,12 @@
1
+ class Tweet
2
+ include Dynamoid::Document
3
+
4
+ table name: :twitters, key: :tweet_id, read_capacity: 200, write_capacity: 200
5
+
6
+ range :group, :string
7
+
8
+ field :msg
9
+ field :count, :integer
10
+ field :tags, :set
11
+ field :user_name
12
+ end
@@ -0,0 +1,26 @@
1
+ class User
2
+ include Dynamoid::Document
3
+
4
+ field :name
5
+ field :email
6
+ field :password
7
+ field :last_logged_in_at, :datetime
8
+
9
+ index :name
10
+ index :email
11
+ index [:name, :email]
12
+ index :name, :range_key => :created_at
13
+ index :name, :range_key => :last_logged_in_at
14
+ index :created_at, :range => true
15
+
16
+ has_and_belongs_to_many :subscriptions
17
+
18
+ has_many :books, :class_name => 'Magazine', :inverse_of => :owner
19
+ has_one :monthly, :class_name => 'Subscription', :inverse_of => :customer
20
+
21
+ has_and_belongs_to_many :followers, :class_name => 'User', :inverse_of => :following
22
+ has_and_belongs_to_many :following, :class_name => 'User', :inverse_of => :followers
23
+
24
+ belongs_to :camel_case
25
+
26
+ end
@@ -0,0 +1,7 @@
1
+ class Vehicle
2
+ include Dynamoid::Document
3
+
4
+ field :type
5
+
6
+ field :description
7
+ end
@@ -0,0 +1,376 @@
1
+ require 'dynamoid/adapter/aws_sdk'
2
+ require File.expand_path(File.dirname(__FILE__) + '../../../spec_helper')
3
+
4
+ describe Dynamoid::Adapter::AwsSdk do
5
+ before(:each) do
6
+ pending "You must have an active DynamoDB connection" unless ENV['ACCESS_KEY'] && ENV['SECRET_KEY']
7
+ end
8
+
9
+ #
10
+ # These let() definitions create tables "dynamoid_tests_TestTable<N>" and return the
11
+ # name of the table. An after(:each) handler drops any tables created this way after each test
12
+ #
13
+ # Name => Constructor args
14
+ {
15
+ 1 => [:id],
16
+ 2 => [:id],
17
+ 3 => [:id, {:range_key => {:range => :number}}],
18
+ 4 => [:id, {:range_key => {:range => :number}}]
19
+ }.each do |n, args|
20
+ name = "dynamoid_tests_TestTable#{n}"
21
+ let(:"test_table#{n}") do
22
+ Dynamoid::Adapter.create_table(name, *args)
23
+ @created_tables << name
24
+ name
25
+ end
26
+ end
27
+
28
+ before(:each) { @created_tables = [] }
29
+ after(:each) do
30
+ @created_tables.each do |t|
31
+ Dynamoid::Adapter.delete_table(t)
32
+ end
33
+ end
34
+
35
+ #
36
+ # Returns a random key parition if partitioning is on, or an empty string if
37
+ # it is off, useful for shared examples where partitioning may or may not be on
38
+ #
39
+ def key_partition
40
+ Dynamoid::Config.partitioning? ? ".#{Random.rand(Dynamoid::Config.partition_size)}" : ''
41
+ end
42
+
43
+ #
44
+ # Tests adapter against ranged tables
45
+ #
46
+ shared_examples 'range queries' do
47
+ before do
48
+ Dynamoid::Adapter.put_item(test_table3, {:id => "1#{key_partition}", :range => 1.0})
49
+ Dynamoid::Adapter.put_item(test_table3, {:id => "1#{key_partition}", :range => 3.0})
50
+ end
51
+
52
+ it 'performs query on a table with a range and selects items in a range' do
53
+ Dynamoid::Adapter.query(test_table3, :hash_value => '1', :range_value => 0.0..3.0).to_a.should =~ [{:id => '1', :range => BigDecimal.new(1)}, {:id => '1', :range => BigDecimal.new(3)}]
54
+ end
55
+
56
+ it 'performs query on a table with a range and selects items in a range with :select option' do
57
+ Dynamoid::Adapter.query(test_table3, :hash_value => '1', :range_value => 0.0..3.0, :select => :all).to_a.should =~ [{:id => '1', :range => BigDecimal.new(1)}, {:id => '1', :range => BigDecimal.new(3)}]
58
+ end
59
+
60
+ it 'performs query on a table with a range and selects items greater than' do
61
+ Dynamoid::Adapter.query(test_table3, :hash_value => '1', :range_greater_than => 1.0).to_a.should =~ [{:id => '1', :range => BigDecimal.new(3)}]
62
+ end
63
+
64
+ it 'performs query on a table with a range and selects items less than' do
65
+ Dynamoid::Adapter.query(test_table3, :hash_value => '1', :range_less_than => 2.0).to_a.should =~ [{:id => '1', :range => BigDecimal.new(1)}]
66
+ end
67
+
68
+ it 'performs query on a table with a range and selects items gte' do
69
+ Dynamoid::Adapter.query(test_table3, :hash_value => '1', :range_gte => 1.0).to_a.should =~ [{:id => '1', :range => BigDecimal.new(1)}, {:id => '1', :range => BigDecimal.new(3)}]
70
+ end
71
+
72
+ it 'performs query on a table with a range and selects items lte' do
73
+ Dynamoid::Adapter.query(test_table3, :hash_value => '1', :range_lte => 3.0).to_a.should =~ [{:id => '1', :range => BigDecimal.new(1)}, {:id => '1', :range => BigDecimal.new(3)}]
74
+ end
75
+ end
76
+
77
+ #
78
+ # Tests scan_index_forwards flag behavior on range queries
79
+ #
80
+ shared_examples 'correct ordering' do
81
+ before(:each) do
82
+ pending "Order is not preserved on paritioned tables" if(Dynamoid::Config.partitioning?)
83
+ Dynamoid::Adapter.put_item(test_table4, {:id => "1#{key_partition}", :order => 1, :range => 1.0})
84
+ Dynamoid::Adapter.put_item(test_table4, {:id => "1#{key_partition}", :order => 2, :range => 2.0})
85
+ Dynamoid::Adapter.put_item(test_table4, {:id => "1#{key_partition}", :order => 3, :range => 3.0})
86
+ Dynamoid::Adapter.put_item(test_table4, {:id => "1#{key_partition}", :order => 4, :range => 4.0})
87
+ Dynamoid::Adapter.put_item(test_table4, {:id => "1#{key_partition}", :order => 5, :range => 5.0})
88
+ Dynamoid::Adapter.put_item(test_table4, {:id => "1#{key_partition}", :order => 6, :range => 6.0})
89
+ end
90
+
91
+ it 'performs query on a table with a range and selects items less than that is in the correct order, scan_index_forward true' do
92
+ query = Dynamoid::Adapter.query(test_table4, :hash_value => '1', :range_greater_than => 0, :scan_index_forward => true).to_a
93
+ query[0].should == {:id => '1', :order => 1, :range => BigDecimal.new(1)}
94
+ query[1].should == {:id => '1', :order => 2, :range => BigDecimal.new(2)}
95
+ query[2].should == {:id => '1', :order => 3, :range => BigDecimal.new(3)}
96
+ query[3].should == {:id => '1', :order => 4, :range => BigDecimal.new(4)}
97
+ query[4].should == {:id => '1', :order => 5, :range => BigDecimal.new(5)}
98
+ query[5].should == {:id => '1', :order => 6, :range => BigDecimal.new(6)}
99
+ end
100
+
101
+ it 'performs query on a table with a range and selects items less than that is in the correct order, scan_index_forward false' do
102
+ query = Dynamoid::Adapter.query(test_table4, :hash_value => '1', :range_greater_than => 0, :scan_index_forward => false).to_a
103
+ query[5].should == {:id => '1', :order => 1, :range => BigDecimal.new(1)}
104
+ query[4].should == {:id => '1', :order => 2, :range => BigDecimal.new(2)}
105
+ query[3].should == {:id => '1', :order => 3, :range => BigDecimal.new(3)}
106
+ query[2].should == {:id => '1', :order => 4, :range => BigDecimal.new(4)}
107
+ query[1].should == {:id => '1', :order => 5, :range => BigDecimal.new(5)}
108
+ query[0].should == {:id => '1', :order => 6, :range => BigDecimal.new(6)}
109
+ end
110
+ end
111
+
112
+
113
+ context 'without a preexisting table' do
114
+ # CreateTable and DeleteTable
115
+ it 'performs CreateTable and DeleteTable' do
116
+ table = Dynamoid::Adapter.create_table('CreateTable', :id, :range_key => { :created_at => :number })
117
+
118
+ Dynamoid::Adapter.connection.tables.collect{|t| t.name}.should include 'CreateTable'
119
+
120
+ Dynamoid::Adapter.delete_table('CreateTable')
121
+ end
122
+ end
123
+
124
+
125
+ context 'with a preexisting table without paritioning' do
126
+ # GetItem, PutItem and DeleteItem
127
+ it "performs GetItem for an item that does not exist" do
128
+ Dynamoid::Adapter.get_item(test_table1, '1').should be_nil
129
+ end
130
+
131
+ it "performs GetItem for an item that does exist" do
132
+ Dynamoid::Adapter.put_item(test_table1, {:id => '1', :name => 'Josh'})
133
+
134
+ Dynamoid::Adapter.get_item(test_table1, '1').should == {:name => 'Josh', :id => '1'}
135
+
136
+ Dynamoid::Adapter.delete_item(test_table1, '1')
137
+
138
+ Dynamoid::Adapter.get_item(test_table1, '1').should be_nil
139
+ end
140
+
141
+ it 'performs GetItem for an item that does exist with a range key' do
142
+ Dynamoid::Adapter.put_item(test_table3, {:id => '1', :name => 'Josh', :range => 2.0})
143
+
144
+ Dynamoid::Adapter.get_item(test_table3, '1', :range_key => 2.0).should == {:name => 'Josh', :id => '1', :range => 2.0}
145
+
146
+ Dynamoid::Adapter.delete_item(test_table3, '1', :range_key => 2.0)
147
+
148
+ Dynamoid::Adapter.get_item(test_table3, '1', :range_key => 2.0).should be_nil
149
+ end
150
+
151
+ it 'performs DeleteItem for an item that does not exist' do
152
+ Dynamoid::Adapter.delete_item(test_table1, '1')
153
+
154
+ Dynamoid::Adapter.get_item(test_table1, '1').should be_nil
155
+ end
156
+
157
+ it 'performs PutItem for an item that does not exist' do
158
+ Dynamoid::Adapter.put_item(test_table1, {:id => '1', :name => 'Josh'})
159
+
160
+ Dynamoid::Adapter.get_item(test_table1, '1').should == {:id => '1', :name => 'Josh'}
161
+ end
162
+
163
+ # BatchGetItem
164
+ it 'passes options to underlying BatchGet call' do
165
+ AWS::DynamoDB::BatchGet.any_instance.expects(:table).with(test_table1, :all, ['1', '2'], :consistent_read => true)
166
+ described_class.batch_get_item({test_table1 => ['1', '2']}, :consistent_read => true)
167
+ end
168
+
169
+ it "performs BatchGetItem with singular keys" do
170
+ Dynamoid::Adapter.put_item(test_table1, {:id => '1', :name => 'Josh'})
171
+ Dynamoid::Adapter.put_item(test_table2, {:id => '1', :name => 'Justin'})
172
+
173
+ results = Dynamoid::Adapter.batch_get_item(test_table1 => '1', test_table2 => '1')
174
+ results.size.should == 2
175
+ results[test_table1].should include({:name => 'Josh', :id => '1'})
176
+ results[test_table2].should include({:name => 'Justin', :id => '1'})
177
+ end
178
+
179
+ it "performs BatchGetItem with multiple keys" do
180
+ Dynamoid::Adapter.put_item(test_table1, {:id => '1', :name => 'Josh'})
181
+ Dynamoid::Adapter.put_item(test_table1, {:id => '2', :name => 'Justin'})
182
+
183
+ results = Dynamoid::Adapter.batch_get_item(test_table1 => ['1', '2'])
184
+ results.size.should == 1
185
+ results[test_table1].should include({:name => 'Josh', :id => '1'})
186
+ results[test_table1].should include({:name => 'Justin', :id => '2'})
187
+ end
188
+
189
+ it 'performs BatchGetItem with one ranged key' do
190
+ Dynamoid::Adapter.put_item(test_table3, {:id => '1', :name => 'Josh', :range => 1.0})
191
+ Dynamoid::Adapter.put_item(test_table3, {:id => '2', :name => 'Justin', :range => 2.0})
192
+
193
+ results = Dynamoid::Adapter.batch_get_item(test_table3 => [['1', 1.0]])
194
+ results.size.should == 1
195
+ results[test_table3].should include({:name => 'Josh', :id => '1', :range => 1.0})
196
+ end
197
+
198
+ it 'performs BatchGetItem with multiple ranged keys' do
199
+ Dynamoid::Adapter.put_item(test_table3, {:id => '1', :name => 'Josh', :range => 1.0})
200
+ Dynamoid::Adapter.put_item(test_table3, {:id => '2', :name => 'Justin', :range => 2.0})
201
+
202
+ results = Dynamoid::Adapter.batch_get_item(test_table3 => [['1', 1.0],['2', 2.0]])
203
+ results.size.should == 1
204
+ results[test_table3].should include({:name => 'Josh', :id => '1', :range => 1.0})
205
+ results[test_table3].should include({:name => 'Justin', :id => '2', :range => 2.0})
206
+ end
207
+
208
+ # BatchDeleteItem
209
+ it "performs BatchDeleteItem with singular keys" do
210
+ Dynamoid::Adapter.put_item(test_table1, {:id => '1', :name => 'Josh'})
211
+ Dynamoid::Adapter.put_item(test_table2, {:id => '1', :name => 'Justin'})
212
+
213
+ Dynamoid::Adapter.batch_delete_item(test_table1 => ['1'], test_table2 => ['1'])
214
+
215
+ results = Dynamoid::Adapter.batch_get_item(test_table1 => '1', test_table2 => '1')
216
+ results.size.should == 0
217
+
218
+ results[test_table1].should_not include({:name => 'Josh', :id => '1'})
219
+ results[test_table2].should_not include({:name => 'Justin', :id => '1'})
220
+ end
221
+
222
+ it "performs BatchDeleteItem with multiple keys" do
223
+ Dynamoid::Adapter.put_item(test_table1, {:id => '1', :name => 'Josh'})
224
+ Dynamoid::Adapter.put_item(test_table1, {:id => '2', :name => 'Justin'})
225
+
226
+ Dynamoid::Adapter.batch_delete_item(test_table1 => ['1', '2'])
227
+
228
+ results = Dynamoid::Adapter.batch_get_item(test_table1 => ['1', '2'])
229
+ results.size.should == 0
230
+
231
+ results[test_table1].should_not include({:name => 'Josh', :id => '1'})
232
+ results[test_table1].should_not include({:name => 'Justin', :id => '2'})
233
+ end
234
+
235
+ it 'performs BatchDeleteItem with one ranged key' do
236
+ Dynamoid::Adapter.put_item(test_table3, {:id => '1', :name => 'Josh', :range => 1.0})
237
+ Dynamoid::Adapter.put_item(test_table3, {:id => '2', :name => 'Justin', :range => 2.0})
238
+
239
+ Dynamoid::Adapter.batch_delete_item(test_table3 => [['1', 1.0]])
240
+ results = Dynamoid::Adapter.batch_get_item(test_table3 => [['1', 1.0]])
241
+ results.size.should == 0
242
+
243
+ results[test_table3].should_not include({:name => 'Josh', :id => '1', :range => 1.0})
244
+ end
245
+
246
+ it 'performs BatchDeleteItem with multiple ranged keys' do
247
+ Dynamoid::Adapter.put_item(test_table3, {:id => '1', :name => 'Josh', :range => 1.0})
248
+ Dynamoid::Adapter.put_item(test_table3, {:id => '2', :name => 'Justin', :range => 2.0})
249
+
250
+ Dynamoid::Adapter.batch_delete_item(test_table3 => [['1', 1.0],['2', 2.0]])
251
+ results = Dynamoid::Adapter.batch_get_item(test_table3 => [['1', 1.0],['2', 2.0]])
252
+ results.size.should == 0
253
+
254
+ results[test_table3].should_not include({:name => 'Josh', :id => '1', :range => 1.0})
255
+ results[test_table3].should_not include({:name => 'Justin', :id => '2', :range => 2.0})
256
+ end
257
+
258
+ # ListTables
259
+ it 'performs ListTables' do
260
+ #Force creation of the tables
261
+ test_table1; test_table2; test_table3; test_table4
262
+
263
+ Dynamoid::Adapter.list_tables.should include test_table1
264
+ Dynamoid::Adapter.list_tables.should include test_table2
265
+ end
266
+
267
+ # Query
268
+ it 'performs query on a table and returns items' do
269
+ Dynamoid::Adapter.put_item(test_table1, {:id => '1', :name => 'Josh'})
270
+
271
+ Dynamoid::Adapter.query(test_table1, :hash_value => '1').first.should == { :id=> '1', :name=>"Josh" }
272
+ end
273
+
274
+ it 'performs query on a table and returns items if there are multiple items' do
275
+ Dynamoid::Adapter.put_item(test_table1, {:id => '1', :name => 'Josh'})
276
+ Dynamoid::Adapter.put_item(test_table1, {:id => '2', :name => 'Justin'})
277
+
278
+ Dynamoid::Adapter.query(test_table1, :hash_value => '1').first.should == { :id=> '1', :name=>"Josh" }
279
+ end
280
+
281
+ it_behaves_like 'range queries'
282
+
283
+ # Scan
284
+ it 'performs scan on a table and returns items' do
285
+ Dynamoid::Adapter.put_item(test_table1, {:id => '1', :name => 'Josh'})
286
+
287
+ Dynamoid::Adapter.scan(test_table1, :name => 'Josh').to_a.should == [{ :id=> '1', :name=>"Josh" }]
288
+ end
289
+
290
+ it 'performs scan on a table and returns items if there are multiple items but only one match' do
291
+ Dynamoid::Adapter.put_item(test_table1, {:id => '1', :name => 'Josh'})
292
+ Dynamoid::Adapter.put_item(test_table1, {:id => '2', :name => 'Justin'})
293
+
294
+ Dynamoid::Adapter.scan(test_table1, :name => 'Josh').to_a.should == [{ :id=> '1', :name=>"Josh" }]
295
+ end
296
+
297
+ it 'performs scan on a table and returns multiple items if there are multiple matches' do
298
+ Dynamoid::Adapter.put_item(test_table1, {:id => '1', :name => 'Josh'})
299
+ Dynamoid::Adapter.put_item(test_table1, {:id => '2', :name => 'Josh'})
300
+
301
+ Dynamoid::Adapter.scan(test_table1, :name => 'Josh').should include({:name=>"Josh", :id=>"2"}, {:name=>"Josh", :id=>"1"})
302
+ end
303
+
304
+ it 'performs scan on a table and returns all items if no criteria are specified' do
305
+ Dynamoid::Adapter.put_item(test_table1, {:id => '1', :name => 'Josh'})
306
+ Dynamoid::Adapter.put_item(test_table1, {:id => '2', :name => 'Josh'})
307
+
308
+ Dynamoid::Adapter.scan(test_table1, {}).should include({:name=>"Josh", :id=>"2"}, {:name=>"Josh", :id=>"1"})
309
+ end
310
+
311
+ it_behaves_like 'correct ordering'
312
+ end
313
+
314
+ context 'with a preexisting table with paritioning' do
315
+ before(:all) do
316
+ @previous_value = Dynamoid::Config.partitioning
317
+ Dynamoid::Config.partitioning = true
318
+ end
319
+
320
+ after(:all) do
321
+ Dynamoid::Config.partitioning = @previous_value
322
+ end
323
+
324
+ # Query
325
+ it 'performs query on a table and returns items' do
326
+ Dynamoid::Adapter.put_item(test_table1, {:id => '1.1', :name => 'Josh'})
327
+
328
+ Dynamoid::Adapter.query(test_table1, :hash_value => '1').first.should == { :id=> '1', :name=>"Josh" }
329
+ end
330
+
331
+ it 'performs query on a table and returns items if there are multiple items' do
332
+ Dynamoid::Adapter.put_item(test_table1, {:id => '1.1', :name => 'Josh'})
333
+ Dynamoid::Adapter.put_item(test_table1, {:id => '2.1', :name => 'Justin'})
334
+
335
+ Dynamoid::Adapter.query(test_table1, :hash_value => '1').first.should == { :id=> '1', :name=>"Josh" }
336
+ end
337
+
338
+ it_behaves_like 'range queries'
339
+
340
+ # Scan
341
+ it 'performs scan on a table and returns items' do
342
+ Dynamoid::Adapter.put_item(test_table1, {:id => '1.1', :name => 'Josh'})
343
+
344
+ Dynamoid::Adapter.scan(test_table1, :name => 'Josh').should == [{ :id=> '1', :name=>"Josh" }]
345
+ end
346
+
347
+ it 'performs scan on a table and returns items if there are multiple items but only one match' do
348
+ Dynamoid::Adapter.put_item(test_table1, {:id => '1.1', :name => 'Josh'})
349
+ Dynamoid::Adapter.put_item(test_table1, {:id => '2.1', :name => 'Justin'})
350
+
351
+ Dynamoid::Adapter.scan(test_table1, :name => 'Josh').should == [{ :id=> '1', :name=>"Josh" }]
352
+ end
353
+
354
+ it 'performs scan on a table and returns multiple items if there are multiple matches' do
355
+ Dynamoid::Adapter.put_item(test_table1, {:id => '1.1', :name => 'Josh'})
356
+ Dynamoid::Adapter.put_item(test_table1, {:id => '2.1', :name => 'Josh'})
357
+
358
+ Dynamoid::Adapter.scan(test_table1, :name => 'Josh').should include({:name=>"Josh", :id=>"2"}, {:name=>"Josh", :id=>"1"})
359
+ end
360
+
361
+ it 'performs scan on a table and returns all items if no criteria are specified' do
362
+ Dynamoid::Adapter.put_item(test_table1, {:id => '1.1', :name => 'Josh'})
363
+ Dynamoid::Adapter.put_item(test_table1, {:id => '2.1', :name => 'Josh'})
364
+
365
+ Dynamoid::Adapter.scan(test_table1, {}).should include({:name=>"Josh", :id=>"2"}, {:name=>"Josh", :id=>"1"})
366
+ end
367
+
368
+ it_behaves_like 'correct ordering'
369
+ end
370
+
371
+ # DescribeTable
372
+
373
+ # UpdateItem
374
+
375
+ # UpdateTable
376
+ end