ripple 0.9.5 → 1.0.0.beta

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 (152) hide show
  1. data/.gitignore +32 -0
  2. data/Gemfile +10 -9
  3. data/Guardfile +15 -0
  4. data/Rakefile +11 -40
  5. data/lib/rails/generators/ripple/configuration/configuration_generator.rb +0 -13
  6. data/lib/rails/generators/ripple/configuration/templates/ripple.yml +4 -4
  7. data/lib/rails/generators/ripple/js/js_generator.rb +0 -13
  8. data/lib/rails/generators/ripple/js/templates/js/contrib.js +0 -17
  9. data/lib/rails/generators/ripple/js/templates/js/ripple.js +0 -13
  10. data/lib/rails/generators/ripple/model/model_generator.rb +0 -14
  11. data/lib/rails/generators/ripple/model/templates/model.rb +1 -1
  12. data/lib/rails/generators/ripple/observer/observer_generator.rb +0 -14
  13. data/lib/rails/generators/ripple/test/test_generator.rb +7 -19
  14. data/lib/rails/generators/ripple_generator.rb +1 -14
  15. data/lib/ripple.rb +7 -14
  16. data/lib/ripple/associations.rb +129 -26
  17. data/lib/ripple/associations/embedded.rb +1 -15
  18. data/lib/ripple/associations/instantiators.rb +0 -13
  19. data/lib/ripple/associations/linked.rb +41 -19
  20. data/lib/ripple/associations/many.rb +0 -14
  21. data/lib/ripple/associations/many_embedded_proxy.rb +0 -14
  22. data/lib/ripple/associations/many_linked_proxy.rb +39 -18
  23. data/lib/ripple/associations/many_reference_proxy.rb +93 -0
  24. data/lib/ripple/associations/many_stored_key_proxy.rb +76 -0
  25. data/lib/ripple/associations/one.rb +1 -15
  26. data/lib/ripple/associations/one_embedded_proxy.rb +0 -14
  27. data/lib/ripple/associations/one_key_proxy.rb +58 -0
  28. data/lib/ripple/associations/one_linked_proxy.rb +4 -14
  29. data/lib/ripple/associations/one_stored_key_proxy.rb +43 -0
  30. data/lib/ripple/associations/proxy.rb +8 -14
  31. data/lib/ripple/attribute_methods.rb +19 -17
  32. data/lib/ripple/attribute_methods/dirty.rb +19 -15
  33. data/lib/ripple/attribute_methods/query.rb +0 -14
  34. data/lib/ripple/attribute_methods/read.rb +0 -14
  35. data/lib/ripple/attribute_methods/write.rb +0 -14
  36. data/lib/ripple/callbacks.rb +10 -16
  37. data/lib/ripple/conflict/basic_resolver.rb +82 -0
  38. data/lib/ripple/conflict/document_hooks.rb +20 -0
  39. data/lib/ripple/conflict/resolver.rb +71 -0
  40. data/lib/ripple/conflict/test_helper.rb +33 -0
  41. data/lib/ripple/conversion.rb +0 -14
  42. data/lib/ripple/core_ext.rb +1 -14
  43. data/lib/ripple/core_ext/casting.rb +19 -19
  44. data/lib/ripple/core_ext/object.rb +8 -0
  45. data/lib/ripple/document.rb +21 -16
  46. data/lib/ripple/document/bucket_access.rb +0 -14
  47. data/lib/ripple/document/finders.rb +17 -23
  48. data/lib/ripple/document/key.rb +8 -28
  49. data/lib/ripple/document/link.rb +30 -0
  50. data/lib/ripple/document/persistence.rb +12 -20
  51. data/lib/ripple/embedded_document.rb +10 -15
  52. data/lib/ripple/embedded_document/around_callbacks.rb +18 -0
  53. data/lib/ripple/embedded_document/finders.rb +7 -18
  54. data/lib/ripple/embedded_document/persistence.rb +5 -17
  55. data/lib/ripple/i18n.rb +0 -14
  56. data/lib/ripple/inspection.rb +21 -15
  57. data/lib/ripple/locale/en.yml +9 -13
  58. data/lib/ripple/nested_attributes.rb +33 -39
  59. data/lib/ripple/observable.rb +0 -13
  60. data/lib/ripple/properties.rb +1 -14
  61. data/lib/ripple/property_type_mismatch.rb +0 -14
  62. data/lib/ripple/railtie.rb +4 -14
  63. data/lib/ripple/railties/ripple.rake +86 -0
  64. data/lib/ripple/serialization.rb +11 -11
  65. data/lib/ripple/test_server.rb +36 -0
  66. data/lib/ripple/timestamps.rb +0 -13
  67. data/lib/ripple/translation.rb +4 -14
  68. data/lib/ripple/validations.rb +1 -15
  69. data/lib/ripple/validations/associated_validator.rb +26 -17
  70. data/lib/ripple/version.rb +3 -0
  71. data/ripple.gemspec +24 -35
  72. data/spec/integration/ripple/associations_spec.rb +89 -58
  73. data/spec/integration/ripple/conflict_resolution_spec.rb +298 -0
  74. data/spec/integration/ripple/nested_attributes_spec.rb +19 -19
  75. data/spec/integration/ripple/persistence_spec.rb +15 -34
  76. data/spec/integration/ripple/search_associations_spec.rb +31 -0
  77. data/spec/ripple/associations/many_embedded_proxy_spec.rb +23 -36
  78. data/spec/ripple/associations/many_linked_proxy_spec.rb +133 -45
  79. data/spec/ripple/associations/many_reference_proxy_spec.rb +170 -0
  80. data/spec/ripple/associations/many_stored_key_proxy_spec.rb +158 -0
  81. data/spec/ripple/associations/one_embedded_proxy_spec.rb +24 -37
  82. data/spec/ripple/associations/one_key_proxy_spec.rb +82 -0
  83. data/spec/ripple/associations/one_linked_proxy_spec.rb +22 -23
  84. data/spec/ripple/associations/one_stored_key_proxy_spec.rb +72 -0
  85. data/spec/ripple/associations/proxy_spec.rb +21 -15
  86. data/spec/ripple/associations_spec.rb +54 -23
  87. data/spec/ripple/attribute_methods/dirty_spec.rb +56 -5
  88. data/spec/ripple/attribute_methods_spec.rb +47 -21
  89. data/spec/ripple/bucket_access_spec.rb +4 -17
  90. data/spec/ripple/callbacks_spec.rb +52 -15
  91. data/spec/ripple/conflict/resolver_spec.rb +42 -0
  92. data/spec/ripple/conversion_spec.rb +2 -15
  93. data/spec/ripple/core_ext_spec.rb +12 -15
  94. data/spec/ripple/document/link_spec.rb +67 -0
  95. data/spec/ripple/document_spec.rb +34 -19
  96. data/spec/ripple/embedded_document/finders_spec.rb +12 -19
  97. data/spec/ripple/embedded_document/persistence_spec.rb +20 -26
  98. data/spec/ripple/embedded_document_spec.rb +44 -34
  99. data/spec/ripple/finders_spec.rb +58 -29
  100. data/spec/ripple/inspection_spec.rb +40 -37
  101. data/spec/ripple/key_spec.rb +5 -17
  102. data/spec/ripple/observable_spec.rb +3 -16
  103. data/spec/ripple/persistence_spec.rb +134 -18
  104. data/spec/ripple/properties_spec.rb +21 -15
  105. data/spec/ripple/ripple_spec.rb +1 -14
  106. data/spec/ripple/serialization_spec.rb +16 -17
  107. data/spec/ripple/timestamps_spec.rb +73 -52
  108. data/spec/ripple/validations/associated_validator_spec.rb +69 -25
  109. data/spec/ripple/validations_spec.rb +3 -16
  110. data/spec/spec_helper.rb +17 -18
  111. data/spec/support/associations.rb +1 -1
  112. data/spec/support/associations/proxies.rb +0 -13
  113. data/spec/support/integration_setup.rb +11 -0
  114. data/spec/support/mocks.rb +0 -13
  115. data/spec/support/models.rb +33 -2
  116. data/spec/support/models/address.rb +1 -16
  117. data/spec/support/models/box.rb +7 -14
  118. data/spec/support/models/car.rb +27 -1
  119. data/spec/support/models/cardboard_box.rb +0 -14
  120. data/spec/support/models/clock.rb +6 -15
  121. data/spec/support/models/clock_observer.rb +0 -14
  122. data/spec/support/models/credit_card.rb +5 -0
  123. data/spec/support/models/customer.rb +0 -14
  124. data/spec/support/models/email.rb +0 -14
  125. data/spec/support/models/family.rb +1 -13
  126. data/spec/support/models/favorite.rb +0 -14
  127. data/spec/support/models/invoice.rb +0 -14
  128. data/spec/support/models/late_invoice.rb +0 -14
  129. data/spec/support/models/nested.rb +12 -0
  130. data/spec/support/models/ninja.rb +7 -0
  131. data/spec/support/models/note.rb +0 -14
  132. data/spec/support/models/page.rb +1 -15
  133. data/spec/support/models/paid_invoice.rb +0 -14
  134. data/spec/support/models/post.rb +12 -0
  135. data/spec/support/models/profile.rb +7 -0
  136. data/spec/support/models/subscription.rb +26 -0
  137. data/spec/support/models/tasks.rb +0 -18
  138. data/spec/support/models/team.rb +11 -0
  139. data/spec/support/models/transactions.rb +17 -0
  140. data/spec/support/models/tree.rb +2 -16
  141. data/spec/support/models/user.rb +14 -16
  142. data/spec/support/models/widget.rb +8 -14
  143. data/spec/support/search.rb +14 -0
  144. data/spec/support/test_server.rb +22 -12
  145. data/spec/support/test_server.yml.example +14 -2
  146. metadata +223 -59
  147. data/lib/rails/generators/ripple/test/templates/test_server.rb +0 -46
  148. data/spec/support/models/driver.rb +0 -5
  149. data/spec/support/models/engine.rb +0 -4
  150. data/spec/support/models/passenger.rb +0 -5
  151. data/spec/support/models/seat.rb +0 -4
  152. data/spec/support/models/wheel.rb +0 -5
