dynamoid 0.6.1 → 0.7.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.
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