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
@@ -2,21 +2,13 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Ripple::Conversion do
|
4
4
|
# require 'support/models/box'
|
5
|
+
subject { Box.new { |a| a.key = 'some-key' } }
|
5
6
|
|
6
7
|
before :each do
|
7
|
-
|
8
|
-
@box.stub!(:new?).and_return(false)
|
8
|
+
subject.stub!(:new?).and_return(false)
|
9
9
|
end
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
it "should be able to be converted to a param" do
|
16
|
-
@box.to_param.should == 'some-key'
|
17
|
-
end
|
18
|
-
|
19
|
-
it "should be able to be converted to a model" do
|
20
|
-
@box.to_model.should == @box
|
21
|
-
end
|
11
|
+
its(:to_key) { should == ['some-key'] }
|
12
|
+
its(:to_param){ should == 'some-key' }
|
13
|
+
its(:to_model){ should == subject }
|
22
14
|
end
|
@@ -3,64 +3,103 @@ require 'spec_helper'
|
|
3
3
|
describe Time do
|
4
4
|
before { @date_format = Ripple.date_format }
|
5
5
|
after { Ripple.date_format = @date_format }
|
6
|
-
|
6
|
+
subject { Time.utc(2010,3,16,12) }
|
7
7
|
it "serializes to JSON in UTC, ISO 8601 format by default" do
|
8
|
-
|
8
|
+
subject.as_json.should == "2010-03-16T12:00:00Z"
|
9
9
|
end
|
10
10
|
|
11
11
|
it "serializes to JSON in UTC, RFC822 format when specified" do
|
12
12
|
Ripple.date_format = :rfc822
|
13
|
-
|
13
|
+
subject.as_json.should == "Tue, 16 Mar 2010 12:00:00 -0000"
|
14
|
+
end
|
15
|
+
|
16
|
+
context "converting to an index value" do
|
17
|
+
it "should convert to an integer epoch milliseconds for 'int' type" do
|
18
|
+
subject.to_ripple_index('int').should == 1268740800000
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should convert to the date format for 'bin' type" do
|
22
|
+
subject.to_ripple_index('bin').should == "2010-03-16T12:00:00Z"
|
23
|
+
end
|
14
24
|
end
|
15
25
|
end
|
16
26
|
|
17
27
|
describe Date do
|
18
28
|
before { @date_format = Ripple.date_format }
|
19
29
|
after { Ripple.date_format = @date_format }
|
20
|
-
|
30
|
+
subject { Date.civil(2010,3,16) }
|
21
31
|
it "serializes to JSON ISO 8601 format by default" do
|
22
|
-
|
32
|
+
subject.as_json.should == "2010-03-16"
|
23
33
|
end
|
24
34
|
|
25
35
|
it "serializes to JSON in UTC, RFC822 format when specified" do
|
26
36
|
Ripple.date_format = :rfc822
|
27
|
-
|
37
|
+
subject.as_json.should == "16 Mar 2010"
|
38
|
+
end
|
39
|
+
|
40
|
+
context "converting to an index value" do
|
41
|
+
it "should convert to an integer epoch milliseconds for 'int' type" do
|
42
|
+
subject.to_ripple_index('int').should == 1268697600000
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should convert to the date format for 'bin' type" do
|
46
|
+
subject.to_ripple_index('bin').should == "2010-03-16"
|
47
|
+
end
|
28
48
|
end
|
29
49
|
end
|
30
50
|
|
31
51
|
describe DateTime do
|
32
52
|
before { @date_format = Ripple.date_format }
|
33
53
|
after { Ripple.date_format = @date_format }
|
34
|
-
|
54
|
+
subject { DateTime.civil(2010,3,16,12) }
|
35
55
|
before :each do
|
36
56
|
Time.zone = "UTC"
|
37
57
|
end
|
38
58
|
|
39
59
|
it "serializes to JSON in UTC, ISO 8601 format by default" do
|
40
|
-
|
60
|
+
subject.as_json.should == "2010-03-16T12:00:00+00:00"
|
41
61
|
end
|
42
62
|
|
43
63
|
it "serializes to JSON in UTC, RFC822 format when specified" do
|
44
64
|
Ripple.date_format = :rfc822
|
45
|
-
|
65
|
+
subject.as_json.should == "Tue, 16 Mar 2010 12:00:00 +0000"
|
66
|
+
end
|
67
|
+
|
68
|
+
context "converting to an index value" do
|
69
|
+
it "should convert to an integer epoch milliseconds for 'int' type" do
|
70
|
+
subject.to_ripple_index('int').should == 1268740800000
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should convert to the date format for 'bin' type" do
|
74
|
+
subject.to_ripple_index('bin').should == "2010-03-16T12:00:00+00:00"
|
75
|
+
end
|
46
76
|
end
|
47
77
|
end
|
48
78
|
|
49
79
|
describe ActiveSupport::TimeWithZone do
|
50
80
|
before { @date_format = Ripple.date_format }
|
51
81
|
after { Ripple.date_format = @date_format }
|
82
|
+
let(:time) { Time.utc(2010,3,16,12) }
|
83
|
+
let(:zone) { ActiveSupport::TimeZone['Alaska'] }
|
84
|
+
subject { ActiveSupport::TimeWithZone.new(time, zone) }
|
52
85
|
|
53
86
|
it "serializes to JSON in UTC, ISO 8601 format by default" do
|
54
|
-
|
55
|
-
zone = ActiveSupport::TimeZone['Alaska']
|
56
|
-
ActiveSupport::TimeWithZone.new(time, zone).as_json.should == "2010-03-16T12:00:00Z"
|
87
|
+
subject.as_json.should == "2010-03-16T12:00:00Z"
|
57
88
|
end
|
58
89
|
|
59
90
|
it "serializes to JSON in UTC, RFC822 format when specified" do
|
60
91
|
Ripple.date_format = :rfc822
|
61
|
-
|
62
|
-
|
63
|
-
|
92
|
+
subject.as_json.should == "Tue, 16 Mar 2010 12:00:00 -0000"
|
93
|
+
end
|
94
|
+
|
95
|
+
context "converting to an index value" do
|
96
|
+
it "should convert to an integer epoch milliseconds for 'int' type" do
|
97
|
+
subject.to_ripple_index('int').should == 1268740800000
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should convert to the date format for 'bin' type" do
|
101
|
+
subject.to_ripple_index('bin').should == "2010-03-16T12:00:00Z"
|
102
|
+
end
|
64
103
|
end
|
65
104
|
end
|
66
105
|
|
@@ -79,6 +118,23 @@ describe String do
|
|
79
118
|
'Tue, 16 Mar 2010 12:00:00 +0000'.to_datetime.should == DateTime.civil(2010,3,16,12)
|
80
119
|
'2010-03-16T12:00:00+00:00'.to_datetime.should == DateTime.civil(2010,3,16,12)
|
81
120
|
end
|
121
|
+
|
122
|
+
context "converting to an index value" do
|
123
|
+
subject { "1234" }
|
124
|
+
|
125
|
+
it "should not be treated as an Enumerable" do
|
126
|
+
subject.should_not_receive(:map)
|
127
|
+
subject.to_ripple_index('bin')
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should convert to a binary index" do
|
131
|
+
subject.to_ripple_index('bin').should == subject
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should convert to an integer index" do
|
135
|
+
subject.to_ripple_index('int').should == 1234
|
136
|
+
end
|
137
|
+
end
|
82
138
|
end
|
83
139
|
|
84
140
|
describe "Boolean" do
|
@@ -101,3 +157,25 @@ describe Set do
|
|
101
157
|
end
|
102
158
|
end
|
103
159
|
|
160
|
+
describe Enumerable do
|
161
|
+
subject { [1,2,3] }
|
162
|
+
let(:int) { subject.to_ripple_index('int') }
|
163
|
+
let(:bin) { subject.to_ripple_index('bin') }
|
164
|
+
|
165
|
+
context "converting to an index value" do
|
166
|
+
it "should convert to a Set of strings for 'bin' type" do
|
167
|
+
bin.should be_kind_of(Set)
|
168
|
+
%w{1 2 3}.each do |i|
|
169
|
+
bin.should include(i)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
it "should convert to a Set of integers for 'int' type" do
|
174
|
+
int.should be_kind_of(Set)
|
175
|
+
subject.each do |i|
|
176
|
+
int.should include(i)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
data/spec/ripple/finders_spec.rb
CHANGED
@@ -5,9 +5,7 @@ describe Ripple::Document::Finders do
|
|
5
5
|
# require 'support/models/cardboard_box'
|
6
6
|
|
7
7
|
before :each do
|
8
|
-
@backend = mock("Backend")
|
9
8
|
@client = Ripple.client
|
10
|
-
@client.stub!(:backend).and_return(@backend)
|
11
9
|
@bucket = Ripple.client.bucket("boxes")
|
12
10
|
@plain = Riak::RObject.new(@bucket, "square"){|o| o.content_type = "application/json"; o.raw_data = '{"shape":"square"}'}
|
13
11
|
@cb = Riak::RObject.new(@bucket, "rectangle"){|o| o.content_type = "application/json"; o.raw_data = '{"shape":"rectangle"}'}
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ripple::Indexes do
|
4
|
+
context "class methods" do
|
5
|
+
subject { Indexer }
|
6
|
+
it { should respond_to(:indexes) }
|
7
|
+
it { should have(4).indexes }
|
8
|
+
|
9
|
+
it "should remove the :index key from the property options" do
|
10
|
+
subject.properties[:name].options.should_not include(:index)
|
11
|
+
subject.properties[:age].options.should_not include(:index)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should not have a property for synthetic indexes" do
|
15
|
+
subject.properties[:name_age].should == nil
|
16
|
+
subject.properties[:name_greeting].should == nil
|
17
|
+
end
|
18
|
+
end
|
19
|
+
context "inherited indexes" do
|
20
|
+
subject { SubIndexer }
|
21
|
+
it { should have(5).indexes }
|
22
|
+
it "should have inherited indexes" do
|
23
|
+
subject.indexes.keys.should include('age', 'name', 'name_age', 'name_greeting')
|
24
|
+
end
|
25
|
+
it "should have indexes defined on the subclass" do
|
26
|
+
subject.indexes.keys.should include('height')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "instance methods" do
|
31
|
+
before { subject.robject.stub!(:store).and_return(true) }
|
32
|
+
subject { Indexer.new(:name => "Bob", :age => 28) }
|
33
|
+
it { should respond_to(:indexes_for_persistence) }
|
34
|
+
its(:indexes_for_persistence) { should include('age_int') }
|
35
|
+
its(:indexes_for_persistence) { should include('name_bin') }
|
36
|
+
its(:indexes_for_persistence) { should include('name_age_bin') }
|
37
|
+
its(:indexes_for_persistence) { should include('name_greeting_bin') }
|
38
|
+
its(:indexes_for_persistence) { should be_all {|_,i| i.kind_of?(Set) } }
|
39
|
+
|
40
|
+
it "should set the indexes on the internal Riak object when saving" do
|
41
|
+
subject.save
|
42
|
+
subject.robject.indexes.should_not be_empty
|
43
|
+
subject.robject.indexes["name_bin"].should == Set["Bob"]
|
44
|
+
subject.robject.indexes["age_int"].should == Set[28]
|
45
|
+
subject.robject.indexes["name_age_bin"].should == Set["Bob-28"]
|
46
|
+
subject.robject.indexes["name_greeting_bin"].should == Set["Bob: Hello!"]
|
47
|
+
end
|
48
|
+
|
49
|
+
context "when embedded documents have indexes" do
|
50
|
+
subject do
|
51
|
+
Indexer.new(:name => "Bob",
|
52
|
+
:age => 28,
|
53
|
+
:addresses => [{:street => "10 Main St", :city => "Anywhere"},
|
54
|
+
{:street => "100 W 10th Avenue", :city => "Springfield"}],
|
55
|
+
:primary_address => {
|
56
|
+
:street => "1 W 5th Place",
|
57
|
+
:city => "Independence"})
|
58
|
+
end
|
59
|
+
|
60
|
+
its(:indexes_for_persistence){ should include("primary_address_street_bin") }
|
61
|
+
its(:indexes_for_persistence){ should include("primary_address_city_bin") }
|
62
|
+
its(:indexes_for_persistence){ should include("addresses_street_bin") }
|
63
|
+
its(:indexes_for_persistence){ should include("addresses_city_bin") }
|
64
|
+
|
65
|
+
it "should merge indexes for many embedded associations" do
|
66
|
+
subject.indexes_for_persistence['addresses_city_bin'].should == Set["Anywhere", "Springfield"]
|
67
|
+
subject.indexes_for_persistence['addresses_street_bin'].should == Set["10 Main St", "100 W 10th Avenue"]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe Ripple::Index do
|
74
|
+
it "should use a binary index when the type is a String" do
|
75
|
+
Ripple::Index.new('foo', String).index_type.should == 'bin'
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should use an integer index when the type is an Integer" do
|
79
|
+
Ripple::Index.new('foo', Integer).index_type.should == 'int'
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should use an integer index when the type is a time" do
|
83
|
+
[Time, Date, ActiveSupport::TimeWithZone].each do |klass|
|
84
|
+
Ripple::Index.new('foo', klass).index_type.should == 'int'
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should use an integer index when index type is Integer" do
|
89
|
+
Ripple::Index.new('foo', String, Integer).index_type.should == 'int'
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should use a binary index when index type is String" do
|
93
|
+
Ripple::Index.new('foo', Integer, String).index_type.should == 'bin'
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should raise an error when the index type cannot be determined" do
|
97
|
+
klass = Class.new
|
98
|
+
expect { Ripple::Index.new('foo', klass).index_type }.to raise_error(ArgumentError)
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should use the specified index type" do
|
102
|
+
Ripple::Index.new('foo', Time, 'bin').index_type.should == 'bin'
|
103
|
+
Ripple::Index.new('foo', Time, String).index_type.should == 'bin'
|
104
|
+
Ripple::Index.new('foo', Time, Integer).index_type.should == 'int'
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should provide an index key that includes the type" do
|
108
|
+
Ripple::Index.new('foo', Integer).index_key.should == 'foo_int'
|
109
|
+
Ripple::Index.new('foo', String).index_key.should == 'foo_bin'
|
110
|
+
end
|
111
|
+
end
|
@@ -6,8 +6,7 @@ describe Ripple::Observable do
|
|
6
6
|
|
7
7
|
before :each do
|
8
8
|
@client = Ripple.client
|
9
|
-
@
|
10
|
-
@client.stub!(:backend).and_return(@backend)
|
9
|
+
@client.stub(:store_object => true)
|
11
10
|
@clock = Clock.new
|
12
11
|
@observer = ClockObserver.instance
|
13
12
|
end
|
@@ -4,9 +4,7 @@ describe Ripple::Document::Persistence do
|
|
4
4
|
# require 'support/models/widget'
|
5
5
|
|
6
6
|
before :each do
|
7
|
-
@backend = mock("Backend")
|
8
7
|
@client = Ripple.client
|
9
|
-
@client.stub!(:backend).and_return(@backend)
|
10
8
|
@bucket = Ripple.client.bucket("widgets")
|
11
9
|
@widget = Widget.new(:size => 1000)
|
12
10
|
end
|
@@ -14,7 +12,7 @@ describe Ripple::Document::Persistence do
|
|
14
12
|
it "forces the content type to 'application/json'" do
|
15
13
|
@widget.robject.content_type = 'application/not-json'
|
16
14
|
|
17
|
-
@
|
15
|
+
@client.should_receive(:store_object) do |obj, *_|
|
18
16
|
obj.content_type.should == 'application/json'
|
19
17
|
end
|
20
18
|
|
@@ -22,9 +20,9 @@ describe Ripple::Document::Persistence do
|
|
22
20
|
end
|
23
21
|
|
24
22
|
it "should save a new object to Riak" do
|
25
|
-
json = @widget.attributes.merge("_type" => "Widget")
|
26
|
-
@
|
27
|
-
obj.
|
23
|
+
json = @widget.attributes.merge("_type" => "Widget")
|
24
|
+
@client.should_receive(:store_object) do |obj, _, _, _|
|
25
|
+
obj.data.should == json
|
28
26
|
obj.key.should be_nil
|
29
27
|
# Simulate loading the response with the key
|
30
28
|
obj.key = "new_widget"
|
@@ -36,9 +34,9 @@ describe Ripple::Document::Persistence do
|
|
36
34
|
end
|
37
35
|
|
38
36
|
it "should modify attributes and save a new object" do
|
39
|
-
json = @widget.attributes.merge("_type" => "Widget", "size" => 5)
|
40
|
-
@
|
41
|
-
obj.
|
37
|
+
json = @widget.attributes.merge("_type" => "Widget", "size" => 5)
|
38
|
+
@client.should_receive(:store_object) do |obj, _, _, _|
|
39
|
+
obj.data.should == json
|
42
40
|
obj.key.should be_nil
|
43
41
|
# Simulate loading the response with the key
|
44
42
|
obj.key = "new_widget"
|
@@ -50,9 +48,9 @@ describe Ripple::Document::Persistence do
|
|
50
48
|
end
|
51
49
|
|
52
50
|
it "should modify a single attribute and save a new object" do
|
53
|
-
json = @widget.attributes.merge("_type" => "Widget", "size" => 5)
|
54
|
-
@
|
55
|
-
obj.
|
51
|
+
json = @widget.attributes.merge("_type" => "Widget", "size" => 5)
|
52
|
+
@client.should_receive(:store_object) do |obj, _, _, _|
|
53
|
+
obj.data.should == json
|
56
54
|
obj.key.should be_nil
|
57
55
|
# Simulate loading the response with the key
|
58
56
|
obj.key = "new_widget"
|
@@ -65,10 +63,10 @@ describe Ripple::Document::Persistence do
|
|
65
63
|
end
|
66
64
|
|
67
65
|
it "should instantiate and save a new object to riak" do
|
68
|
-
json = @widget.attributes.merge(:size => 10, :shipped_at =>
|
69
|
-
@
|
70
|
-
obj.raw_data.should == json
|
66
|
+
json = @widget.attributes.merge(:size => 10, :shipped_at => Time.utc(2000,"jan",1,20,15,1), :_type => 'Widget')
|
67
|
+
@client.should_receive(:store_object) do |obj, _, _, _|
|
71
68
|
obj.key.should be_nil
|
69
|
+
obj.data.should == json
|
72
70
|
# Simulate loading the response with the key
|
73
71
|
obj.key = "new_widget"
|
74
72
|
end
|
@@ -79,9 +77,9 @@ describe Ripple::Document::Persistence do
|
|
79
77
|
end
|
80
78
|
|
81
79
|
it "should instantiate and save a new object to riak and allow its attributes to be set via a block" do
|
82
|
-
json = @widget.attributes.merge(:size => 10, :_type => 'Widget')
|
83
|
-
@
|
84
|
-
obj.
|
80
|
+
json = @widget.attributes.merge(:size => 10, :_type => 'Widget')
|
81
|
+
@client.should_receive(:store_object) do |obj, _, _, _|
|
82
|
+
obj.data.should == json
|
85
83
|
obj.key.should be_nil
|
86
84
|
# Simulate loading the response with the key
|
87
85
|
obj.key = "new_widget"
|
@@ -95,7 +93,7 @@ describe Ripple::Document::Persistence do
|
|
95
93
|
|
96
94
|
it "should save the attributes not having a corresponding property" do
|
97
95
|
attrs = @widget.attributes.merge("_type" => "Widget", "unknown_property" => "a_value")
|
98
|
-
@
|
96
|
+
@client.should_receive(:store_object) do |obj, _, _, _|
|
99
97
|
obj.data.should == attrs
|
100
98
|
obj.key.should be_nil
|
101
99
|
# Simulate loading the response with the key
|
@@ -109,22 +107,22 @@ describe Ripple::Document::Persistence do
|
|
109
107
|
end
|
110
108
|
|
111
109
|
it "should allow unexpected exceptions to be raised" do
|
112
|
-
robject = mock("robject", :key => @widget.key, "data=" => true, "content_type=" => true)
|
110
|
+
robject = mock("robject", :key => @widget.key, "data=" => true, "content_type=" => true, "indexes=" => true)
|
113
111
|
robject.should_receive(:store).and_raise(Riak::HTTPFailedRequest.new(:post, 200, 404, {}, "404 not found"))
|
114
112
|
@widget.stub!(:robject).and_return(robject)
|
115
113
|
lambda { @widget.save }.should raise_error(Riak::FailedRequest)
|
116
114
|
end
|
117
115
|
|
118
116
|
it "should reload a saved object, including associations" do
|
119
|
-
json = @widget.attributes.merge(:_type => "Widget")
|
120
|
-
@
|
121
|
-
obj.
|
117
|
+
json = @widget.attributes.merge(:_type => "Widget")
|
118
|
+
@client.should_receive(:store_object) do |obj, _, _, _|
|
119
|
+
obj.data.should == json
|
122
120
|
obj.key.should be_nil
|
123
121
|
# Simulate loading the response with the key
|
124
122
|
obj.key = "new_widget"
|
125
123
|
end
|
126
124
|
@widget.save
|
127
|
-
@
|
125
|
+
@client.should_receive(:reload_object) do |obj, _|
|
128
126
|
obj.key.should == "new_widget"
|
129
127
|
obj.content_type = 'application/json'
|
130
128
|
obj.raw_data = '{"name":"spring","size":10,"shipped_at":"Sat, 01 Jan 2000 20:15:01 -0000","_type":"Widget"}'
|
@@ -140,13 +138,14 @@ describe Ripple::Document::Persistence do
|
|
140
138
|
end
|
141
139
|
|
142
140
|
it "should destroy a saved object" do
|
143
|
-
@
|
141
|
+
@client.should_receive(:store_object).and_return(true)
|
144
142
|
@widget.key = "foo"
|
145
143
|
@widget.save
|
146
144
|
@widget.should_not be_new
|
147
|
-
@
|
145
|
+
@client.should_receive(:delete_object).and_return(true)
|
148
146
|
@widget.destroy.should be_true
|
149
147
|
@widget.should be_frozen
|
148
|
+
@widget.should be_deleted
|
150
149
|
end
|
151
150
|
|
152
151
|
it "should destroy all saved objects" do
|
@@ -155,10 +154,34 @@ describe Ripple::Document::Persistence do
|
|
155
154
|
Widget.destroy_all.should be_true
|
156
155
|
end
|
157
156
|
|
157
|
+
context 'when a delete fails' do
|
158
|
+
let(:error) { Riak::FailedRequest.new("Riak could not delete your object") }
|
159
|
+
before(:each) do
|
160
|
+
@widget.stub(:new? => false)
|
161
|
+
@widget.robject.should_receive(:delete).and_raise(error)
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'causes destroy to return false' do
|
165
|
+
@widget.destroy.should be_false
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'causes destroy! to raise an error' do
|
169
|
+
expect {
|
170
|
+
@widget.destroy!
|
171
|
+
}.to raise_error(Riak::FailedRequest)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
158
175
|
it "should freeze an unsaved object when destroying" do
|
159
|
-
@
|
176
|
+
@client.should_not_receive(:delete_object)
|
177
|
+
@widget.destroy.should be_true
|
178
|
+
@widget.should be_frozen
|
179
|
+
end
|
180
|
+
|
181
|
+
it "should be able to call #errors after destroying" do
|
160
182
|
@widget.destroy.should be_true
|
161
183
|
@widget.should be_frozen
|
184
|
+
expect { @widget.errors }.to_not raise_error
|
162
185
|
end
|
163
186
|
|
164
187
|
it "should be a root document" do
|
@@ -171,9 +194,9 @@ describe Ripple::Document::Persistence do
|
|
171
194
|
end
|
172
195
|
|
173
196
|
it "should store the _type field as the class name" do
|
174
|
-
json = @cog.attributes.merge("_type" => "Cog")
|
175
|
-
@
|
176
|
-
obj.
|
197
|
+
json = @cog.attributes.merge("_type" => "Cog")
|
198
|
+
@client.should_receive(:store_object) do |obj, _, _, _|
|
199
|
+
obj.data.should == json
|
177
200
|
obj.key = "new_widget"
|
178
201
|
end
|
179
202
|
@cog.save
|
@@ -213,7 +236,7 @@ describe Ripple::Document::Persistence do
|
|
213
236
|
|
214
237
|
shared_examples_for "saving a parent document with linked child documents" do
|
215
238
|
before(:each) do
|
216
|
-
@
|
239
|
+
@client.stub(:store_object)
|
217
240
|
end
|
218
241
|
|
219
242
|
it 'saves new children when the parent is saved' do
|
@@ -279,7 +302,7 @@ describe Ripple::Document::Persistence do
|
|
279
302
|
|
280
303
|
shared_examples_for "embedded association persistence logic" do
|
281
304
|
before(:each) do
|
282
|
-
@
|
305
|
+
@client.stub(:store_object)
|
283
306
|
end
|
284
307
|
|
285
308
|
it "does not save children when the parent is saved" do
|