@@ -0,0 +1,82 @@
1
+ require 'spec_helper'
2
+
3
+ describe Ripple::Associations::OneKeyProxy do
4
+
5
+ before :each do
6
+ @user = User.new
7
+ @profile = Profile.new(:color => 'blue')
8
+ end
9
+
10
+ describe "User with a key corresponded profile" do
11
+ it "should not have a profile before it is set" do
12
+ @user.profile.should be_nil
13
+ end
14
+
15
+ it "should be able to get and set its profile" do
16
+ @user.profile = @profile
17
+ @user.profile.should eq(@profile)
18
+ end
19
+
20
+ it "should return the assignment when assigning" do
21
+ rtn = @user.profile = @profile
22
+ rtn.should eq(@profile)
23
+ end
24
+
25
+ it "should be able to replace its profile with a new one" do
26
+ @user.profile = @profile
27
+ @user.profile = Profile.new(:color => 'red')
28
+ @user.profile.color.should eq('red')
29
+ end
30
+
31
+ it "should be able to build a new profile" do
32
+ Profile.stub(:new).and_return(@profile)
33
+ @user.profile.build.should eq(@profile)
34
+ end
35
+
36
+ it "should assign its key to the associated profile when assigning" do
37
+ @user.key = "foo"
38
+ @user.profile = @profile
39
+ @profile.key.should eq("foo")
40
+ end
41
+
42
+ it "should assign its key to the built profile" do
43
+ @user.key = "foo"
44
+ @user.profile.build.key.should eq("foo")
45
+ end
46
+
47
+ it "should update the key on the profile when updating it on the user" do
48
+ @user.profile = @profile
49
+ @user.key = "foo"
50
+ @profile.key.should eq("foo")
51
+ end
52
+
53
+ it "should not update the key of a previous profile" do
54
+ @profile2 = Profile.new
55
+ @user.profile = @profile
56
+ @user.profile = @profile2
57
+ @user.key = "foo"
58
+ @profile.key.should_not eq("foo")
59
+ end
60
+
61
+ it "should not infinitely loop when assigning a user to the profile" do
62
+ @user2 = User.new
63
+ @user.profile = @profile
64
+ @profile.user = @user2
65
+ @profile.key = "foo"
66
+ @user2.key.should eq("foo")
67
+ end
68
+
69
+ it "should update the user's key when being updated on the profile" do
70
+ @user.profile = @profile
71
+ @profile.key = "foo"
72
+ @user.key.should eq("foo")
73
+ end
74
+
75
+ it "should work properly with custom key methods" do
76
+ @user = Ninja.new(:name => 'Naruto')
77
+ @user.profile = @profile
78
+ @profile.key.should eq("ninja-naruto")
79
+ end
80
+ end
81
+
82
+ end
@@ -1,21 +1,8 @@
1
- # Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
- require File.expand_path("../../../spec_helper", __FILE__)
1
+ require 'spec_helper'
15
2
 
