dynamoid 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. data/.travis.yml +4 -0
  2. data/Gemfile +3 -2
  3. data/Gemfile.lock +40 -45
  4. data/README.markdown +55 -25
  5. data/Rakefile +31 -0
  6. data/VERSION +1 -1
  7. data/doc/Dynamoid.html +58 -42
  8. data/doc/Dynamoid/Adapter.html +666 -179
  9. data/doc/Dynamoid/Adapter/AwsSdk.html +752 -236
  10. data/doc/Dynamoid/Associations.html +28 -21
  11. data/doc/Dynamoid/Associations/Association.html +102 -49
  12. data/doc/Dynamoid/Associations/BelongsTo.html +28 -25
  13. data/doc/Dynamoid/Associations/ClassMethods.html +95 -52
  14. data/doc/Dynamoid/Associations/HasAndBelongsToMany.html +28 -25
  15. data/doc/Dynamoid/Associations/HasMany.html +28 -25
  16. data/doc/Dynamoid/Associations/HasOne.html +28 -25
  17. data/doc/Dynamoid/Associations/ManyAssociation.html +138 -94
  18. data/doc/Dynamoid/Associations/SingleAssociation.html +67 -38
  19. data/doc/Dynamoid/Components.html +60 -22
  20. data/doc/Dynamoid/Config.html +61 -44
  21. data/doc/Dynamoid/Config/Options.html +90 -61
  22. data/doc/Dynamoid/Criteria.html +28 -21
  23. data/doc/Dynamoid/Criteria/Chain.html +508 -100
  24. data/doc/Dynamoid/Criteria/ClassMethods.html +26 -19
  25. data/doc/Dynamoid/Dirty.html +424 -0
  26. data/doc/Dynamoid/Dirty/ClassMethods.html +174 -0
  27. data/doc/Dynamoid/Document.html +451 -84
  28. data/doc/Dynamoid/Document/ClassMethods.html +281 -102
  29. data/doc/Dynamoid/Errors.html +29 -22
  30. data/doc/Dynamoid/Errors/ConditionalCheckFailedException.html +141 -0
  31. data/doc/Dynamoid/Errors/DocumentNotValid.html +36 -25
  32. data/doc/Dynamoid/Errors/Error.html +27 -20
  33. data/doc/Dynamoid/Errors/InvalidField.html +27 -19
  34. data/doc/Dynamoid/Errors/InvalidQuery.html +131 -0
  35. data/doc/Dynamoid/Errors/MissingRangeKey.html +27 -19
  36. data/doc/Dynamoid/Fields.html +94 -77
  37. data/doc/Dynamoid/Fields/ClassMethods.html +166 -37
  38. data/doc/Dynamoid/Finders.html +28 -21
  39. data/doc/Dynamoid/Finders/ClassMethods.html +505 -78
  40. data/doc/Dynamoid/IdentityMap.html +492 -0
  41. data/doc/Dynamoid/IdentityMap/ClassMethods.html +534 -0
  42. data/doc/Dynamoid/Indexes.html +41 -28
  43. data/doc/Dynamoid/Indexes/ClassMethods.html +45 -29
  44. data/doc/Dynamoid/Indexes/Index.html +100 -62
  45. data/doc/Dynamoid/Middleware.html +115 -0
  46. data/doc/Dynamoid/Middleware/IdentityMap.html +264 -0
  47. data/doc/Dynamoid/Persistence.html +326 -85
  48. data/doc/Dynamoid/Persistence/ClassMethods.html +275 -109
  49. data/doc/Dynamoid/Validations.html +47 -31
  50. data/doc/_index.html +116 -71
  51. data/doc/class_list.html +13 -7
  52. data/doc/css/full_list.css +4 -2
  53. data/doc/css/style.css +60 -44
  54. data/doc/file.LICENSE.html +26 -19
  55. data/doc/file.README.html +152 -48
  56. data/doc/file_list.html +14 -8
  57. data/doc/frames.html +20 -5
  58. data/doc/index.html +152 -48
  59. data/doc/js/app.js +52 -43
  60. data/doc/js/full_list.js +14 -9
  61. data/doc/js/jquery.js +4 -16
  62. data/doc/method_list.html +446 -540
  63. data/doc/top-level-namespace.html +27 -20
  64. data/{Dynamoid.gemspec → dynamoid.gemspec} +21 -8
  65. data/lib/dynamoid/adapter.rb +11 -10
  66. data/lib/dynamoid/adapter/aws_sdk.rb +40 -19
  67. data/lib/dynamoid/components.rb +2 -1
  68. data/lib/dynamoid/criteria/chain.rb +29 -11
  69. data/lib/dynamoid/dirty.rb +6 -0
  70. data/lib/dynamoid/document.rb +34 -19
  71. data/lib/dynamoid/fields.rb +36 -30
  72. data/lib/dynamoid/finders.rb +7 -5
  73. data/lib/dynamoid/persistence.rb +37 -10
  74. data/spec/app/models/address.rb +2 -0
  75. data/spec/app/models/camel_case.rb +10 -0
  76. data/spec/app/models/car.rb +6 -0
  77. data/spec/app/models/nuclear_submarine.rb +5 -0
  78. data/spec/app/models/subscription.rb +2 -2
  79. data/spec/app/models/vehicle.rb +7 -0
  80. data/spec/dynamoid/adapter/aws_sdk_spec.rb +20 -11
  81. data/spec/dynamoid/adapter_spec.rb +67 -82
  82. data/spec/dynamoid/associations/association_spec.rb +30 -30
  83. data/spec/dynamoid/criteria/chain_spec.rb +56 -9
  84. data/spec/dynamoid/criteria_spec.rb +3 -0
  85. data/spec/dynamoid/dirty_spec.rb +8 -0
  86. data/spec/dynamoid/document_spec.rb +109 -47
  87. data/spec/dynamoid/fields_spec.rb +32 -3
  88. data/spec/dynamoid/finders_spec.rb +12 -0
  89. data/spec/dynamoid/persistence_spec.rb +73 -8
  90. data/spec/spec_helper.rb +1 -0
  91. data/spec/support/with_partitioning.rb +15 -0
  92. metadata +22 -9
