seomoz-ripple 1.0.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +20 -0
- data/Guardfile +15 -0
- data/Rakefile +88 -0
- data/lib/rails/generators/ripple/configuration/configuration_generator.rb +13 -0
- data/lib/rails/generators/ripple/configuration/templates/ripple.yml +24 -0
- data/lib/rails/generators/ripple/js/js_generator.rb +13 -0
- data/lib/rails/generators/ripple/js/templates/js/contrib.js +63 -0
- data/lib/rails/generators/ripple/js/templates/js/iso8601.js +76 -0
- data/lib/rails/generators/ripple/js/templates/js/ripple.js +132 -0
- data/lib/rails/generators/ripple/model/model_generator.rb +20 -0
- data/lib/rails/generators/ripple/model/templates/model.rb +10 -0
- data/lib/rails/generators/ripple/observer/observer_generator.rb +16 -0
- data/lib/rails/generators/ripple/observer/templates/observer.rb +4 -0
- data/lib/rails/generators/ripple/test/templates/test_server.rb +46 -0
- data/lib/rails/generators/ripple/test/test_generator.rb +39 -0
- data/lib/rails/generators/ripple_generator.rb +78 -0
- data/lib/ripple.rb +79 -0
- data/lib/ripple/associations.rb +356 -0
- data/lib/ripple/associations/embedded.rb +35 -0
- data/lib/ripple/associations/instantiators.rb +26 -0
- data/lib/ripple/associations/linked.rb +65 -0
- data/lib/ripple/associations/many.rb +38 -0
- data/lib/ripple/associations/many_embedded_proxy.rb +38 -0
- data/lib/ripple/associations/many_linked_proxy.rb +66 -0
- data/lib/ripple/associations/many_reference_proxy.rb +93 -0
- data/lib/ripple/associations/many_stored_key_proxy.rb +76 -0
- data/lib/ripple/associations/one.rb +20 -0
- data/lib/ripple/associations/one_embedded_proxy.rb +35 -0
- data/lib/ripple/associations/one_key_proxy.rb +58 -0
- data/lib/ripple/associations/one_linked_proxy.rb +22 -0
- data/lib/ripple/associations/one_stored_key_proxy.rb +43 -0
- data/lib/ripple/associations/proxy.rb +118 -0
- data/lib/ripple/attribute_methods.rb +118 -0
- data/lib/ripple/attribute_methods/dirty.rb +50 -0
- data/lib/ripple/attribute_methods/query.rb +34 -0
- data/lib/ripple/attribute_methods/read.rb +26 -0
- data/lib/ripple/attribute_methods/write.rb +25 -0
- data/lib/ripple/callbacks.rb +74 -0
- data/lib/ripple/conflict/basic_resolver.rb +82 -0
- data/lib/ripple/conflict/document_hooks.rb +20 -0
- data/lib/ripple/conflict/resolver.rb +71 -0
- data/lib/ripple/conflict/test_helper.rb +33 -0
- data/lib/ripple/conversion.rb +28 -0
- data/lib/ripple/core_ext.rb +2 -0
- data/lib/ripple/core_ext/casting.rb +148 -0
- data/lib/ripple/core_ext/object.rb +8 -0
- data/lib/ripple/document.rb +104 -0
- data/lib/ripple/document/bucket_access.rb +25 -0
- data/lib/ripple/document/finders.rb +131 -0
- data/lib/ripple/document/key.rb +43 -0
- data/lib/ripple/document/link.rb +30 -0
- data/lib/ripple/document/persistence.rb +115 -0
- data/lib/ripple/embedded_document.rb +64 -0
- data/lib/ripple/embedded_document/around_callbacks.rb +18 -0
- data/lib/ripple/embedded_document/finders.rb +26 -0
- data/lib/ripple/embedded_document/persistence.rb +77 -0
- data/lib/ripple/i18n.rb +2 -0
- data/lib/ripple/inspection.rb +32 -0
- data/lib/ripple/locale/en.yml +21 -0
- data/lib/ripple/nested_attributes.rb +265 -0
- data/lib/ripple/observable.rb +28 -0
- data/lib/ripple/properties.rb +73 -0
- data/lib/ripple/property_type_mismatch.rb +12 -0
- data/lib/ripple/railtie.rb +13 -0
- data/lib/ripple/serialization.rb +84 -0
- data/lib/ripple/timestamps.rb +27 -0
- data/lib/ripple/translation.rb +14 -0
- data/lib/ripple/validations.rb +67 -0
- data/lib/ripple/validations/associated_validator.rb +43 -0
- data/ripple.gemspec +46 -0
- data/seomoz-ripple.gemspec +46 -0
- data/spec/fixtures/config.yml +8 -0
- data/spec/integration/ripple/associations_spec.rb +220 -0
- data/spec/integration/ripple/conflict_resolution_spec.rb +293 -0
- data/spec/integration/ripple/nested_attributes_spec.rb +264 -0
- data/spec/integration/ripple/persistence_spec.rb +57 -0
- data/spec/integration/ripple/search_associations_spec.rb +42 -0
- data/spec/ripple/associations/many_embedded_proxy_spec.rb +122 -0
- data/spec/ripple/associations/many_linked_proxy_spec.rb +191 -0
- data/spec/ripple/associations/many_reference_proxy_spec.rb +170 -0
- data/spec/ripple/associations/many_stored_key_proxy_spec.rb +158 -0
- data/spec/ripple/associations/one_embedded_proxy_spec.rb +125 -0
- data/spec/ripple/associations/one_key_proxy_spec.rb +82 -0
- data/spec/ripple/associations/one_linked_proxy_spec.rb +91 -0
- data/spec/ripple/associations/one_stored_key_proxy_spec.rb +72 -0
- data/spec/ripple/associations/proxy_spec.rb +84 -0
- data/spec/ripple/associations_spec.rb +129 -0
- data/spec/ripple/attribute_methods/dirty_spec.rb +80 -0
- data/spec/ripple/attribute_methods_spec.rb +230 -0
- data/spec/ripple/bucket_access_spec.rb +25 -0
- data/spec/ripple/callbacks_spec.rb +176 -0
- data/spec/ripple/conflict/resolver_spec.rb +42 -0
- data/spec/ripple/conversion_spec.rb +22 -0
- data/spec/ripple/core_ext_spec.rb +103 -0
- data/spec/ripple/document/link_spec.rb +67 -0
- data/spec/ripple/document_spec.rb +96 -0
- data/spec/ripple/embedded_document/finders_spec.rb +29 -0
- data/spec/ripple/embedded_document/persistence_spec.rb +80 -0
- data/spec/ripple/embedded_document_spec.rb +84 -0
- data/spec/ripple/finders_spec.rb +217 -0
- data/spec/ripple/inspection_spec.rb +51 -0
- data/spec/ripple/key_spec.rb +30 -0
- data/spec/ripple/observable_spec.rb +121 -0
- data/spec/ripple/persistence_spec.rb +326 -0
- data/spec/ripple/properties_spec.rb +262 -0
- data/spec/ripple/ripple_spec.rb +71 -0
- data/spec/ripple/serialization_spec.rb +51 -0
- data/spec/ripple/timestamps_spec.rb +76 -0
- data/spec/ripple/validations/associated_validator_spec.rb +77 -0
- data/spec/ripple/validations_spec.rb +104 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/support/associations.rb +1 -0
- data/spec/support/associations/proxies.rb +17 -0
- data/spec/support/integration_setup.rb +11 -0
- data/spec/support/mocks.rb +4 -0
- data/spec/support/models.rb +4 -0
- data/spec/support/models/address.rb +12 -0
- data/spec/support/models/box.rb +13 -0
- data/spec/support/models/car.rb +16 -0
- data/spec/support/models/cardboard_box.rb +3 -0
- data/spec/support/models/clock.rb +12 -0
- data/spec/support/models/clock_observer.rb +3 -0
- data/spec/support/models/company.rb +23 -0
- data/spec/support/models/customer.rb +4 -0
- data/spec/support/models/driver.rb +6 -0
- data/spec/support/models/email.rb +4 -0
- data/spec/support/models/engine.rb +5 -0
- data/spec/support/models/family.rb +16 -0
- data/spec/support/models/favorite.rb +4 -0
- data/spec/support/models/invoice.rb +7 -0
- data/spec/support/models/late_invoice.rb +3 -0
- data/spec/support/models/ninja.rb +9 -0
- data/spec/support/models/note.rb +5 -0
- data/spec/support/models/page.rb +4 -0
- data/spec/support/models/paid_invoice.rb +4 -0
- data/spec/support/models/passenger.rb +6 -0
- data/spec/support/models/profile.rb +10 -0
- data/spec/support/models/seat.rb +5 -0
- data/spec/support/models/subscription.rb +27 -0
- data/spec/support/models/tasks.rb +14 -0
- data/spec/support/models/team.rb +11 -0
- data/spec/support/models/transactions.rb +17 -0
- data/spec/support/models/tree.rb +4 -0
- data/spec/support/models/user.rb +10 -0
- data/spec/support/models/wheel.rb +6 -0
- data/spec/support/models/widget.rb +22 -0
- data/spec/support/test_server.rb +18 -0
- data/spec/support/test_server.yml.example +2 -0
- metadata +362 -0
@@ -0,0 +1,191 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ripple::Associations::ManyLinkedProxy do
|
4
|
+
require 'support/models/tasks'
|
5
|
+
|
6
|
+
before :each do
|
7
|
+
@person = Person.new {|p| p.key = "riak-user" }
|
8
|
+
@task = Task.new {|t| t.key = "one" }
|
9
|
+
@other_task = Task.new {|t| t.key = "two" }
|
10
|
+
@third_task = Task.new {|t| t.key = "three" }
|
11
|
+
[@person, @task, @other_task].each do |doc|
|
12
|
+
doc.stub!(:new?).and_return(false)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should be empty before any associated documents are set" do
|
17
|
+
@person.tasks.should be_empty
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should accept an array of documents" do
|
21
|
+
@person.tasks = [@task]
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should set the links on the RObject when assigning" do
|
25
|
+
@person.tasks = [@task]
|
26
|
+
@person.robject.links.should include(@task.to_link("tasks"))
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should be able to replace the entire collection of documents (even appended ones)" do
|
30
|
+
@person.tasks << @task
|
31
|
+
@person.tasks = [@other_task]
|
32
|
+
@person.tasks.should == [@other_task]
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should return the assigned documents when assigning" do
|
36
|
+
t = (@person.tasks = [@task])
|
37
|
+
t.should == [@task]
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should link-walk to the associated documents when accessing" do
|
41
|
+
@person.robject.links << @task.to_link("tasks")
|
42
|
+
@person.robject.should_receive(:walk).with(Riak::WalkSpec.new(:bucket => "tasks", :tag => "tasks")).and_return([])
|
43
|
+
@person.tasks.should == []
|
44
|
+
end
|
45
|
+
|
46
|
+
it "handles conflict appropriately by selecting the linked-walk robjects that match the links" do
|
47
|
+
@person.robject.links << @task.to_link("tasks") << @other_task.to_link("tasks")
|
48
|
+
@person.robject.
|
49
|
+
should_receive(:walk).
|
50
|
+
with(Riak::WalkSpec.new(:bucket => "tasks", :tag => "tasks")).
|
51
|
+
and_return([[@task.robject, @other_task.robject, @third_task.robject]])
|
52
|
+
|
53
|
+
@person.tasks.should == [@task, @other_task]
|
54
|
+
end
|
55
|
+
|
56
|
+
it "allows the links to be replaced directly" do
|
57
|
+
@person.tasks = [@task]
|
58
|
+
@person.tasks.__send__(:should_receive, :reset)
|
59
|
+
@person.tasks.__send__(:links).should == [@task.robject.to_link("tasks")]
|
60
|
+
@person.tasks.replace_links([@other_task, @third_task].map { |t| t.robject.to_link("tasks") })
|
61
|
+
@person.tasks.__send__(:links).should == [@other_task, @third_task].map { |t| t.robject.to_link("tasks") }
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should replace associated documents with a new set" do
|
65
|
+
@person.tasks = [@task]
|
66
|
+
@person.tasks = [@other_task]
|
67
|
+
@person.tasks.should == [@other_task]
|
68
|
+
end
|
69
|
+
|
70
|
+
it "asks the keys set for the count to avoid having to unnecessarily load all documents" do
|
71
|
+
@person.tasks.keys.stub(:size => 17)
|
72
|
+
@person.tasks.count.should == 17
|
73
|
+
end
|
74
|
+
|
75
|
+
# it "should be able to build a new associated document" do
|
76
|
+
# pending "Need unsaved document support"
|
77
|
+
# end
|
78
|
+
|
79
|
+
it "should return an array from to_ary" do
|
80
|
+
@person.tasks << @task
|
81
|
+
@person.tasks.to_ary.should == [@task]
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should refuse assigning a collection of the wrong type" do
|
85
|
+
lambda { @person.tasks = nil }.should raise_error
|
86
|
+
lambda { @person.tasks = @task }.should raise_error
|
87
|
+
lambda { @person.tasks = [@person] }.should raise_error
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "#<< (when the target has not already been loaded)" do
|
91
|
+
it "avoids link-walking when adding a record to an unloaded association" do
|
92
|
+
@person.robject.should_not_receive(:walk)
|
93
|
+
@person.tasks << @task
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should be able to count the associated documents" do
|
97
|
+
@person.tasks << @task
|
98
|
+
@person.tasks.count.should == 1
|
99
|
+
@person.tasks << @other_task
|
100
|
+
@person.tasks.count.should == 2
|
101
|
+
end
|
102
|
+
|
103
|
+
it "maintains the list of keys properly as new documents are appended" do
|
104
|
+
@person.tasks << @task
|
105
|
+
@person.tasks.should have(1).key
|
106
|
+
@person.tasks << @other_task
|
107
|
+
@person.tasks.should have(2).keys
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should be able to append documents to the associated set" do
|
111
|
+
@person.tasks << @task
|
112
|
+
@person.tasks << @other_task
|
113
|
+
@person.should have(2).tasks
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should be able to chain calls to adding documents" do
|
117
|
+
@person.tasks << @task << @other_task
|
118
|
+
@person.should have(2).tasks
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should set the links on the RObject when appending" do
|
122
|
+
@person.tasks << @task << @other_task
|
123
|
+
[@task, @other_task].each do |t|
|
124
|
+
@person.robject.links.should include(t.to_link("tasks"))
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
it "does not return duplicates (for when the object has been appended and it's robject is found while walking the links)" do
|
129
|
+
@person.tasks.stub(:robjects => [@task.robject])
|
130
|
+
@person.tasks.reset
|
131
|
+
@person.tasks << @task
|
132
|
+
@person.tasks.should == [@task]
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe "#reset" do
|
137
|
+
it "clears appended documents" do
|
138
|
+
@person.tasks << @task
|
139
|
+
@person.tasks.reset
|
140
|
+
@person.tasks.should == []
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe "#keys" do
|
145
|
+
let(:link_keys) { %w[ 1 2 3 ] }
|
146
|
+
let(:links) { link_keys.map { |k| Riak::Link.new('tasks', k, 'task') } }
|
147
|
+
|
148
|
+
before(:each) do
|
149
|
+
@person.tasks.stub(:links => links)
|
150
|
+
end
|
151
|
+
|
152
|
+
it "returns a set of keys" do
|
153
|
+
@person.tasks.keys.should be_a(Set)
|
154
|
+
@person.tasks.keys.to_a.should == link_keys
|
155
|
+
end
|
156
|
+
|
157
|
+
it "is memoized between calls" do
|
158
|
+
@person.tasks.keys.should equal(@person.tasks.keys)
|
159
|
+
end
|
160
|
+
|
161
|
+
it "is cleared when the association is reset" do
|
162
|
+
orig_set = @person.tasks.keys
|
163
|
+
@person.tasks.reset
|
164
|
+
@person.tasks.keys.should_not equal(orig_set)
|
165
|
+
end
|
166
|
+
|
167
|
+
it "is cleared when the association is replaced" do
|
168
|
+
orig_set = @person.tasks.keys
|
169
|
+
@person.tasks.replace([@task])
|
170
|
+
@person.tasks.keys.should_not equal(orig_set)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
describe "#include?" do
|
175
|
+
it "delegates to the set of keys so as not to unnecessarily load the associated documents" do
|
176
|
+
@person.tasks.keys.should_receive(:include?).with(@task.key).and_return(true)
|
177
|
+
@person.tasks.include?(@task).should be_true
|
178
|
+
end
|
179
|
+
|
180
|
+
it "short-circuits and returns false if the given object is not a ripple document" do
|
181
|
+
@person.tasks.keys.should_not_receive(:include?)
|
182
|
+
@person.tasks.include?(Object.new).should be_false
|
183
|
+
end
|
184
|
+
|
185
|
+
it "returns false if the document's bucket is different from the associations bucket, even if the keys are the same" do
|
186
|
+
@person.tasks << @task
|
187
|
+
other_person = Person.new { |p| p.key = @task.key }
|
188
|
+
@person.tasks.include?(other_person).should be_false
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ripple::Associations::ManyReferenceProxy do
|
4
|
+
require 'support/models/transactions'
|
5
|
+
|
6
|
+
before :each do
|
7
|
+
@account = Account.new {|e| e.key = "accounty"}
|
8
|
+
@payment_method = PaymentMethod.new {|e| e.key = "paymadoo"}
|
9
|
+
@other_payment_method = PaymentMethod.new {|e| e.key = "otherpaym"}
|
10
|
+
Ripple.client.stub(:search => {"response" => {"docs" => []}})
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should be empty before any associated documents are set" do
|
14
|
+
@account.payment_methods.should be_empty
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should accept an array of documents" do
|
18
|
+
@account.payment_methods = [@payment_method]
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should set the key on the sub object when assigning" do
|
22
|
+
@account.payment_methods = [@payment_method]
|
23
|
+
@payment_method.account_key.should == "accounty"
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should be able to replace the entire collection of documents (even appended ones)" do
|
27
|
+
@account.payment_methods << @payment_method
|
28
|
+
@account.payment_methods = [@other_payment_method]
|
29
|
+
@account.payment_methods.should == [@other_payment_method]
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should return the assigned documents when assigning" do
|
33
|
+
t = (@account.payment_methods = [@payment_method])
|
34
|
+
t.should == [@payment_method]
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should find the associated documents when accessing" do
|
38
|
+
Ripple.client.should_receive(:search).with("payment_methods", "account_key: accounty").and_return({"response" => {"docs" => ["id" => "paymadoo"]}})
|
39
|
+
PaymentMethod.should_receive(:find).with(["paymadoo"]).and_return([@payment_method])
|
40
|
+
@account.payment_methods.should == [@payment_method]
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should replace associated documents with a new set" do
|
44
|
+
@account.payment_methods = [@payment_method]
|
45
|
+
@account.payment_methods = [@other_payment_method]
|
46
|
+
@account.payment_methods.should == [@other_payment_method]
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should return an array from to_ary" do
|
50
|
+
@account.payment_methods << @payment_method
|
51
|
+
@account.payment_methods.to_ary.should == [@payment_method]
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should refuse assigning a collection of the wrong type" do
|
55
|
+
lambda { @account.payment_methods = nil }.should raise_error
|
56
|
+
lambda { @account.payment_methods = @payment_method }.should raise_error
|
57
|
+
lambda { @account.payment_methods = [@account] }.should raise_error
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "#<< (when the target has not already been loaded)" do
|
61
|
+
it "avoids searching when adding a record to an unloaded association" do
|
62
|
+
PaymentMethod.should_not_receive(:search)
|
63
|
+
@account.payment_methods << @payment_method
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should be able to count the associated documents" do
|
67
|
+
@account.payment_methods << @payment_method
|
68
|
+
@account.payment_methods.count.should == 1
|
69
|
+
@account.payment_methods << @other_payment_method
|
70
|
+
@account.payment_methods.count.should == 2
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should be able to count without loading documents" do
|
74
|
+
Ripple.client.stub(:search => {"response" => {"docs" => [{"id" => @payment_method.key}, {"id" => @other_payment_method.key}]}})
|
75
|
+
PaymentMethod.should_not_receive(:find)
|
76
|
+
@account.payment_methods.count.should == 2
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should be able to append documents to the associated set" do
|
80
|
+
@account.payment_methods << @payment_method
|
81
|
+
@account.payment_methods << @other_payment_method
|
82
|
+
@account.should have(2).payment_methods
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should be able to chain calls to adding documents" do
|
86
|
+
@account.payment_methods << @payment_method << @other_payment_method
|
87
|
+
@account.should have(2).payment_methods
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should assign the keys on the sub object when appending" do
|
91
|
+
@account.payment_methods << @payment_method << @other_payment_method
|
92
|
+
[@payment_method, @other_payment_method].each do |t|
|
93
|
+
t.account_key.should == "accounty"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
it "does not return duplicates (for when the object has been appended and it's robject is found while walking the links)" do
|
98
|
+
@account.payment_methods.stub(:find_target => Set.new([@payment_method]))
|
99
|
+
@account.payment_methods.reset
|
100
|
+
@account.payment_methods << @payment_method
|
101
|
+
@account.payment_methods.should == [@payment_method]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "#reset" do
|
106
|
+
it "clears appended documents" do
|
107
|
+
@account.payment_methods << @payment_method
|
108
|
+
@account.payment_methods.reset
|
109
|
+
@account.payment_methods.should == []
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "#keys" do
|
114
|
+
let(:ze_keys) { %w(1 2 3) }
|
115
|
+
let(:search_results) do
|
116
|
+
{"response" => {"docs" => ze_keys.map { |k| {"id" => k} }}}
|
117
|
+
end
|
118
|
+
|
119
|
+
before(:each) do
|
120
|
+
Ripple.client.stub(:search => search_results)
|
121
|
+
PaymentMethod.stub(:find => [@payment_method])
|
122
|
+
end
|
123
|
+
|
124
|
+
it "returns a set of keys" do
|
125
|
+
@account.payment_methods.keys.should be_a(Set)
|
126
|
+
@account.payment_methods.keys.to_a.should == ze_keys
|
127
|
+
end
|
128
|
+
|
129
|
+
it "is memoized between calls" do
|
130
|
+
@account.payment_methods.keys.should equal(@account.payment_methods.keys)
|
131
|
+
end
|
132
|
+
|
133
|
+
it "is cleared when the association is reset" do
|
134
|
+
orig_set = @account.payment_methods.keys
|
135
|
+
@account.payment_methods.reset
|
136
|
+
@account.payment_methods.keys.should_not equal(orig_set)
|
137
|
+
end
|
138
|
+
|
139
|
+
it "is cleared when the association is replaced" do
|
140
|
+
orig_set = @account.payment_methods.keys
|
141
|
+
@account.payment_methods.replace([@payment_method])
|
142
|
+
@account.payment_methods.keys.should_not equal(orig_set)
|
143
|
+
end
|
144
|
+
|
145
|
+
it "maintains the list of keys properly as new documents are appended" do
|
146
|
+
@account.payment_methods << @payment_method
|
147
|
+
@account.payment_methods.should have(1).key
|
148
|
+
@account.payment_methods << @other_payment_method
|
149
|
+
@account.payment_methods.should have(2).keys
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe "#include?" do
|
154
|
+
it "delegates to the set of keys so as not to unnecessarily load the associated documents" do
|
155
|
+
@account.payment_methods.keys.should_receive(:include?).with(@payment_method.key).and_return(true)
|
156
|
+
@account.payment_methods.include?(@payment_method).should be_true
|
157
|
+
end
|
158
|
+
|
159
|
+
it "short-circuits and returns false if the given object is not a ripple document" do
|
160
|
+
@account.payment_methods.keys.should_not_receive(:include?)
|
161
|
+
@account.payment_methods.include?(Object.new).should be_false
|
162
|
+
end
|
163
|
+
|
164
|
+
it "returns false if the document's bucket is different from the associations bucket, even if the keys are the same" do
|
165
|
+
@account.payment_methods << @payment_method
|
166
|
+
other_account = Account.new { |p| p.key = @payment_method.key }
|
167
|
+
@account.payment_methods.include?(other_account).should be_false
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ripple::Associations::ManyStoredKeyProxy do
|
4
|
+
require 'support/models/transactions'
|
5
|
+
|
6
|
+
before :each do
|
7
|
+
@account = Account.new {|t| t.key = "accounty" }
|
8
|
+
@transaction_one = Transaction.new {|t| t.key = "one" }
|
9
|
+
@transaction_two = Transaction.new {|t| t.key = "two" }
|
10
|
+
@transaction_three = Transaction.new {|t| t.key = "three" }
|
11
|
+
@transaction_one.stub(:new_record?).and_return(false)
|
12
|
+
@transaction_two.stub(:new_record?).and_return(false)
|
13
|
+
@transaction_three.stub(:new_record?).and_return(false)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should be empty before any associated documents are set" do
|
17
|
+
@account.transactions.should be_empty
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should allow appending" do
|
21
|
+
@account.transactions << @transaction_one
|
22
|
+
@account.transactions.should == [@transaction_one]
|
23
|
+
@account.transaction_keys.should == ["one"]
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should be able to chain calls to adding documents" do
|
27
|
+
@account.transactions << @transaction_one << @transaction_two
|
28
|
+
@account.transactions.should == [@transaction_one, @transaction_two]
|
29
|
+
end
|
30
|
+
|
31
|
+
it "creates the right type of key collection" do
|
32
|
+
Object.module_eval do
|
33
|
+
class DifferentlyKeyedAccount
|
34
|
+
include Ripple::Document
|
35
|
+
property :transaction_keys, SortedSet
|
36
|
+
many :transactions, :using => :stored_key
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
account = DifferentlyKeyedAccount.new
|
41
|
+
account.transactions << @transaction_one
|
42
|
+
account.transaction_keys.should == SortedSet.new(["one"])
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should accept an array of documents" do
|
46
|
+
@account.transactions = [@transaction_one]
|
47
|
+
@account.transactions.should == [@transaction_one]
|
48
|
+
@account.transaction_keys.should == %w(one)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should be able to replace the entire collection of documents (even appended ones)" do
|
52
|
+
@account.transactions << @transaction_one
|
53
|
+
@account.transactions = [@transaction_two]
|
54
|
+
@account.transactions.should == [@transaction_two]
|
55
|
+
@account.transaction_keys.should == %w(two)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should return the assigned documents when assigning" do
|
59
|
+
t = (@account.transactions = [@transaction_one])
|
60
|
+
t.should == [@transaction_one]
|
61
|
+
end
|
62
|
+
|
63
|
+
it "asks the keys set for the count to avoid having to unnecessarily load all documents" do
|
64
|
+
@account.transactions << @transaction_one
|
65
|
+
@account.transaction_keys.stub(:size => 17)
|
66
|
+
@account.transactions.count.should == 17
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should return an array from to_ary" do
|
70
|
+
@account.transactions << @transaction_one
|
71
|
+
@account.transactions.to_ary.should == [@transaction_one]
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should refuse assigning a collection of the wrong type" do
|
75
|
+
lambda { @account.transactions = nil }.should raise_error
|
76
|
+
lambda { @account.transactions = @transaction_one }.should raise_error
|
77
|
+
lambda { @account.transactions = [@account] }.should raise_error
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should refuse appending a document of the wrong type" do
|
81
|
+
lambda { @account.transactions << Account.new }.should raise_error
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "#reset" do
|
85
|
+
it "clears appended documents" do
|
86
|
+
@account.transactions << @transaction_one
|
87
|
+
@account.transactions.reset
|
88
|
+
@account.transactions.should == []
|
89
|
+
end
|
90
|
+
|
91
|
+
it "resets to the saved state of the proxy" do
|
92
|
+
Transaction.stub(:find).and_return([@transaction_one])
|
93
|
+
@account.transactions << @transaction_two
|
94
|
+
@account.transactions.reset
|
95
|
+
Transaction.stub(:find).and_return([@transaction_one])
|
96
|
+
@account.transactions.should == [ @transaction_one ]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "#include?" do
|
101
|
+
it "delegates to the set of keys so as not to unnecessarily load the associated documents" do
|
102
|
+
@account.transactions.keys.should_receive(:include?).with(@transaction_two.key).and_return(true)
|
103
|
+
@account.transactions.include?(@transaction_two).should be_true
|
104
|
+
end
|
105
|
+
|
106
|
+
it "short-circuits and returns false if the given object is not a ripple document" do
|
107
|
+
@account.transactions.keys.should_not_receive(:include?)
|
108
|
+
@account.transactions.include?(Object.new).should be_false
|
109
|
+
end
|
110
|
+
|
111
|
+
it "returns false if the document's bucket is different from the associations bucket, even if the keys are the same" do
|
112
|
+
@account.transactions << @transaction_one
|
113
|
+
other_account = Account.new { |p| p.key = @transaction_one.key }
|
114
|
+
@account.transactions.include?(other_account).should be_false
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe "#keys" do
|
119
|
+
before do
|
120
|
+
@account.transactions << @transaction_one << @transaction_two
|
121
|
+
end
|
122
|
+
|
123
|
+
it "returns a set of keys" do
|
124
|
+
@account.transactions.keys.should be_a(Array)
|
125
|
+
@account.transactions.keys.to_a.should == %w(one two)
|
126
|
+
end
|
127
|
+
|
128
|
+
it "is memoized between calls" do
|
129
|
+
@account.transactions.keys.should equal(@account.transactions.keys)
|
130
|
+
end
|
131
|
+
|
132
|
+
it "is cleared when the association is reset" do
|
133
|
+
orig_set = @account.transactions.keys
|
134
|
+
@account.transactions.reset
|
135
|
+
@account.transactions.keys.should_not equal(orig_set)
|
136
|
+
@account.transactions.keys.should == []
|
137
|
+
end
|
138
|
+
|
139
|
+
it "is cleared when the association is replaced" do
|
140
|
+
orig_set = @account.transactions.keys
|
141
|
+
@account.transactions.replace([@transaction_one])
|
142
|
+
@account.transactions.keys.should_not equal(orig_set)
|
143
|
+
@account.transactions.keys.to_a.should == %w(one)
|
144
|
+
end
|
145
|
+
|
146
|
+
it "maintains the list of keys properly as new documents are appended" do
|
147
|
+
@account.transactions.keys.size.should == 2
|
148
|
+
@account.transactions << @transaction_three
|
149
|
+
@account.transactions.keys.size.should == 3
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
it "temporarily bombs if the document you're appending isn't saved. This behavior shouldn't last long." do
|
155
|
+
lambda { @account.transactions << Transaction.new }.should raise_error
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|