16
3
  describe Ripple::Associations::OneLinkedProxy do
17
- require 'support/models/tasks'
18
- require 'support/models/family'
4
+ # require 'support/models/tasks'
5
+ # require 'support/models/family'
19
6
 
20
7
  before :each do
21
8
  @person = Person.new {|p| p.key = "riak-user" }
@@ -36,7 +23,7 @@ describe Ripple::Associations::OneLinkedProxy do
36
23
 
37
24
  it "should set the link on the RObject when assigning" do
38
25
  @person.profile = @profile
39
- @person.robject.links.should include(@profile.robject.to_link("profile"))
26
+ @person.robject.links.should include(@profile.to_link("profile"))
40
27
  end
41
28
 
42
29
  it "should return the assigned document when assigning" do
@@ -44,18 +31,30 @@ describe Ripple::Associations::OneLinkedProxy do
44
31
  ret.should == @profile
45
32
  end
46
33
 
47
- it "should save the new document when assigning" do
48
- @profile.should_receive(:new?).and_return(true)
49
- @profile.should_receive(:save).and_return(true)
50
- @person.profile = @profile
51
- end
52
-
53
34
  it "should link-walk to the associated document when accessing" do
54
35
  @person.robject.links << @profile.robject.to_link("profile")
55
36
  @person.robject.should_receive(:walk).with(Riak::WalkSpec.new(:bucket => "profiles", :tag => "profile")).and_return([[@profile.robject]])