@@ -1,17 +1,21 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
2
 
3
- describe "Dynamoid::Adapter" do
3
+ describe Dynamoid::Adapter do
4
4
 
5
+ def test_table; 'dynamoid_tests_TestTable'; end
6
+ let(:single_id){'123'}
7
+ let(:many_ids){%w(1 2)}
8
+
5
9
  before(:all) do
6
- Dynamoid::Adapter.create_table('dynamoid_tests_TestTable', :id) unless Dynamoid::Adapter.list_tables.include?('dynamoid_tests_TestTable')
10
+ described_class.create_table(test_table, :id) unless described_class.list_tables.include?(test_table)
7
11
  end
8
12
 
9
13
  it 'extends itself automatically' do
10
- lambda {Dynamoid::Adapter.list_tables}.should_not raise_error
14
+ lambda {described_class.list_tables}.should_not raise_error
11
15
  end
12
-
13
- it 'raises nomethod if we try a method that is not on the child' do
14
- lambda {Dynamoid::Adapter.foobar}.should raise_error
16
+
17
+ it 'raises NoMethodError if we try a method that is not on the child' do
18
+ lambda {described_class.foobar}.should raise_error(NoMethodError)
15
19
  end
16
20
 
17
21
  context 'without partioning' do
@@ -25,145 +29,126 @@ describe "Dynamoid::Adapter" do
25
29
  end
26
30
 
27
31
  it 'writes through the adapter' do
28
- Dynamoid::Adapter.expects(:put_item).with('dynamoid_tests_TestTable', {:id => '123'}, nil).returns(true)
29
-
30
- Dynamoid::Adapter.write('dynamoid_tests_TestTable', {:id => '123'})
32
+ described_class.expects(:put_item).with(test_table, {:id => single_id}, nil).returns(true)
33
+ described_class.write(test_table, {:id => single_id})
31
34
  end
32
35
 
33
36
  it 'reads through the adapter for one ID' do
34
- Dynamoid::Adapter.expects(:get_item).with('dynamoid_tests_TestTable', '123', {}).returns(true)
35
-
36
- Dynamoid::Adapter.read('dynamoid_tests_TestTable', '123')
37
+ described_class.expects(:get_item).with(test_table, single_id, {}).returns(true)
38
+ described_class.read(test_table, single_id)
37
39
  end
38
40
 
39
41
  it 'reads through the adapter for many IDs' do
