ripple 1.0.0.beta → 1.0.0.beta2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +10 -6
- data/Gemfile +6 -15
- data/Gemfile.rails30 +3 -0
- data/Gemfile.rails31 +3 -0
- data/Gemfile.rails32 +3 -0
- data/Guardfile +3 -1
- data/LICENSE +16 -0
- data/README.markdown +173 -0
- data/RELEASE_NOTES.textile +286 -0
- data/Rakefile +19 -0
- data/lib/rails/generators/ripple/configuration/templates/ripple.yml +1 -0
- data/lib/rails/generators/ripple/model/model_generator.rb +1 -1
- data/lib/rails/generators/ripple/model/templates/{model.rb → model.rb.erb} +0 -0
- data/lib/rails/generators/ripple/observer/observer_generator.rb +1 -1
- data/lib/rails/generators/ripple/observer/templates/{observer.rb → observer.rb.erb} +0 -2
- data/lib/rails/generators/ripple/test/templates/cucumber.rb.erb +7 -0
- data/lib/rails/generators/ripple/test/test_generator.rb +17 -13
- data/lib/rails/generators/ripple_generator.rb +1 -0
- data/lib/ripple/associations.rb +65 -55
- data/lib/ripple/associations/embedded.rb +1 -1
- data/lib/ripple/associations/linked.rb +1 -1
- data/lib/ripple/associations/many.rb +1 -1
- data/lib/ripple/associations/many_embedded_proxy.rb +3 -2
- data/lib/ripple/associations/many_linked_proxy.rb +1 -1
- data/lib/ripple/associations/many_reference_proxy.rb +7 -5
- data/lib/ripple/associations/proxy.rb +2 -2
- data/lib/ripple/attribute_methods.rb +69 -61
- data/lib/ripple/attribute_methods/dirty.rb +2 -2
- data/lib/ripple/attribute_methods/read.rb +4 -2
- data/lib/ripple/callbacks.rb +23 -26
- data/lib/ripple/conflict/basic_resolver.rb +6 -2
- data/lib/ripple/conflict/document_hooks.rb +26 -0
- data/lib/ripple/conflict/resolver.rb +10 -2
- data/lib/ripple/conflict/test_helper.rb +3 -2
- data/lib/ripple/conversion.rb +1 -0
- data/lib/ripple/core_ext.rb +1 -0
- data/lib/ripple/core_ext/casting.rb +2 -0
- data/lib/ripple/core_ext/indexes.rb +89 -0
- data/lib/ripple/document.rb +23 -22
- data/lib/ripple/document/key.rb +12 -14
- data/lib/ripple/document/persistence.rb +99 -84
- data/lib/ripple/embedded_document.rb +9 -10
- data/lib/ripple/embedded_document/persistence.rb +42 -44
- data/lib/ripple/i18n.rb +4 -1
- data/lib/ripple/indexes.rb +151 -0
- data/lib/ripple/locale/en.yml +4 -0
- data/lib/ripple/locale/fr.yml +24 -0
- data/lib/ripple/nested_attributes.rb +92 -90
- data/lib/ripple/properties.rb +2 -1
- data/lib/ripple/railtie.rb +9 -0
- data/lib/ripple/railties/ripple.rake +32 -15
- data/lib/ripple/serialization.rb +50 -52
- data/lib/ripple/test_server.rb +1 -2
- data/lib/ripple/timestamps.rb +6 -8
- data/lib/ripple/validations.rb +19 -21
- data/lib/ripple/version.rb +1 -1
- data/ripple.gemspec +6 -5
- data/spec/generators/ripple/configuration_generator_spec.rb +9 -0
- data/spec/generators/ripple/js_generator_spec.rb +14 -0
- data/spec/generators/ripple/model_generator_spec.rb +64 -0
- data/spec/generators/ripple/observer_generator_spec.rb +20 -0
- data/spec/generators/ripple/test_generator_spec.rb +116 -0
- data/spec/generators/ripple_generator_spec.rb +11 -0
- data/spec/integration/ripple/conflict_resolution_spec.rb +35 -4
- data/spec/integration/ripple/indexes_spec.rb +47 -0
- data/spec/ripple/associations/many_embedded_proxy_spec.rb +50 -60
- data/spec/ripple/associations/many_linked_proxy_spec.rb +2 -2
- data/spec/ripple/associations/many_reference_proxy_spec.rb +1 -1
- data/spec/ripple/associations_spec.rb +16 -7
- data/spec/ripple/attribute_methods_spec.rb +43 -2
- data/spec/ripple/callbacks_spec.rb +120 -101
- data/spec/ripple/conversion_spec.rb +5 -13
- data/spec/ripple/core_ext_spec.rb +93 -15
- data/spec/ripple/finders_spec.rb +0 -2
- data/spec/ripple/indexes_spec.rb +111 -0
- data/spec/ripple/observable_spec.rb +1 -2
- data/spec/ripple/persistence_spec.rb +55 -32
- data/spec/ripple/properties_spec.rb +1 -1
- data/spec/ripple/ripple_spec.rb +5 -5
- data/spec/ripple/timestamps_spec.rb +9 -2
- data/spec/ripple/validations_spec.rb +50 -52
- data/spec/spec_helper.rb +9 -2
- data/spec/support/generator_setup.rb +26 -0
- data/spec/support/models.rb +1 -0
- data/spec/support/models/box.rb +1 -0
- data/spec/support/models/clock.rb +1 -1
- data/spec/support/models/indexer.rb +26 -0
- data/spec/support/models/post.rb +3 -2
- data/spec/support/models/widget.rb +2 -0
- data/spec/support/search.rb +2 -2
- data/spec/support/test_server.rb +23 -11
- data/spec/support/test_server.yml.example +1 -1
- metadata +159 -135
- data/spec/support/mocks.rb +0 -4
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rails/generators/ripple_generator'
|
3
|
+
|
4
|
+
describe RippleGenerator do
|
5
|
+
it "should invoke the sub-generators" do
|
6
|
+
%w{configuration js test}.each do |gen|
|
7
|
+
generator.should_receive(:invoke).with("ripple:#{gen}")
|
8
|
+
end
|
9
|
+
run_generator
|
10
|
+
end
|
11
|
+
end
|
@@ -37,11 +37,8 @@ describe "Ripple conflict resolution", :integration => true do
|
|
37
37
|
property :title, String
|
38
38
|
end
|
39
39
|
|
40
|
-
before :all do
|
41
|
-
ConflictedPerson.bucket.allow_mult = true
|
42
|
-
end
|
43
|
-
|
44
40
|
before(:each) do
|
41
|
+
ConflictedPerson.bucket.allow_mult ||= true
|
45
42
|
ConflictedPerson.on_conflict { } # reset to no-op
|
46
43
|
end
|
47
44
|
|
@@ -76,6 +73,40 @@ describe "Ripple conflict resolution", :integration => true do
|
|
76
73
|
)
|
77
74
|
end
|
78
75
|
|
76
|
+
context 'for a document that has a deleted sibling' do
|
77
|
+
before(:each) do
|
78
|
+
create_conflict original_person,
|
79
|
+
lambda { |p| p.destroy! },
|
80
|
+
lambda { |p| p.age = 20 },
|
81
|
+
lambda { |p| p.age = 30 }
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'indicates that one of the siblings was deleted' do
|
85
|
+
siblings = nil
|
86
|
+
ConflictedPerson.on_conflict { |s, c| siblings = s }
|
87
|
+
ConflictedPerson.find('John')
|
88
|
+
|
89
|
+
siblings.should have(3).sibling_records
|
90
|
+
deleted, undeleted = siblings.partition(&:deleted?)
|
91
|
+
deleted.should have(1).record
|
92
|
+
undeleted.should have(2).records
|
93
|
+
deleted = deleted.first
|
94
|
+
|
95
|
+
# the deleted record should be totally blank except for the name (since it is the key)
|
96
|
+
deleted.attributes.reject { |k, v| v.blank? }.should == {"name" => "John"}
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'does not consider the deleted sibling when trying basic resolution of attributes that siblings are in agreement about' do
|
100
|
+
record = conflicts = nil
|
101
|
+
ConflictedPerson.on_conflict { |s, c| conflicts = c; record = self }
|
102
|
+
ConflictedPerson.find('John')
|
103
|
+
|
104
|
+
conflicts.should == [:age]
|
105
|
+
record.gender.should == 'male'
|
106
|
+
record.favorite_colors.should == ['green'].to_set
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
79
110
|
context 'for a document that has conflicted attributes' do
|
80
111
|
let(:most_recent_updated_at) { DateTime.new(2011, 6, 4, 12, 30) }
|
81
112
|
let(:earliest_created_at) { DateTime.new(2010, 5, 3, 12, 30) }
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ripple::Indexes do
|
4
|
+
context "finding documents by an index" do
|
5
|
+
before do
|
6
|
+
@bob = Indexer.create(:name => "Bob", :age => 28)
|
7
|
+
@sally = Indexer.create(:name => "Sally", :age => 28)
|
8
|
+
@mary = Indexer.create(:name => "Mary", :age => 25)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should find a document by equality" do
|
12
|
+
Indexer.find_by_index(:name, 'Bob').should == [@bob]
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should find many documents by equality" do
|
16
|
+
Indexer.find_by_index(:age, 28).should =~ [@bob, @sally]
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should find nothing by equality" do
|
20
|
+
Indexer.find_by_index(:age, 30).should == []
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should find a document by range" do
|
24
|
+
Indexer.find_by_index(:name, "B".."C").should == [@bob]
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should find many documents by range" do
|
28
|
+
Indexer.find_by_index(:age, 27..29).should =~ [@bob, @sally]
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should find nothing by range" do
|
32
|
+
Indexer.find_by_index(:age, 10..20).should == []
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should find by the special $bucket index" do
|
36
|
+
Indexer.find_by_index('$bucket', Indexer.bucket.name).should =~ [@bob, @sally, @mary]
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should find by the special $key index" do
|
40
|
+
Indexer.find_by_index('$key', @bob.key..@bob.key.succ).should =~ [@bob]
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should raise an error when the requested index doesn't exist" do
|
44
|
+
lambda { Indexer.find_by_index(:hair, 'blonde') }.should raise_error(ArgumentError)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -1,122 +1,112 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Ripple::Associations::ManyEmbeddedProxy do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
before :each do
|
9
|
-
@user = User.new(:email => "riak@ripple.com")
|
10
|
-
@address = Address.new
|
11
|
-
@addr = Address.new(:street => '123 Somewhere')
|
12
|
-
@note = Note.new
|
13
|
-
end
|
4
|
+
let(:user){ User.new(:email => "riak@ripple.com") }
|
5
|
+
let(:address){ Address.new }
|
6
|
+
let(:addr){ Address.new(:street => '123 Somewhere') }
|
7
|
+
let(:note){ Note.new }
|
14
8
|
|
15
9
|
it "should not have children before any are set" do
|
16
|
-
|
10
|
+
user.addresses.should == []
|
17
11
|
end
|
18
12
|
|
19
13
|
it "should be able to set and get its children" do
|
20
|
-
|
21
|
-
|
22
|
-
@user.addresses.should == [@address]
|
14
|
+
user.addresses = [address]
|
15
|
+
user.addresses.should == [address]
|
23
16
|
end
|
24
17
|
|
25
18
|
it "should set the parent document on the children when assigning" do
|
26
|
-
|
27
|
-
|
19
|
+
user.addresses = [address]
|
20
|
+
address._parent_document.should == user
|
28
21
|
end
|
29
22
|
|
30
23
|
it "should return the assignment when assigning" do
|
31
|
-
rtn =
|
32
|
-
rtn.should == [
|
24
|
+
rtn = user.addresses = [address]
|
25
|
+
rtn.should == [address]
|
33
26
|
end
|
34
27
|
|
35
28
|
it "should set the parent document on the children when accessing" do
|
36
|
-
|
37
|
-
|
29
|
+
user.addresses = [address]
|
30
|
+
user.addresses.first._parent_document.should == user
|
38
31
|
end
|
39
32
|
|
40
33
|
it "should be able to replace its children with different children" do
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
34
|
+
user.addresses = [address]
|
35
|
+
user.addresses.first.street.should be_blank
|
36
|
+
user.addresses = [addr]
|
37
|
+
user.addresses.first.street.should == '123 Somewhere'
|
45
38
|
end
|
46
39
|
|
47
40
|
it "should be able to add to its children" do
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
@user.addresses.should == [@address, @address]
|
41
|
+
user.addresses = [address]
|
42
|
+
user.addresses << addr
|
43
|
+
user.addresses.should == [address, addr]
|
52
44
|
end
|
53
45
|
|
54
46
|
it "should be able to chain calls to adding children" do
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
@user.addresses.should == [@address, @address, @address, @address]
|
47
|
+
user.addresses = [address]
|
48
|
+
user.addresses << address << address << address
|
49
|
+
user.addresses.should == [address, address, address, address]
|
59
50
|
end
|
60
51
|
|
61
52
|
it "should set the parent document when adding to its children" do
|
62
|
-
|
63
|
-
|
53
|
+
user.addresses << address
|
54
|
+
user.addresses.first._parent_document.should == user
|
64
55
|
end
|
65
56
|
|
66
57
|
it "should be able to count its children" do
|
67
|
-
|
68
|
-
|
58
|
+
user.addresses = [address, address]
|
59
|
+
user.addresses.count.should == 2
|
69
60
|
end
|
70
61
|
|
71
62
|
it "should be able to build a new child" do
|
72
|
-
|
73
|
-
@user.addresses.build.should == @address
|
63
|
+
user.addresses.build.should be_kind_of(Address)
|
74
64
|
end
|
75
65
|
|
76
66
|
it "should assign a parent to the children created with instantiate_target" do
|
77
|
-
|
78
|
-
|
79
|
-
@user.addresses.build._parent_document.should == @user
|
67
|
+
address._parent_document.should be_nil
|
68
|
+
user.addresses.build._parent_document.should == user
|
80
69
|
end
|
81
70
|
|
82
71
|
it "should validate the children when saving the parent" do
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
72
|
+
user.valid?.should be_true
|
73
|
+
user.addresses << address
|
74
|
+
address.valid?.should be_false
|
75
|
+
user.valid?.should be_false
|
87
76
|
end
|
88
77
|
|
89
78
|
it "should not save the root document when a child is invalid" do
|
90
|
-
|
91
|
-
|
79
|
+
user.addresses << address
|
80
|
+
user.save.should be_false
|
92
81
|
end
|
93
82
|
|
94
83
|
it "should allow embedding documents in embedded documents" do
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
84
|
+
user.addresses << address
|
85
|
+
address.notes << note
|
86
|
+
note._root_document.should == user
|
87
|
+
note._parent_document.should == address
|
99
88
|
end
|
100
89
|
|
101
90
|
it "should allow assiging child documents as an array of hashes" do
|
102
|
-
|
103
|
-
|
91
|
+
user.attributes = {'addresses' => [{'street' => '123 Somewhere'}]}
|
92
|
+
user.addresses.first.street.should == '123 Somewhere'
|
104
93
|
end
|
105
94
|
|
106
95
|
it "should return an array from to_ary" do
|
107
|
-
|
108
|
-
|
109
|
-
@user.addresses.to_ary.should == [@address]
|
96
|
+
user.addresses << address
|
97
|
+
user.addresses.to_ary.should == [address]
|
110
98
|
end
|
111
99
|
|
112
100
|
it "should refuse assigning documents of the wrong type" do
|
113
|
-
lambda {
|
114
|
-
lambda {
|
115
|
-
lambda {
|
101
|
+
lambda { user.addresses = nil }.should raise_error
|
102
|
+
lambda { user.addresses = address }.should raise_error
|
103
|
+
lambda { user.addresses = [note] }.should raise_error
|
116
104
|
end
|
117
105
|
|
118
106
|
it "should not add the associated validator multiple times" do
|
119
|
-
|
107
|
+
# TODO: the validator is added lazily on first instantiation of
|
108
|
+
# the proxy. This is a code smell!
|
109
|
+
user.addresses
|
120
110
|
User.validators_on(:addresses).count.should eq(1)
|
121
111
|
end
|
122
112
|
end
|
@@ -58,7 +58,7 @@ describe Ripple::Associations::ManyLinkedProxy do
|
|
58
58
|
@person.tasks.__send__(:should_receive, :reset)
|
59
59
|
@person.tasks.__send__(:links).should == [@task.robject.to_link("tasks")]
|
60
60
|
@person.tasks.replace_links([@other_task, @third_task].map { |t| t.robject.to_link("tasks") })
|
61
|
-
@person.tasks.__send__(:links).should
|
61
|
+
@person.tasks.__send__(:links).should =~ [@other_task, @third_task].map { |t| t.robject.to_link("tasks") }
|
62
62
|
end
|
63
63
|
|
64
64
|
it "should replace associated documents with a new set" do
|
@@ -151,7 +151,7 @@ describe Ripple::Associations::ManyLinkedProxy do
|
|
151
151
|
|
152
152
|
it "returns a set of keys" do
|
153
153
|
@person.tasks.keys.should be_a(Set)
|
154
|
-
@person.tasks.keys.to_a.should
|
154
|
+
@person.tasks.keys.to_a.should =~ link_keys
|
155
155
|
end
|
156
156
|
|
157
157
|
it "is memoized between calls" do
|
@@ -123,7 +123,7 @@ describe Ripple::Associations::ManyReferenceProxy do
|
|
123
123
|
|
124
124
|
it "returns a set of keys" do
|
125
125
|
@account.payment_methods.keys.should be_a(Set)
|
126
|
-
@account.payment_methods.keys.to_a.should
|
126
|
+
@account.payment_methods.keys.to_a.should =~ ze_keys
|
127
127
|
end
|
128
128
|
|
129
129
|
it "is memoized between calls" do
|
@@ -11,22 +11,23 @@ describe Ripple::Associations do
|
|
11
11
|
end
|
12
12
|
|
13
13
|
it "should collect the embedded associations" do
|
14
|
-
Invoice.embedded_associations.should ==
|
14
|
+
Invoice.embedded_associations.should == [ Invoice.associations[:note] ]
|
15
15
|
end
|
16
16
|
|
17
17
|
it "should collect the linked associations" do
|
18
|
-
Invoice.linked_associations.should ==
|
18
|
+
Invoice.linked_associations.should == [ Invoice.associations[:customer] ]
|
19
19
|
end
|
20
20
|
|
21
21
|
it "should collect the stored_key associations" do
|
22
|
-
Account.stored_key_associations.should ==
|
23
|
-
Transaction.stored_key_associations.should ==
|
22
|
+
Account.stored_key_associations.should == [ Account.associations[:transactions] ]
|
23
|
+
Transaction.stored_key_associations.should == [ Transaction.associations[:account] ]
|
24
24
|
end
|
25
25
|
|
26
26
|
it "should copy associations to a subclass" do
|
27
27
|
Invoice.associations[:foo] = "bar"
|
28
28
|
class SubInvoice < Invoice; end
|
29
29
|
SubInvoice.associations[:foo].should == "bar"
|
30
|
+
Invoice.associations.delete :foo
|
30
31
|
end
|
31
32
|
|
32
33
|
describe "when adding a :many association" do
|
@@ -128,9 +129,6 @@ describe Ripple::Association do
|
|
128
129
|
end
|
129
130
|
|
130
131
|
describe "key correspondence" do
|
131
|
-
require 'support/models/profile'
|
132
|
-
require 'support/models/user'
|
133
|
-
|
134
132
|
it "should raise an exception if trying to use a many association when :using => :key" do
|
135
133
|
expect { Profile.many :user, :using => :key }.to raise_error(ArgumentError, "You cannot use a many association using :key")
|
136
134
|
end
|
@@ -141,4 +139,15 @@ describe Ripple::Association do
|
|
141
139
|
Profile.new.should respond_to(:key_delegate=)
|
142
140
|
end
|
143
141
|
end
|
142
|
+
|
143
|
+
describe "stored key" do
|
144
|
+
it "should raise an error when the target property does not exist" do
|
145
|
+
expect {
|
146
|
+
Class.new do
|
147
|
+
include Ripple::Document
|
148
|
+
many :transactions, :using => :stored_key
|
149
|
+
end
|
150
|
+
}.to raise_error(ArgumentError)
|
151
|
+
end
|
152
|
+
end
|
144
153
|
end
|
@@ -140,7 +140,7 @@ describe Ripple::AttributeMethods do
|
|
140
140
|
|
141
141
|
describe "#attributes" do
|
142
142
|
it "it returns a hash representation of all of the attributes" do
|
143
|
-
@widget.attributes.should == {"name" => "widget", "size" => nil, "manufactured" => false, "shipped_at" => nil}
|
143
|
+
@widget.attributes.should == {"name" => "widget", "size" => nil, "manufactured" => false, "shipped_at" => nil, "restricted" => false}
|
144
144
|
end
|
145
145
|
|
146
146
|
it "does not include ghost attributes (attributes that do not have a defined property)" do
|
@@ -157,7 +157,8 @@ describe Ripple::AttributeMethods do
|
|
157
157
|
'size' => nil,
|
158
158
|
'manufactured' => false,
|
159
159
|
'shipped_at' => nil,
|
160
|
-
'some_undefined_prop' => 17
|
160
|
+
'some_undefined_prop' => 17,
|
161
|
+
'restricted' => false,
|
161
162
|
}
|
162
163
|
end
|
163
164
|
end
|
@@ -227,4 +228,44 @@ describe Ripple::AttributeMethods do
|
|
227
228
|
lambda { @widget = Widget.new(:explode => '?BOOM') }.should raise_error(ArgumentError, %q[Undefined property :explode for class 'Widget'])
|
228
229
|
end
|
229
230
|
|
231
|
+
it "should allow mass assigning arbitrary attributes when without_protection is specified" do
|
232
|
+
@widget = Widget.new({:manufactured => true}, :without_protection => true)
|
233
|
+
@widget[:manufactured].should be_true
|
234
|
+
|
235
|
+
@client = Ripple.client
|
236
|
+
@client.stub(:store_object => true)
|
237
|
+
|
238
|
+
@widget = Widget.create({:manufactured => true}, :without_protection => true)
|
239
|
+
@widget[:manufactured].should be_true
|
240
|
+
|
241
|
+
@widget = Widget.create!({:manufactured => true}, :without_protection => true)
|
242
|
+
@widget[:manufactured].should be_true
|
243
|
+
end
|
244
|
+
|
245
|
+
it "default assign_attributes should respect mass attribute assignment security" do
|
246
|
+
@widget = Widget.new
|
247
|
+
@widget.assign_attributes(:manufactured => true)
|
248
|
+
@widget.manufactured.should be_false
|
249
|
+
end
|
250
|
+
|
251
|
+
if(ActiveModel::VERSION::MAJOR == 3 && ActiveModel::VERSION::MINOR == 0)
|
252
|
+
it "assigning attributes should respect roles" do
|
253
|
+
@widget = Widget.new
|
254
|
+
@widget.assign_attributes(:restricted => true)
|
255
|
+
@widget.restricted.should be_false
|
256
|
+
|
257
|
+
lambda do
|
258
|
+
@widget.assign_attributes({:restricted => true}, :as => :admin)
|
259
|
+
end.should raise_error(ArgumentError, %q[Roles for mass assignment are not supported for Rails 3.0])
|
260
|
+
end
|
261
|
+
else
|
262
|
+
it "assigning attributes with a role should raise an error" do
|
263
|
+
@widget = Widget.new
|
264
|
+
@widget.assign_attributes(:restricted => true)
|
265
|
+
@widget.restricted.should be_false
|
266
|
+
|
267
|
+
@widget.assign_attributes({:restricted => true}, :as => :admin)
|
268
|
+
@widget.restricted.should be_true
|
269
|
+
end
|
270
|
+
end
|
230
271
|
end
|
@@ -1,175 +1,194 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Ripple::Callbacks do
|
4
|
-
|
4
|
+
let(:doc) do
|
5
|
+
_e = embedded
|
6
|
+
Class.new do
|
7
|
+
include Ripple::Document
|
8
|
+
self.bucket_name = "docs"
|
9
|
+
many :embeddeds, :class => _e
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:embedded) do
|
14
|
+
Class.new do
|
15
|
+
include Ripple::EmbeddedDocument
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
subject { doc.new }
|
5
20
|
|
6
21
|
it "should add create, update, save, and destroy callback declarations" do
|
7
22
|
[:save, :create, :update, :destroy].each do |event|
|
8
|
-
|
23
|
+
doc.private_instance_methods.map(&:to_s).should include("_run_#{event}_callbacks")
|
9
24
|
[:before, :after, :around].each do |time|
|
10
|
-
|
25
|
+
doc.should respond_to("#{time}_#{event}")
|
11
26
|
end
|
12
27
|
end
|
13
28
|
end
|
14
29
|
|
15
30
|
it "should validate callback declarations" do
|
16
|
-
|
17
|
-
|
18
|
-
|
31
|
+
doc.private_instance_methods.map(&:to_s).should include("_run_validation_callbacks")
|
32
|
+
doc.should respond_to("before_validation")
|
33
|
+
doc.should respond_to("after_validation")
|
19
34
|
end
|
20
35
|
|
21
36
|
describe "invoking callbacks" do
|
22
37
|
before :each do
|
23
|
-
response = {:headers => {"content-type" => ["application/json"]}, :body => "{}"}
|
24
38
|
@client = Ripple.client
|
25
|
-
@
|
26
|
-
@client.stub!(:backend).and_return(@backend)
|
27
|
-
$pinger = mock("callback verifier")
|
39
|
+
@client.stub(:store_object => true)
|
28
40
|
end
|
29
41
|
|
30
42
|
it "should call save callbacks on save" do
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
43
|
+
callbacks = []
|
44
|
+
doc.before_save { callbacks << :before }
|
45
|
+
doc.after_save { callbacks << :after }
|
46
|
+
doc.around_save(lambda { callbacks << :around })
|
47
|
+
subject.save
|
48
|
+
callbacks.should == [ :before, :around, :after ]
|
37
49
|
end
|
38
50
|
|
39
51
|
it "propagates callbacks to embedded associated documents" do
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
52
|
+
callbacks = []
|
53
|
+
doc.before_save { callbacks << :box }
|
54
|
+
embedded.before_save { callbacks << :side }
|
55
|
+
subject.embeddeds << embedded.new
|
56
|
+
subject.save
|
57
|
+
callbacks.should == [:side, :box]
|
46
58
|
end
|
47
59
|
|
48
60
|
it 'does not persist the object to riak multiple times when propagating callbacks' do
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
@box.sides << BoxSide.new << BoxSide.new
|
61
|
+
doc.before_save { }
|
62
|
+
embedded.before_save { }
|
63
|
+
subject.embeddeds << embedded.new << embedded.new
|
53
64
|
|
54
|
-
|
55
|
-
|
65
|
+
subject.robject.should_receive(:store).once
|
66
|
+
subject.save
|
56
67
|
end
|
57
68
|
|
58
69
|
it 'invokes the before/after callbacks in the correct order on embedded associated documents' do
|
59
70
|
callbacks = []
|
60
|
-
|
61
|
-
|
71
|
+
embedded.before_save { callbacks << :before_save }
|
72
|
+
embedded.after_save { callbacks << :after_save }
|
62
73
|
|
63
|
-
|
64
|
-
|
65
|
-
@box.robject.stub(:store) do
|
74
|
+
subject.embeddeds << embedded.new
|
75
|
+
subject.robject.stub(:store) do
|
66
76
|
callbacks << :save
|
67
77
|
end
|
68
|
-
|
78
|
+
subject.save
|
69
79
|
|
70
80
|
callbacks.should == [:before_save, :save, :after_save]
|
71
81
|
end
|
72
82
|
|
73
83
|
it 'does not allow around callbacks on embedded associated documents' do
|
74
84
|
expect {
|
75
|
-
|
85
|
+
embedded.around_save { }
|
76
86
|
}.to raise_error(/around_save callbacks are not supported/)
|
77
87
|
end
|
78
88
|
|
79
89
|
it 'does not propagate validation callbacks multiple times' do
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
90
|
+
callbacks = []
|
91
|
+
doc.before_validation { callbacks << :box }
|
92
|
+
embedded.before_validation { callbacks << :side }
|
93
|
+
|
94
|
+
subject.embeddeds << embedded.new
|
95
|
+
subject.valid?
|
96
|
+
callbacks.should == [:box, :side]
|
86
97
|
end
|
87
98
|
|
88
99
|
it "should call create callbacks on save when the document is new" do
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
100
|
+
callbacks = []
|
101
|
+
doc.before_create { callbacks << :before }
|
102
|
+
doc.after_create { callbacks << :after }
|
103
|
+
doc.around_create(lambda { callbacks << :around })
|
104
|
+
|
105
|
+
subject.save
|
106
|
+
callbacks.should == [:before, :around, :after ]
|
95
107
|
end
|
96
108
|
|
97
109
|
it "should call update callbacks on save when the document is not new" do
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
110
|
+
callbacks = []
|
111
|
+
doc.before_update { callbacks << :before }
|
112
|
+
doc.after_update { callbacks << :after }
|
113
|
+
doc.around_update(lambda { callbacks << :around })
|
114
|
+
|
115
|
+
subject.stub!(:new?).and_return(false)
|
116
|
+
subject.save
|
117
|
+
callbacks.should == [:before, :around, :after ]
|
105
118
|
end
|
106
119
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
120
|
+
describe "destroy callbacks" do
|
121
|
+
let(:callbacks) { [] }
|
122
|
+
|
123
|
+
before(:each) do
|
124
|
+
_callbacks = callbacks
|
125
|
+
doc.before_destroy { _callbacks << :before }
|
126
|
+
doc.after_destroy { _callbacks << :after }
|
127
|
+
doc.around_destroy(lambda { _callbacks << :around })
|
128
|
+
end
|
129
|
+
|
130
|
+
after { callbacks.should == [ :before, :around, :after ] }
|
131
|
+
|
132
|
+
it "invokes them when #destroy is called" do
|
133
|
+
subject.destroy
|
134
|
+
end
|
135
|
+
|
136
|
+
it "invokes them when #destroy! is called" do
|
137
|
+
subject.destroy!
|
138
|
+
end
|
114
139
|
end
|
115
140
|
|
116
141
|
it "should call save and validate callbacks in the correct order" do
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
142
|
+
callbacks = []
|
143
|
+
doc.before_validation { callbacks << :validation }
|
144
|
+
doc.before_save { callbacks << :save }
|
145
|
+
|
146
|
+
subject.save
|
147
|
+
callbacks.should == [:validation, :save]
|
123
148
|
end
|
124
149
|
|
125
150
|
describe "validation callbacks" do
|
126
151
|
it "should call validation callbacks" do
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
152
|
+
callbacks = []
|
153
|
+
doc.before_validation { callbacks << :before }
|
154
|
+
doc.after_validation { callbacks << :after }
|
155
|
+
subject.valid?
|
156
|
+
callbacks.should == [:before, :after]
|
132
157
|
end
|
133
158
|
|
134
159
|
it "should call validation callbacks only if the document is new" do
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
160
|
+
callbacks = []
|
161
|
+
doc.before_validation(:on => :create) { callbacks << :before }
|
162
|
+
doc.after_validation(:on => :create) { callbacks << :after }
|
163
|
+
subject.valid?
|
164
|
+
callbacks.should == [:before, :after]
|
140
165
|
end
|
141
166
|
|
142
167
|
it "should not call validation callbacks only if the document is new" do
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
168
|
+
callbacks = []
|
169
|
+
doc.before_validation(:on => :update) { callbacks << :before }
|
170
|
+
doc.after_validation(:on => :update) { callbacks << :after }
|
171
|
+
subject.valid?
|
172
|
+
callbacks.should be_empty
|
148
173
|
end
|
149
174
|
|
150
175
|
it "should call validation callbacks only if the document is not new" do
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
@box = Box.new
|
155
|
-
@box.stub(:new?).and_return(false)
|
156
|
-
@box.valid?
|
157
|
-
end
|
176
|
+
callbacks = []
|
177
|
+
doc.before_validation(:on => :update) { callbacks << :before }
|
178
|
+
doc.after_validation(:on => :update) { callbacks << :after }
|
158
179
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
$pinger.should_not_receive(:ping)
|
163
|
-
@box = Box.new
|
164
|
-
@box.stub!(:new?).and_return(false)
|
165
|
-
@box.valid?
|
180
|
+
subject.stub(:new?).and_return(false)
|
181
|
+
subject.valid?
|
182
|
+
callbacks.should == [:before, :after]
|
166
183
|
end
|
167
|
-
end
|
168
184
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
185
|
+
it "should not call validation callbacks only if the document is not new" do
|
186
|
+
callbacks = []
|
187
|
+
doc.before_validation(:on => :create) { callbacks << :validation }
|
188
|
+
doc.after_validation(:on => :create) { callbacks << :after }
|
189
|
+
subject.stub!(:new?).and_return(false)
|
190
|
+
subject.valid?
|
191
|
+
callbacks.should be_empty
|
173
192
|
end
|
174
193
|
end
|
175
194
|
end
|