56
37
  @person.profile.should be_present
57
38
  end
58
39
 
40
+ it "handles conflict appropriately by selecting the linked-walk robject that matches the link" do
41
+ @person.robject.links << @profile.robject.to_link("profile")
42
+ @person.robject.
43
+ should_receive(:walk).
44
+ with(Riak::WalkSpec.new(:bucket => "profiles", :tag => "profile")).
45
+ and_return([[@other_profile.robject, @profile.robject]])
46
+
47
+ @person.profile.should == @profile
48
+ end
49
+
50
+ it "allows the links to be replaced directly" do
51
+ @person.profile = @profile
52
+ @person.profile.__send__(:should_receive, :reset)
53
+ @person.profile.__send__(:links).should == [@profile.robject.to_link("profile")]
54
+ @person.profile.replace_links(@other_profile.robject.to_link("profile"))
55
+ @person.profile.__send__(:links).should == [@other_profile.robject.to_link("profile")]
56
+ end
57
+
59
58
  it "should return nil immediately if the association link is missing" do
60
59
  @person.robject.links.should be_empty
61
60
  @person.profile.should be_nil
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+
3
+ describe Ripple::Associations::OneStoredKeyProxy do
4
+ # require 'support/models/transactions'
5
+ # require 'support/models/family'
6
+
7
+ before :each do
8
+ @account = Account.new {|e| e.key = "accounty" }
9
+ @other_account = Account.new{|e| e.key = "ickycount" }
10
+ @transaction = Transaction.new{|e| e.key = "transacty" }
11
+ end
12
+
13
+ it "should be blank before the associated document is set" do
14
+ @transaction.account.should_not be_present
15
+ end
16
+
17
+ it "should accept a single document" do
18
+ lambda { @transaction.account = @account }.should_not raise_error
19
+ end
20
+
21
+ it "should set the key when assigning" do
22
+ @transaction.account = @account
23
+ @transaction.account_key.should == "accounty"
24
+ end
25
+
26
+ it "should return the assigned document when assigning" do
27
+ ret = (@transaction.account = @account)
28
+ ret.should == @account
29
+ end
30
+
31
+ it "should find the associated document when accessing" do
32
+ @transaction.account_key = "accounty"
33
+ Account.should_receive(:find).with("accounty").and_return(@account)
34
+ @transaction.account.should be_present
35
+ end
36
+
37
+ it "should return nil immediately if the association link is missing" do
38
+ @transaction.account_key.should be_nil
39
+ @transaction.account.should be_nil
40
+ end
41
+
42
+ it "should replace associated document with a new one" do
43
+ @transaction.account = @account
44
+ @transaction.account = @other_account
45
+ @transaction.account.should == @other_account
46
+ @transaction.account_key.should == "ickycount"
47
+ end
48
+
49
+ it "should replace the associated document with the target of the proxy" do
50
+ other_transaction = Transaction.new {|e| e.key = "ickytrans" }
51
+ other_transaction.account = @other_account
52
+
53
+ @transaction.account = other_transaction.account
54
+ @transaction.account.should == @other_account
55
+ end
56
+
57
+ it "refuses assigning a proxy if its target is the wrong type" do
58
+ parent = Parent.new{|e| e.child = Child.new}
59
+ lambda { @transaction.account = parent.child }.should raise_error
60
+ end
61
+
62
+ it "should refuse assigning a document of the wrong type" do
63
+ lambda { @transaction.account = @transaction }.should raise_error
64
+ end
65
+
66
+ it "should nil out the association if nil is assigned" do
67
+ @transaction.account = @account
68
+ @transaction.account = nil
69
+ @transaction.account.should be_nil
70
+ @transaction.account_key.should be_nil
71
+ end
72
+ end
@@ -1,17 +1,4 @@
1
- # Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
- require File.expand_path("../../../spec_helper", __FILE__)
1
+ require 'spec_helper'
15
2
 