40
- Dynamoid::Adapter.expects(:batch_get_item).with({'dynamoid_tests_TestTable' => ['1', '2']}).returns(true)
41
-
42
- Dynamoid::Adapter.read('dynamoid_tests_TestTable', ['1', '2'])
42
+ described_class.expects(:batch_get_item).with({test_table => many_ids}, {}).returns(true)
43
+ described_class.read(test_table, many_ids)
43
44
  end
44
-
45
- it 'delete through the adapter for one ID' do
46
- Dynamoid::Adapter.expects(:delete_item).with('dynamoid_tests_TestTable', '123', {}).returns(nil)
47
45
 
48
- Dynamoid::Adapter.delete('dynamoid_tests_TestTable', '123')
46
+ it 'delete through the adapter for one ID' do
47
+ described_class.expects(:delete_item).with(test_table, single_id, {}).returns(nil)
48
+ described_class.delete(test_table, single_id)
49
49
  end
50
-
51
- it 'deletes through the adapter for many IDs' do
52
- Dynamoid::Adapter.expects(:batch_delete_item).with({'dynamoid_tests_TestTable' => ['1', '2']}).returns(nil)
53
50
 
54
- Dynamoid::Adapter.delete('dynamoid_tests_TestTable', ['1', '2'])
51
+ it 'deletes through the adapter for many IDs' do
52
+ described_class.expects(:batch_delete_item).with({test_table => many_ids}).returns(nil)
53
+ described_class.delete(test_table, many_ids)
55
54
  end
56
-
57
- it 'reads through the adapter for one ID and a range key' do
58
- Dynamoid::Adapter.expects(:get_item).with('dynamoid_tests_TestTable', '123', :range_key => 2.0).returns(true)
59
55
 
60
- Dynamoid::Adapter.read('dynamoid_tests_TestTable', '123', :range_key => 2.0)
56
+ it 'reads through the adapter for one ID and a range key' do
57
+ described_class.expects(:get_item).with(test_table, single_id, :range_key => 2.0).returns(true)
58
+ described_class.read(test_table, single_id, :range_key => 2.0)
61
59
  end
62
-
63
- it 'reads through the adapter for many IDs and a range key' do
64
- Dynamoid::Adapter.expects(:batch_get_item).with({'dynamoid_tests_TestTable' => [['1', 2.0], ['2', 2.0]]}).returns(true)
65
60
 
66
- Dynamoid::Adapter.read('dynamoid_tests_TestTable', ['1', '2'], :range_key => 2.0)
61
+ it 'reads through the adapter for many IDs and a range key' do
62
+ described_class.expects(:batch_get_item).with({test_table => [['1', 2.0], ['2', 2.0]]}, {}).returns(true)
63
+ described_class.read(test_table, many_ids, :range_key => 2.0)
67
64
  end
68
-
69
- it 'deletes through the adapter for one ID and a range key' do
70
- Dynamoid::Adapter.expects(:delete_item).with('dynamoid_tests_TestTable', '123', :range_key => 2.0).returns(nil)
71
65
 
72
- Dynamoid::Adapter.delete('dynamoid_tests_TestTable', '123', :range_key => 2.0)
66
+ it 'deletes through the adapter for one ID and a range key' do
67
+ described_class.expects(:delete_item).with(test_table, single_id, :range_key => 2.0).returns(nil)
68
+ described_class.delete(test_table, single_id, :range_key => 2.0)
73
69
  end
74
-
75
- it 'deletes through the adapter for many IDs and a range key' do
76
- Dynamoid::Adapter.expects(:batch_delete_item).with({'dynamoid_tests_TestTable' => [['1', 2.0], ['2', 2.0]]}).returns(nil)
77
70
 
78
- Dynamoid::Adapter.delete('dynamoid_tests_TestTable', ['1', '2'], :range_key => [2.0,2.0])
71
+ it 'deletes through the adapter for many IDs and a range key' do
72
+ described_class.expects(:batch_delete_item).with({test_table => [['1', 2.0], ['2', 2.0]]}).returns(nil)
73
+ described_class.delete(test_table, many_ids, :range_key => [2.0,2.0])
79
74
  end
80
75
  end
81
76
 
82
- context 'with partitioning' do
83
- before(:all) do
84
- @previous_value = Dynamoid::Config.partitioning
85
- Dynamoid::Config.partitioning = true
86
- end
87
-
88
- after(:all) do
89
- Dynamoid::Config.partitioning = @previous_value
90
- end
91
-
77
+ configured_with 'partitioning' do
78
+ let(:partition_range){0...Dynamoid::Config.partition_size}
79
+
92
80
  it 'writes through the adapter' do
