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.
Files changed (94) hide show
  1. data/.gitignore +10 -6
  2. data/Gemfile +6 -15
  3. data/Gemfile.rails30 +3 -0
  4. data/Gemfile.rails31 +3 -0
  5. data/Gemfile.rails32 +3 -0
  6. data/Guardfile +3 -1
  7. data/LICENSE +16 -0
  8. data/README.markdown +173 -0
  9. data/RELEASE_NOTES.textile +286 -0
  10. data/Rakefile +19 -0
  11. data/lib/rails/generators/ripple/configuration/templates/ripple.yml +1 -0
  12. data/lib/rails/generators/ripple/model/model_generator.rb +1 -1
  13. data/lib/rails/generators/ripple/model/templates/{model.rb → model.rb.erb} +0 -0
  14. data/lib/rails/generators/ripple/observer/observer_generator.rb +1 -1
  15. data/lib/rails/generators/ripple/observer/templates/{observer.rb → observer.rb.erb} +0 -2
  16. data/lib/rails/generators/ripple/test/templates/cucumber.rb.erb +7 -0
  17. data/lib/rails/generators/ripple/test/test_generator.rb +17 -13
  18. data/lib/rails/generators/ripple_generator.rb +1 -0
  19. data/lib/ripple/associations.rb +65 -55
  20. data/lib/ripple/associations/embedded.rb +1 -1
  21. data/lib/ripple/associations/linked.rb +1 -1
  22. data/lib/ripple/associations/many.rb +1 -1
  23. data/lib/ripple/associations/many_embedded_proxy.rb +3 -2
  24. data/lib/ripple/associations/many_linked_proxy.rb +1 -1
  25. data/lib/ripple/associations/many_reference_proxy.rb +7 -5
  26. data/lib/ripple/associations/proxy.rb +2 -2
  27. data/lib/ripple/attribute_methods.rb +69 -61
  28. data/lib/ripple/attribute_methods/dirty.rb +2 -2
  29. data/lib/ripple/attribute_methods/read.rb +4 -2
  30. data/lib/ripple/callbacks.rb +23 -26
  31. data/lib/ripple/conflict/basic_resolver.rb +6 -2
  32. data/lib/ripple/conflict/document_hooks.rb +26 -0
  33. data/lib/ripple/conflict/resolver.rb +10 -2
  34. data/lib/ripple/conflict/test_helper.rb +3 -2
  35. data/lib/ripple/conversion.rb +1 -0
  36. data/lib/ripple/core_ext.rb +1 -0
  37. data/lib/ripple/core_ext/casting.rb +2 -0
  38. data/lib/ripple/core_ext/indexes.rb +89 -0
  39. data/lib/ripple/document.rb +23 -22
  40. data/lib/ripple/document/key.rb +12 -14
  41. data/lib/ripple/document/persistence.rb +99 -84
  42. data/lib/ripple/embedded_document.rb +9 -10
  43. data/lib/ripple/embedded_document/persistence.rb +42 -44
  44. data/lib/ripple/i18n.rb +4 -1
  45. data/lib/ripple/indexes.rb +151 -0
  46. data/lib/ripple/locale/en.yml +4 -0
  47. data/lib/ripple/locale/fr.yml +24 -0
  48. data/lib/ripple/nested_attributes.rb +92 -90
  49. data/lib/ripple/properties.rb +2 -1
  50. data/lib/ripple/railtie.rb +9 -0
  51. data/lib/ripple/railties/ripple.rake +32 -15
  52. data/lib/ripple/serialization.rb +50 -52
  53. data/lib/ripple/test_server.rb +1 -2
  54. data/lib/ripple/timestamps.rb +6 -8
  55. data/lib/ripple/validations.rb +19 -21
  56. data/lib/ripple/version.rb +1 -1
  57. data/ripple.gemspec +6 -5
  58. data/spec/generators/ripple/configuration_generator_spec.rb +9 -0
  59. data/spec/generators/ripple/js_generator_spec.rb +14 -0
  60. data/spec/generators/ripple/model_generator_spec.rb +64 -0
  61. data/spec/generators/ripple/observer_generator_spec.rb +20 -0
  62. data/spec/generators/ripple/test_generator_spec.rb +116 -0
  63. data/spec/generators/ripple_generator_spec.rb +11 -0
  64. data/spec/integration/ripple/conflict_resolution_spec.rb +35 -4
  65. data/spec/integration/ripple/indexes_spec.rb +47 -0
  66. data/spec/ripple/associations/many_embedded_proxy_spec.rb +50 -60
  67. data/spec/ripple/associations/many_linked_proxy_spec.rb +2 -2
  68. data/spec/ripple/associations/many_reference_proxy_spec.rb +1 -1
  69. data/spec/ripple/associations_spec.rb +16 -7
  70. data/spec/ripple/attribute_methods_spec.rb +43 -2
  71. data/spec/ripple/callbacks_spec.rb +120 -101
  72. data/spec/ripple/conversion_spec.rb +5 -13
  73. data/spec/ripple/core_ext_spec.rb +93 -15
  74. data/spec/ripple/finders_spec.rb +0 -2
  75. data/spec/ripple/indexes_spec.rb +111 -0
  76. data/spec/ripple/observable_spec.rb +1 -2
  77. data/spec/ripple/persistence_spec.rb +55 -32
  78. data/spec/ripple/properties_spec.rb +1 -1
  79. data/spec/ripple/ripple_spec.rb +5 -5
  80. data/spec/ripple/timestamps_spec.rb +9 -2
  81. data/spec/ripple/validations_spec.rb +50 -52
  82. data/spec/spec_helper.rb +9 -2
  83. data/spec/support/generator_setup.rb +26 -0
  84. data/spec/support/models.rb +1 -0
  85. data/spec/support/models/box.rb +1 -0
  86. data/spec/support/models/clock.rb +1 -1
  87. data/spec/support/models/indexer.rb +26 -0
  88. data/spec/support/models/post.rb +3 -2
  89. data/spec/support/models/widget.rb +2 -0
  90. data/spec/support/search.rb +2 -2
  91. data/spec/support/test_server.rb +23 -11
  92. data/spec/support/test_server.yml.example +1 -1
  93. metadata +159 -135
  94. 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