16
3
  describe Ripple::Associations::Proxy do
17
4
  require 'support/associations/proxies'
@@ -75,4 +62,23 @@ describe Ripple::Associations::Proxy do
75
62
  it { should be_blank }
76
63
  it { should_not be_present }
77
64
  end
78
- end
65
+
66
+ describe "#has_changed_documents?" do
67
+ before(:each) { @proxy.respond_to?(:loaded_documents).should be_true }
68
+
69
+ it "returns true if any of the loaded documents return true from #changed?" do
70
+ @proxy.stub(:loaded_documents => [stub(:changed? => false), stub(:changed? => true)])
71
+ @proxy.has_changed_documents?.should be_true
72
+ end
73
+
74
+ it "returns false if none of the loaded documents return true from #changed?" do
75
+ @proxy.stub(:loaded_documents => [stub(:changed? => false), stub(:changed? => false)])
76
+ @proxy.has_changed_documents?.should be_false
77
+ end
78
+
79
+ it "returns false if it has no loaded documents" do
80
+ @proxy.stub(:loaded_documents => [])
81
+ @proxy.has_changed_documents?.should be_false
82
+ end
83
+ end
84
+ end
@@ -1,22 +1,9 @@
1
- # Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
- require File.expand_path("../../spec_helper", __FILE__)
1
+ require 'spec_helper'
15
2
 
16
3
  describe Ripple::Associations do
17
- require 'support/models/invoice'
18
- require 'support/models/customer'
19
- require 'support/models/note'
4
+ # require 'support/models/invoice'
5
+ # require 'support/models/customer'
6
+ # require 'support/models/note'
20
7
 
21
8
  it "should provide access to the associations hash" do
22
9
  Invoice.should respond_to(:associations)
@@ -27,6 +14,15 @@ describe Ripple::Associations do
27
14
  Invoice.embedded_associations.should == Array(Invoice.associations[:note])
28
15
  end
29
16
 
17
+ it "should collect the linked associations" do
18
+ Invoice.linked_associations.should == Array(Invoice.associations[:customer])
19
+ end
20
+
21
+ it "should collect the stored_key associations" do
22
+ Account.stored_key_associations.should == Array(Account.associations[:transactions])
23
+ Transaction.stored_key_associations.should == Array(Transaction.associations[:account])
24
+ end
25
+
30
26
  it "should copy associations to a subclass" do
31
27
  Invoice.associations[:foo] = "bar"
32
28
  class SubInvoice < Invoice; end
@@ -50,11 +46,16 @@ describe Ripple::Associations do
50
46
  end
51
47
  end
52
48
 
53
- after do
54
- Invoice.associations.delete(:foo)
55
- Invoice.associations.delete(:items)
56
- SubInvoice.associations.delete(:foo) if defined?(SubInvoice)
57
- Invoice.associations.delete(:payee)
49
+ before(:all) do
50
+ @orig_invoice = Invoice
51
+ Object.send(:remove_const, :Invoice)
52
+ class Invoice < @orig_invoice; end
53
+ end
54
+
55
+ after(:all) do
56
+ Object.send(:remove_const, :SubInvoice) if defined?(SubInvoice)
57
+ Object.send(:remove_const, :Invoice)
58
+ Object.send(:const_set, :Invoice, @orig_invoice)
58
59
  end
59
60
  end
60
61
 
@@ -81,7 +82,22 @@ describe Ripple::Association do
81
82
  end
82
83
 
83
84
  describe "determining the target class" do