93
81
  Random.expects(:rand).with(Dynamoid::Config.partition_size).once.returns(0)
94
- Dynamoid::Adapter.write('dynamoid_tests_TestTable', {:id => 'testid'})
95
-
96
- Dynamoid::Adapter.get_item('dynamoid_tests_TestTable', 'testid.0')[:id].should == 'testid.0'
97
- Dynamoid::Adapter.get_item('dynamoid_tests_TestTable', 'testid.0')[:updated_at].should_not be_nil
82
+ described_class.write(test_table, {:id => 'testid'})
83
+
84
+ described_class.get_item(test_table, 'testid.0')[:id].should == 'testid.0'
85
+ described_class.get_item(test_table, 'testid.0')[:updated_at].should_not be_nil
98
86
  end
99
87
 
100
88
  it 'reads through the adapter for one ID' do
101
- Dynamoid::Adapter.expects(:batch_get_item).with('dynamoid_tests_TestTable' => (0...Dynamoid::Config.partition_size).collect{|n| "123.#{n}"}).returns({})
102
-
103
- Dynamoid::Adapter.read('dynamoid_tests_TestTable', '123')
89
+ described_class.expects(:batch_get_item).with({test_table => partition_range.map{|n| "123.#{n}"}}, {}).returns({})
90
+ described_class.read(test_table, single_id)
104
91
  end
105
92
 
106
93
  it 'reads through the adapter for many IDs' do
107
- Dynamoid::Adapter.expects(:batch_get_item).with('dynamoid_tests_TestTable' => (0...Dynamoid::Config.partition_size).collect{|n| "1.#{n}"} + (0...Dynamoid::Config.partition_size).collect{|n| "2.#{n}"}).returns({})
108
-
109
- Dynamoid::Adapter.read('dynamoid_tests_TestTable', ['1', '2'])
94
+ described_class.expects(:batch_get_item).with({test_table => partition_range.map{|n| "1.#{n}"} + partition_range.map{|n| "2.#{n}"}}, {}).returns({})
95
+ described_class.read(test_table, many_ids)
110
96
  end
111
97
 
112
98
  it 'reads through the adapter for one ID and a range key' do
113
- Dynamoid::Adapter.expects(:batch_get_item).with('dynamoid_tests_TestTable' => (0...Dynamoid::Config.partition_size).collect{|n| ["123.#{n}", 2.0]}).returns({})
114
-
115
- Dynamoid::Adapter.read('dynamoid_tests_TestTable', '123', :range_key => 2.0)
99
+ described_class.expects(:batch_get_item).with({test_table => partition_range.map{|n| ["123.#{n}", 2.0]}}, {}).returns({})
100
+ described_class.read(test_table, single_id, :range_key => 2.0)
116
101
  end
117
102
 
118
103
  it 'reads through the adapter for many IDs and a range key' do
119
- Dynamoid::Adapter.expects(:batch_get_item).with('dynamoid_tests_TestTable' => (0...Dynamoid::Config.partition_size).collect{|n| ["1.#{n}", 2.0]} + (0...Dynamoid::Config.partition_size).collect{|n| ["2.#{n}", 2.0]}).returns({})
120
-
121
- Dynamoid::Adapter.read('dynamoid_tests_TestTable', ['1', '2'], :range_key => 2.0)
104
+ described_class.expects(:batch_get_item).with({test_table => partition_range.map{|n| ["1.#{n}", 2.0]} + partition_range.map{|n| ["2.#{n}", 2.0]}}, {}).returns({})
105
+ described_class.read(test_table, many_ids, :range_key => 2.0)
122
106
  end
123
107
 
124
108
  it 'returns an ID with all partitions' do
125
- Dynamoid::Adapter.id_with_partitions('1').should =~ (0...Dynamoid::Config.partition_size).collect{|n| "1.#{n}"}
109
+ described_class.id_with_partitions('1').should =~ partition_range.map{|n| "1.#{n}"}
126
110
  end
127
111
 
128
112
  it 'returns an ID and range key with all partitions' do