- @box = Box.new { |a| a.key = 'some-key' }
8
- @box.stub!(:new?).and_return(false)
8
+ subject.stub!(:new?).and_return(false)
9
9
  end
10
10
 
11
- it "should return the key as an array for to_key" do
12
- @box.to_key.should == ['some-key']
13
- end
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
- Time.utc(2010,3,16,12).as_json.should == "2010-03-16T12:00:00Z"
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
- Time.utc(2010,3,16,12).as_json.should == "Tue, 16 Mar 2010 12:00:00 -0000"
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
- Date.civil(2010,3,16).as_json.should == "2010-03-16"
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
- Date.civil(2010,3,16).as_json.should == "16 Mar 2010"
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
- DateTime.civil(2010,3,16,12).as_json.should == "2010-03-16T12:00:00+00:00"
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
- DateTime.civil(2010,3,16,12).as_json.should == "Tue, 16 Mar 2010 12:00:00 +0000"
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
- time = Time.utc(2010,3,16,12)
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
- time = Time.utc(2010,3,16,12)
62
- zone = ActiveSupport::TimeZone['Alaska']
63
- ActiveSupport::TimeWithZone.new(time, zone).as_json.should == "Tue, 16 Mar 2010 12:00:00 -0000"
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
+
@@ -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
- @backend = mock("Backend", :store_object => true)
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
- @backend.should_receive(:store_object) do |obj, *_|
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").to_json
26
- @backend.should_receive(:store_object) do |obj, _, _, _|
27
- obj.raw_data.should == json
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).to_json
40
- @backend.should_receive(:store_object) do |obj, _, _, _|
41
- obj.raw_data.should == json
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).to_json
54
- @backend.should_receive(:store_object) do |obj, _, _, _|
55
- obj.raw_data.should == json
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 => "2000-01-01T20:15:01Z", :_type => 'Widget').to_json
69
- @backend.should_receive(:store_object) do |obj, _, _, _|
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').to_json
83
- @backend.should_receive(:store_object) do |obj, _, _, _|
84
- obj.raw_data.should == json
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
- @backend.should_receive(:store_object) do |obj, _, _, _|
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").to_json
120
- @backend.should_receive(:store_object) do |obj, _, _, _|
121
- obj.raw_data.should == json
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
- @backend.should_receive(:reload_object) do |obj, _|
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
- @backend.should_receive(:store_object).and_return(true)
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
- @backend.should_receive(:delete_object).and_return(true)
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
- @backend.should_not_receive(:delete_object)
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").to_json
175
- @backend.should_receive(:store_object) do |obj, _, _, _|
176
- obj.raw_data.should == json
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
- @backend.stub(:store_object)
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
- @backend.stub(:store_object)
305
+ @client.stub(:store_object)
283
306
  end
284
307
 
285
308
  it "does not save children when the parent is saved" do