84
- require 'support/models/tree'
85
+ context "when in a nested module scope" do
86
+ it "should find the target via the owner's module scope" do
87
+ @association = Ripple::Association.new(:many, :children)
88
+ @association.setup_on(Nested::Scope::Parent)
89
+ @association.klass.should == Nested::Scope::Child
90
+ end
91
+
92
+ it "should not try to find the target in the owner's scope when the class name is fully qualified" do
93
+ @association = Ripple::Association.new(:many, :children, :class_name => "Nested::Scope::Child")
94
+ @association.setup_on(Nested::Scope::Parent)
95
+ expect {
96
+ @association.klass
97
+ }.should_not raise_error
98
+ @association.klass.should == Nested::Scope::Child
99
+ end
100
+ end
85
101
 
86
102
  it "should default to the constantized class name" do
87
103
  @association = Ripple::Association.new(:one, :t, :class_name => "Trunk")
@@ -110,4 +126,19 @@ describe Ripple::Association do
110
126
  it "should determine an instance variable based on the name" do
111
127
  Ripple::Association.new(:many, :pages).ivar.should == "@_pages"
112
128
  end
129
+
130
+ describe "key correspondence" do
131
+ require 'support/models/profile'
132
+ require 'support/models/user'
133
+
134
+ it "should raise an exception if trying to use a many association when :using => :key" do
135
+ expect { Profile.many :user, :using => :key }.to raise_error(ArgumentError, "You cannot use a many association using :key")
136
+ end
137
+
138
+ it "should add the key_delegate attr_accessor to the model declaring the association" do
139
+ Profile.one :user, :using => :key
140
+ Profile.new.should respond_to(:key_delegate)
141
+ Profile.new.should respond_to(:key_delegate=)
142
+ end
143
+ end
113
144
  end
@@ -1,4 +1,4 @@
1
- require File.expand_path("../../../spec_helper", __FILE__)
1
+ require 'spec_helper'
2
2
 
3
3
  describe Ripple::AttributeMethods::Dirty do
4
4
  let(:company) { Company.new }
@@ -12,18 +12,69 @@ describe Ripple::AttributeMethods::Dirty do
12
12
  company.robject.stub!(:store).and_return(true)
13
13
  company.name = 'Fizz Buzz, Inc.'
14
14
  end
15
-
15
+
16
16
  it "should capture previous changes when saving" do
17
17
  company.save
18
18
  company.previous_changes.should include('name')
19
19
  end
20
20
 
21
21
  it "should make previous changes available to after callbacks" do
22
- class << company
22
+ my_class = Class.new(Company) do
23
23
  after_save {|c| c['pc'] = previous_changes }
24
24
  end
25
- company.save
26
- company['pc'].should include('name')
25
+ comp = my_class.new
26
+ comp.robject.stub!(:store).and_return(true)
27
+ comp.name = 'Fizz Buzz, Inc.'
28
+ comp.save
29
+ comp['pc'].should include('name')
30
+ end
31
+ end
32
+
33
+ describe "#changed?" do
34
+
35
+ it "returns true if the document's attributes have changed (regardless of whether or not it has any embedded associated documents)" do
36
+ company.name = 'Fizz Buzz, Inc'
37
+ company.should be_changed
38
+ end
39
+
40
+
41
+ context "when the document's attributes have not changed" do
42
+ it 'returns false if it has no embedded associated documents' do
43
+ company.should_not be_changed
44
+ end
45
+
46
+ context 'when the document has embedded associated documents' do
47
+ before(:each) do
48
+ company.ceo = ceo
49
+ company.invoices << invoice
50
+ company.departments << department
51
+ department.managers << manager
52
+ end
53
+
54
+ it 'returns false if all the embedded documents are not changed' do
55
+ company.should_not be_changed
56
+ end
57
+
58
+ it 'does not consider changes to linked associated documents' do
59
+ invoice.should_not_receive(:changed?)
60
+ company.changed?
61
+ end
62
+
63
+ it 'returns true if a one embedded association document is changed' do
64
+ ceo.should_receive(:changed?).and_return(true)
65
+ company.should be_changed
66
+ end
67
+
68
+ it 'returns true if a many embedded association document is changed' do
69
+ department.should_receive(:changed?).and_return(true)
70
+ company.should be_changed
71
+ end
72
+
73
+ it 'recurses through the whole embedded document structure to find changed grandchild documents' do
74
+ manager.should_receive(:changed?).and_return(true)
75
+ company.should be_changed
76
+ end
77
+ end
27
78
  end
28
79
  end
29
80
  end