129
- Dynamoid::Adapter.id_with_partitions([['1', 1.0]]).should =~ (0...Dynamoid::Config.partition_size).collect{|n| ["1.#{n}", 1.0]}
113
+ described_class.id_with_partitions([['1', 1.0]]).should =~ partition_range.map{|n| ["1.#{n}", 1.0]}
130
114
  end
131
115
 
132
116
  it 'returns a result for one partitioned element' do
133
117
  @time = DateTime.now
134
- @array =[{:id => '1.0', :updated_at => @time - 6.hours}, {:id => '1.1', :updated_at => @time - 3.hours}, {:id => '1.2', :updated_at => @time - 1.hour}, {:id => '1.3', :updated_at => @time - 6.hours}, {:id => '2.0', :updated_at => @time}]
118
+ @array =[{:id => '1.0', :updated_at => @time - 6.hours},
119
+ {:id => '1.1', :updated_at => @time - 3.hours},
120
+ {:id => '1.2', :updated_at => @time - 1.hour},
121
+ {:id => '1.3', :updated_at => @time - 6.hours},
122
+ {:id => '2.0', :updated_at => @time}]
135
123
 
136
- Dynamoid::Adapter.result_for_partition(@array,"dynamoid_tests_TestTable").should =~ [{:id => '1', :updated_at => @time - 1.hour}, {:id => '2', :updated_at => @time}]
124
+ described_class.result_for_partition(@array,test_table).should =~ [{:id => '1', :updated_at => @time - 1.hour},
125
+ {:id => '2', :updated_at => @time}]
137
126
  end
138
127
 
139
128
  it 'returns a valid original id and partition number' do
140
129
  @id = "12345.387327.-sdf3"
141
130
  @partition_number = "4"
142
- Dynamoid::Adapter.get_original_id_and_partition("#{@id}.#{@partition_number}").should == [@id, @partition_number]
131
+ described_class.get_original_id_and_partition("#{@id}.#{@partition_number}").should == [@id, @partition_number]
143
132
  end
144
133
 
145
134
  it 'delete through the adapter for one ID' do
146
- Dynamoid::Adapter.expects(:batch_delete_item).with('dynamoid_tests_TestTable' => (0...Dynamoid::Config.partition_size).collect{|n| "123.#{n}"}).returns(nil)
147
-
148
- Dynamoid::Adapter.delete('dynamoid_tests_TestTable', '123')
135
+ described_class.expects(:batch_delete_item).with(test_table => partition_range.map{|n| "123.#{n}"}).returns(nil)
136
+ described_class.delete(test_table, single_id)
149
137
  end
150
138
 
151
139
  it 'deletes through the adapter for many IDs' do
152
- Dynamoid::Adapter.expects(:batch_delete_item).with('dynamoid_tests_TestTable' => (0...Dynamoid::Config.partition_size).collect{|n| "1.#{n}"} + (0...Dynamoid::Config.partition_size).collect{|n| "2.#{n}"}).returns(nil)
153
-
154
- Dynamoid::Adapter.delete('dynamoid_tests_TestTable', ['1', '2'])
140
+ described_class.expects(:batch_delete_item).with(test_table => partition_range.map{|n| "1.#{n}"} + partition_range.map{|n| "2.#{n}"}).returns(nil)
141
+ described_class.delete(test_table, many_ids)
155
142
  end
156
143
 
157
144
  it 'deletes through the adapter for one ID and a range key' do
158
- Dynamoid::Adapter.expects(:batch_delete_item).with('dynamoid_tests_TestTable' => (0...Dynamoid::Config.partition_size).collect{|n| ["123.#{n}", 2.0]}).returns(nil)
159
-
160
- Dynamoid::Adapter.delete('dynamoid_tests_TestTable', '123', :range_key => 2.0)
145
+ described_class.expects(:batch_delete_item).with(test_table => partition_range.map{|n| ["123.#{n}", 2.0]}).returns(nil)
146
+ described_class.delete(test_table, single_id, :range_key => 2.0)
161
147
  end
162
148
 
163
149
  it 'deletes through the adapter for many IDs and a range key' do
164
- Dynamoid::Adapter.expects(:batch_delete_item).with('dynamoid_tests_TestTable' => (0...Dynamoid::Config.partition_size).collect{|n| ["1.#{n}", 2.0]} + (0...Dynamoid::Config.partition_size).collect{|n| ["2.#{n}", 2.0]}).returns(nil)
165
-
166
- Dynamoid::Adapter.delete('dynamoid_tests_TestTable', ['1', '2'], :range_key => [2.0,2.0])
150
+ described_class.expects(:batch_delete_item).with(test_table => partition_range.map{|n| ["1.#{n}", 2.0]} + partition_range.map{|n| ["2.#{n}", 2.0]}).returns(nil)
151
+ described_class.delete(test_table, many_ids, :range_key => [2.0,2.0])
167
152
  end
168
153
  end
169
154
 
@@ -6,30 +6,30 @@ describe "Dynamoid::Associations::Association" do
6
6
  Subscription.create_table
7
7
  @magazine = Magazine.create
8
8
  end
9
-
9
+
10
10
  it 'returns an empty array if there are no associations' do
11
11
  @magazine.subscriptions.should be_empty
12
12
  end
13
-
13
+
14
14
  it 'adds an item to an association' do
15
15
  @subscription = Subscription.create
16
-
16
+
17
17
  @magazine.subscriptions << @subscription
18
18
  @magazine.subscriptions.size.should == 1
19
19
  @magazine.subscriptions.should include @subscription
20
20
  end
21
-
21
+
22
22
  it 'deletes an item from an association' do
23
23
  @subscription = Subscription.create
24
24
  @magazine.subscriptions << @subscription
25
-
25
+
26
26
  @magazine.subscriptions.delete(@subscription)
27
27
  @magazine.subscriptions.size.should == 0
28
28
  end
29
-
29
+
30
30
  it 'creates an item from an association' do
31
31
  @subscription = @magazine.subscriptions.create
32
-
32
+
33
33
  @subscription.class.should == Subscription
34
34
  @magazine.subscriptions.size.should == 1
35
35
  @magazine.subscriptions.should include @subscription
@@ -38,7 +38,7 @@ describe "Dynamoid::Associations::Association" do
38
38
  it 'returns the number of items in the association' do
39
39
  @magazine.subscriptions.create
40
40
  @magazine.subscriptions.size.should == 1
41
-
41
+
42
42
  @second = @magazine.subscriptions.create
43
43
  @magazine.subscriptions.size.should == 2
44
44
 
@@ -48,61 +48,61 @@ describe "Dynamoid::Associations::Association" do
48
48
  @magazine.subscriptions = []
49
49
  @magazine.subscriptions.size.should == 0
50
50
  end
51
-
51
+
52
52
  it 'assigns directly via the equals operator' do
53
53
  @subscription = Subscription.create
54
54
  @magazine.subscriptions = [@subscription]
55
-
55
+
56
56
  @magazine.subscriptions.should == [@subscription]
57
57
  end
58
-
58
+
59
59
  it 'assigns directly via the equals operator and reflects to the target association' do
60
60
  @subscription = Subscription.create
61
61
  @magazine.subscriptions = [@subscription]
62
-
62
+
63
63
  @subscription.magazine.should == @magazine
64
64
  end
65
-
65
+
66
66
  it 'does not assign reflection association if the reflection association does not exist' do
67
67
  @sponsor = Sponsor.create
68
-
68
+
69
69
  @subscription = @sponsor.subscriptions.create
70
70
  @subscription.should_not respond_to :sponsor
71
71
  @subscription.should_not respond_to :sponsors
72
72
  @subscription.should_not respond_to :sponsors_ids
73
73
  @subscription.should_not respond_to :sponsor_ids
74
74
  end
75
-
75
+
76
76
  it 'deletes all items from the association' do
77
77
  @magazine.subscriptions << Subscription.create
78
78
  @magazine.subscriptions << Subscription.create
79
79
  @magazine.subscriptions << Subscription.create
80
-
80
+
81
81
  @magazine.subscriptions.size.should == 3
82
-
82
+
83
83
  @magazine.subscriptions = nil
84
84
  @magazine.subscriptions.size.should == 0
85
85
  end
86
-
86
+
87
87
  it 'uses where inside an association and returns a result' do
88
88
  @included_subscription = @magazine.subscriptions.create(:length => 10)
89
89
  @unincldued_subscription = @magazine.subscriptions.create(:length => 8)
90
-
90
+
91
91
  @magazine.subscriptions.where(:length => 10).all.should == [@included_subscription]
92
92
  end
93
-
93
+
94
94
  it 'uses where inside an association and returns an empty set' do
95
95
  @included_subscription = @magazine.subscriptions.create(:length => 10)
96
96
  @unincldued_subscription = @magazine.subscriptions.create(:length => 8)
97
-
97
+
98
98
  @magazine.subscriptions.where(:length => 6).all.should be_empty
99
99
  end
100
-
100
+
101
101
  it 'includes enumerable' do
102
102
  @subscription1 = @magazine.subscriptions.create
103
103
  @subscription2 = @magazine.subscriptions.create
104
104
  @subscription3 = @magazine.subscriptions.create
105
-
105
+
106
106
  @magazine.subscriptions.collect(&:id).should =~ [@subscription1.id, @subscription2.id, @subscription3.id]
107
107
  end
108
108
 
@@ -134,41 +134,41 @@ describe "Dynamoid::Associations::Association" do
134
134
  #
135
135
  # @magazine.subscriptions.instance_eval { [first, last] }.should == [@subscription1, @subscription3]
136
136
  #end
137
-
137
+
138
138
  it 'replaces existing associations when using the setter' do
139
139
  @subscription1 = @magazine.subscriptions.create
140
140
  @subscription2 = @magazine.subscriptions.create
141
141
  @subscription3 = Subscription.create
142
-
142
+
143
143
  @subscription1.reload.magazine_ids.should_not be_nil
144
144
  @subscription2.reload.magazine_ids.should_not be_nil
145
145
 
146
146
  @magazine.subscriptions = @subscription3
147
147
  @magazine.subscriptions_ids.should == Set[@subscription3.id]
148
-
148
+
149
149
  @subscription1.reload.magazine_ids.should be_nil
150
150
  @subscription2.reload.magazine_ids.should be_nil
151
151
  @subscription3.reload.magazine_ids.should == Set[@magazine.id]
152
152
  end
153
-
153
+
154
154
  it 'destroys all objects and removes them from the association' do
155
155
  @subscription1 = @magazine.subscriptions.create
156
156
  @subscription2 = @magazine.subscriptions.create
157
157
  @subscription3 = @magazine.subscriptions.create
158
158
 
159
159
  @magazine.subscriptions.destroy_all
160
-
160
+
161
161
  @magazine.subscriptions.should be_empty
162
162
  Subscription.all.should be_empty
163
163
  end
164
-
164
+
165
165
  it 'deletes all objects and removes them from the association' do
166
166
  @subscription1 = @magazine.subscriptions.create
167
167
  @subscription2 = @magazine.subscriptions.create
168
168
  @subscription3 = @magazine.subscriptions.create
169
169
 
170
170
  @magazine.subscriptions.delete_all
171
-
171
+
172
172
  @magazine.subscriptions.should be_empty
173
173
  Subscription.all.should be_empty
174
174
  end
@@ -19,6 +19,11 @@ describe "Dynamoid::Associations::Chain" do
19
19
  @chain.send(:index).should == User.indexes[[:email, :name]]
20
20
  end
21
21
 
22
+ it 'makes string symbol for query keys' do
23
+ @chain.query = {'name' => 'Josh'}
24
+ @chain.send(:index).should == User.indexes[[:name]]
25
+ end
26
+
22
27
  it 'finds matching index for a range query' do
23
28
  @chain.query = {"created_at.gt" => @time - 1.day}
24
29
  @chain.send(:index).should == User.indexes[[:created_at]]
@@ -51,26 +56,26 @@ describe "Dynamoid::Associations::Chain" do
51
56
 
52
57
  it 'finds records with an index' do
53
58
  @chain.query = {:name => 'Josh'}
54
- @chain.send(:records_with_index).should == [@user]
59
+ @chain.send(:records_with_index).should == @user
55
60
 
56
61
  @chain.query = {:email => 'josh@joshsymonds.com'}
57
- @chain.send(:records_with_index).should == [@user]
62
+ @chain.send(:records_with_index).should == @user
58
63
 
59
64
  @chain.query = {:name => 'Josh', :email => 'josh@joshsymonds.com'}
60
- @chain.send(:records_with_index).should == [@user]
65
+ @chain.send(:records_with_index).should == @user
61
66
  end
62
67
 
63
68
  it 'returns records with an index for a ranged query' do
64
69
  @chain.query = {:name => 'Josh', "created_at.gt" => @time - 1.day}
65
- @chain.send(:records_with_index).should == [@user]
70
+ @chain.send(:records_with_index).should == @user
66
71
 
67
72
  @chain.query = {:name => 'Josh', "created_at.lt" => @time + 1.day}
68
- @chain.send(:records_with_index).should == [@user]
73
+ @chain.send(:records_with_index).should == @user
69
74
  end
70
75
 
71
76
  it 'finds records without an index' do
72
77
  @chain.query = {:password => 'Test123'}
73
- @chain.send(:records_without_index).should == [@user]
78
+ @chain.send(:records_without_index).to_a.should == [@user]
74
79
  end
75
80
 
76
81
  it "doesn't crash if it finds a nil id in the index" do
@@ -94,14 +99,38 @@ describe "Dynamoid::Associations::Chain" do
94
99
  @chain.collect {|u| u.name}.should == ['Josh']
95
100
  end
96
101
 
97
- it 'finds range querys' do
102
+ it 'uses a range query when only a hash key or range key is specified in query' do
103
+ # Primary key is [hash_key].
104
+ @chain = Dynamoid::Criteria::Chain.new(Address)
105
+ @chain.query = {}
106
+ @chain.send(:range?).should be_false
107
+
108
+ @chain = Dynamoid::Criteria::Chain.new(Address)
109
+ @chain.query = { :id => 'test' }
110
+ @chain.send(:range?).should be_true
111
+
112
+ @chain = Dynamoid::Criteria::Chain.new(Address)
113
+ @chain.query = { :id => 'test', :city => 'Bucharest' }
114
+ @chain.send(:range?).should be_false
115
+
116
+ # Primary key is [hash_key, range_key].
117
+ @chain = Dynamoid::Criteria::Chain.new(Tweet)
118
+ @chain.query = { }
119
+ @chain.send(:range?).should be_false
120
+
98
121
  @chain = Dynamoid::Criteria::Chain.new(Tweet)
99
122
  @chain.query = { :tweet_id => 'test' }
100
123
  @chain.send(:range?).should be_true
101
124
 
125
+ @chain.query = {:tweet_id => 'test', :msg => 'hai'}
126
+ @chain.send(:range?).should be_false
127
+
102
128
  @chain.query = {:tweet_id => 'test', :group => 'xx'}
103
129
  @chain.send(:range?).should be_true
104
130
 
131
+ @chain.query = {:tweet_id => 'test', :group => 'xx', :msg => 'hai'}
132
+ @chain.send(:range?).should be_false
133
+
105
134
  @chain.query = { :group => 'xx' }
106
135
  @chain.send(:range?).should be_false
107
136
 
@@ -119,7 +148,7 @@ describe "Dynamoid::Associations::Chain" do
119
148
 
120
149
  it 'finds tweets with a simple range query' do
121
150
  @chain.query = { :tweet_id => "x" }
122
- @chain.send(:records_with_range).size.should == 2
151
+ @chain.send(:records_with_range).to_a.size.should == 2
123
152
  @chain.all.size.should == 2
124
153
  @chain.limit(1).size.should == 1
125
154
  end
@@ -133,7 +162,7 @@ describe "Dynamoid::Associations::Chain" do
133
162
  it 'finds one specific tweet' do
134
163
  @chain = Dynamoid::Criteria::Chain.new(Tweet)
135
164
  @chain.query = { :tweet_id => "xx", :group => "two" }
136
- @chain.send(:records_with_range).should == [@tweet3]
165
+ @chain.send(:records_with_range).to_a.should == [@tweet3]
137
166
  end
138
167
  end
139
168
 
@@ -160,4 +189,22 @@ describe "Dynamoid::Associations::Chain" do
160
189
  @chain.consistent.all.size.should == 0
161
190
  end
162
191
  end
192
+
193
+ context 'batch queries' do
194
+ before do
195
+ @tweets = (1..4).map{|count| Tweet.create(:tweet_id => count.to_s, :group => (count % 2).to_s)}
196
+ @chain = Dynamoid::Criteria::Chain.new(Tweet)
197
+ end
198
+
199
+ it 'returns all results' do
200
+ @chain.batch(2).all.to_a.size.should == @tweets.size
201
+ end
202
+
203
+ it 'throws exception if partitioning is used with batching' do
204
+ previous_value = Dynamoid::Config.partitioning
205
+ Dynamoid::Config.partitioning = true
206
+ expect { @chain.batch(2) }.to raise_error
207
+ Dynamoid::Config.partitioning = previous_value
208
+ end
209
+ end
163
210
  end