seomoz-ripple 1.0.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +20 -0
- data/Guardfile +15 -0
- data/Rakefile +88 -0
- data/lib/rails/generators/ripple/configuration/configuration_generator.rb +13 -0
- data/lib/rails/generators/ripple/configuration/templates/ripple.yml +24 -0
- data/lib/rails/generators/ripple/js/js_generator.rb +13 -0
- data/lib/rails/generators/ripple/js/templates/js/contrib.js +63 -0
- data/lib/rails/generators/ripple/js/templates/js/iso8601.js +76 -0
- data/lib/rails/generators/ripple/js/templates/js/ripple.js +132 -0
- data/lib/rails/generators/ripple/model/model_generator.rb +20 -0
- data/lib/rails/generators/ripple/model/templates/model.rb +10 -0
- data/lib/rails/generators/ripple/observer/observer_generator.rb +16 -0
- data/lib/rails/generators/ripple/observer/templates/observer.rb +4 -0
- data/lib/rails/generators/ripple/test/templates/test_server.rb +46 -0
- data/lib/rails/generators/ripple/test/test_generator.rb +39 -0
- data/lib/rails/generators/ripple_generator.rb +78 -0
- data/lib/ripple.rb +79 -0
- data/lib/ripple/associations.rb +356 -0
- data/lib/ripple/associations/embedded.rb +35 -0
- data/lib/ripple/associations/instantiators.rb +26 -0
- data/lib/ripple/associations/linked.rb +65 -0
- data/lib/ripple/associations/many.rb +38 -0
- data/lib/ripple/associations/many_embedded_proxy.rb +38 -0
- data/lib/ripple/associations/many_linked_proxy.rb +66 -0
- data/lib/ripple/associations/many_reference_proxy.rb +93 -0
- data/lib/ripple/associations/many_stored_key_proxy.rb +76 -0
- data/lib/ripple/associations/one.rb +20 -0
- data/lib/ripple/associations/one_embedded_proxy.rb +35 -0
- data/lib/ripple/associations/one_key_proxy.rb +58 -0
- data/lib/ripple/associations/one_linked_proxy.rb +22 -0
- data/lib/ripple/associations/one_stored_key_proxy.rb +43 -0
- data/lib/ripple/associations/proxy.rb +118 -0
- data/lib/ripple/attribute_methods.rb +118 -0
- data/lib/ripple/attribute_methods/dirty.rb +50 -0
- data/lib/ripple/attribute_methods/query.rb +34 -0
- data/lib/ripple/attribute_methods/read.rb +26 -0
- data/lib/ripple/attribute_methods/write.rb +25 -0
- data/lib/ripple/callbacks.rb +74 -0
- data/lib/ripple/conflict/basic_resolver.rb +82 -0
- data/lib/ripple/conflict/document_hooks.rb +20 -0
- data/lib/ripple/conflict/resolver.rb +71 -0
- data/lib/ripple/conflict/test_helper.rb +33 -0
- data/lib/ripple/conversion.rb +28 -0
- data/lib/ripple/core_ext.rb +2 -0
- data/lib/ripple/core_ext/casting.rb +148 -0
- data/lib/ripple/core_ext/object.rb +8 -0
- data/lib/ripple/document.rb +104 -0
- data/lib/ripple/document/bucket_access.rb +25 -0
- data/lib/ripple/document/finders.rb +131 -0
- data/lib/ripple/document/key.rb +43 -0
- data/lib/ripple/document/link.rb +30 -0
- data/lib/ripple/document/persistence.rb +115 -0
- data/lib/ripple/embedded_document.rb +64 -0
- data/lib/ripple/embedded_document/around_callbacks.rb +18 -0
- data/lib/ripple/embedded_document/finders.rb +26 -0
- data/lib/ripple/embedded_document/persistence.rb +77 -0
- data/lib/ripple/i18n.rb +2 -0
- data/lib/ripple/inspection.rb +32 -0
- data/lib/ripple/locale/en.yml +21 -0
- data/lib/ripple/nested_attributes.rb +265 -0
- data/lib/ripple/observable.rb +28 -0
- data/lib/ripple/properties.rb +73 -0
- data/lib/ripple/property_type_mismatch.rb +12 -0
- data/lib/ripple/railtie.rb +13 -0
- data/lib/ripple/serialization.rb +84 -0
- data/lib/ripple/timestamps.rb +27 -0
- data/lib/ripple/translation.rb +14 -0
- data/lib/ripple/validations.rb +67 -0
- data/lib/ripple/validations/associated_validator.rb +43 -0
- data/ripple.gemspec +46 -0
- data/seomoz-ripple.gemspec +46 -0
- data/spec/fixtures/config.yml +8 -0
- data/spec/integration/ripple/associations_spec.rb +220 -0
- data/spec/integration/ripple/conflict_resolution_spec.rb +293 -0
- data/spec/integration/ripple/nested_attributes_spec.rb +264 -0
- data/spec/integration/ripple/persistence_spec.rb +57 -0
- data/spec/integration/ripple/search_associations_spec.rb +42 -0
- data/spec/ripple/associations/many_embedded_proxy_spec.rb +122 -0
- data/spec/ripple/associations/many_linked_proxy_spec.rb +191 -0
- data/spec/ripple/associations/many_reference_proxy_spec.rb +170 -0
- data/spec/ripple/associations/many_stored_key_proxy_spec.rb +158 -0
- data/spec/ripple/associations/one_embedded_proxy_spec.rb +125 -0
- data/spec/ripple/associations/one_key_proxy_spec.rb +82 -0
- data/spec/ripple/associations/one_linked_proxy_spec.rb +91 -0
- data/spec/ripple/associations/one_stored_key_proxy_spec.rb +72 -0
- data/spec/ripple/associations/proxy_spec.rb +84 -0
- data/spec/ripple/associations_spec.rb +129 -0
- data/spec/ripple/attribute_methods/dirty_spec.rb +80 -0
- data/spec/ripple/attribute_methods_spec.rb +230 -0
- data/spec/ripple/bucket_access_spec.rb +25 -0
- data/spec/ripple/callbacks_spec.rb +176 -0
- data/spec/ripple/conflict/resolver_spec.rb +42 -0
- data/spec/ripple/conversion_spec.rb +22 -0
- data/spec/ripple/core_ext_spec.rb +103 -0
- data/spec/ripple/document/link_spec.rb +67 -0
- data/spec/ripple/document_spec.rb +96 -0
- data/spec/ripple/embedded_document/finders_spec.rb +29 -0
- data/spec/ripple/embedded_document/persistence_spec.rb +80 -0
- data/spec/ripple/embedded_document_spec.rb +84 -0
- data/spec/ripple/finders_spec.rb +217 -0
- data/spec/ripple/inspection_spec.rb +51 -0
- data/spec/ripple/key_spec.rb +30 -0
- data/spec/ripple/observable_spec.rb +121 -0
- data/spec/ripple/persistence_spec.rb +326 -0
- data/spec/ripple/properties_spec.rb +262 -0
- data/spec/ripple/ripple_spec.rb +71 -0
- data/spec/ripple/serialization_spec.rb +51 -0
- data/spec/ripple/timestamps_spec.rb +76 -0
- data/spec/ripple/validations/associated_validator_spec.rb +77 -0
- data/spec/ripple/validations_spec.rb +104 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/support/associations.rb +1 -0
- data/spec/support/associations/proxies.rb +17 -0
- data/spec/support/integration_setup.rb +11 -0
- data/spec/support/mocks.rb +4 -0
- data/spec/support/models.rb +4 -0
- data/spec/support/models/address.rb +12 -0
- data/spec/support/models/box.rb +13 -0
- data/spec/support/models/car.rb +16 -0
- data/spec/support/models/cardboard_box.rb +3 -0
- data/spec/support/models/clock.rb +12 -0
- data/spec/support/models/clock_observer.rb +3 -0
- data/spec/support/models/company.rb +23 -0
- data/spec/support/models/customer.rb +4 -0
- data/spec/support/models/driver.rb +6 -0
- data/spec/support/models/email.rb +4 -0
- data/spec/support/models/engine.rb +5 -0
- data/spec/support/models/family.rb +16 -0
- data/spec/support/models/favorite.rb +4 -0
- data/spec/support/models/invoice.rb +7 -0
- data/spec/support/models/late_invoice.rb +3 -0
- data/spec/support/models/ninja.rb +9 -0
- data/spec/support/models/note.rb +5 -0
- data/spec/support/models/page.rb +4 -0
- data/spec/support/models/paid_invoice.rb +4 -0
- data/spec/support/models/passenger.rb +6 -0
- data/spec/support/models/profile.rb +10 -0
- data/spec/support/models/seat.rb +5 -0
- data/spec/support/models/subscription.rb +27 -0
- data/spec/support/models/tasks.rb +14 -0
- data/spec/support/models/team.rb +11 -0
- data/spec/support/models/transactions.rb +17 -0
- data/spec/support/models/tree.rb +4 -0
- data/spec/support/models/user.rb +10 -0
- data/spec/support/models/wheel.rb +6 -0
- data/spec/support/models/widget.rb +22 -0
- data/spec/support/test_server.rb +18 -0
- data/spec/support/test_server.yml.example +2 -0
- metadata +362 -0
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ripple::Validations::AssociatedValidator do
|
4
|
+
context 'for a one association' do
|
5
|
+
require 'support/models/family'
|
6
|
+
|
7
|
+
let(:child) { Child.new }
|
8
|
+
let(:parent) { Parent.new }
|
9
|
+
before(:each) { parent.child = child }
|
10
|
+
|
11
|
+
it "is invalid when the associated record is invalid" do
|
12
|
+
child.should_not be_valid
|
13
|
+
parent.should_not be_valid
|
14
|
+
end
|
15
|
+
|
16
|
+
it "includes the associated record's validation error messages in the error message" do
|
17
|
+
parent.valid?
|
18
|
+
parent.errors[:child].size.should == 1
|
19
|
+
parent.errors[:child].first.should =~ /is invalid \(Name can't be blank and Age can't be blank\)/
|
20
|
+
end
|
21
|
+
|
22
|
+
it "is valid when the associated record is valid" do
|
23
|
+
child.name = 'Coen'
|
24
|
+
child.age = 1
|
25
|
+
child.should be_valid
|
26
|
+
parent.should be_valid
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'for a many association' do
|
31
|
+
require 'support/models/team'
|
32
|
+
|
33
|
+
let(:team) { Team.new }
|
34
|
+
let(:ichiro) { Player.new(:name => 'Ichiro', :position => 'Right Field') }
|
35
|
+
let(:satchel_paige) { Player.new(:position => 'Pitcher') }
|
36
|
+
let(:unknown) { Player.new }
|
37
|
+
|
38
|
+
before(:each) do
|
39
|
+
team.players << ichiro
|
40
|
+
team.players << satchel_paige
|
41
|
+
team.players << unknown
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'is invalid when the associated records are invalid' do
|
45
|
+
satchel_paige.should_not be_valid
|
46
|
+
unknown.should_not be_valid
|
47
|
+
team.should_not be_valid
|
48
|
+
end
|
49
|
+
|
50
|
+
it "includes the associated records' validation error messages in the error message" do
|
51
|
+
team.valid?
|
52
|
+
team.errors[:players].size.should == 1
|
53
|
+
|
54
|
+
expected_errors = [
|
55
|
+
"for player 2: Name can't be blank",
|
56
|
+
"for player 3: Name can't be blank and Position can't be blank"
|
57
|
+
]
|
58
|
+
|
59
|
+
expected_errors = expected_errors.join('; ')
|
60
|
+
team.errors[:players].first.should == "are invalid (#{expected_errors})"
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'is valid when the associated records are all valid' do
|
64
|
+
ichiro.position = 'Right Field'
|
65
|
+
ichiro.should be_valid
|
66
|
+
|
67
|
+
satchel_paige.name = 'Satchel Paige'
|
68
|
+
satchel_paige.should be_valid
|
69
|
+
|
70
|
+
unknown.name = 'John Doe'
|
71
|
+
unknown.position = 'First Base'
|
72
|
+
unknown.should be_valid
|
73
|
+
|
74
|
+
team.should be_valid
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ripple::Validations do
|
4
|
+
require 'support/models/box'
|
5
|
+
|
6
|
+
before :each do
|
7
|
+
@box = Box.new
|
8
|
+
@client = Ripple.client
|
9
|
+
@client.stub!(:backend).and_return(mock("Backend", :store_object => true))
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should add validation declarations to the class" do
|
13
|
+
[:validates, :validate, :validates_with, :validates_each,
|
14
|
+
:validates_acceptance_of, :validates_confirmation_of, :validates_exclusion_of,
|
15
|
+
:validates_format_of, :validates_inclusion_of, :validates_length_of,
|
16
|
+
:validates_numericality_of, :validates_presence_of].each do |meth|
|
17
|
+
Box.should respond_to(meth)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should add validation methods to the instance" do
|
22
|
+
%w{errors valid? invalid?}.each do |meth|
|
23
|
+
@box.should respond_to(meth)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should override save to run validations" do
|
28
|
+
@box.should_receive(:valid?).and_return(false)
|
29
|
+
@box.save.should be_false
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should allow skipping validations by passing save :validate => false" do
|
33
|
+
Ripple.client.http.stub!(:perform).and_return(mock_response)
|
34
|
+
@box.should_not_receive(:valid?)
|
35
|
+
@box.save(:validate => false).should be_true
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "when using save! on an invalid record" do
|
39
|
+
before(:each) { @box.stub!(:valid?).and_return(false) }
|
40
|
+
|
41
|
+
it "should raise DocumentInvalid" do
|
42
|
+
lambda { @box.save! }.should raise_error(Ripple::DocumentInvalid)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should raise an exception that has the invalid document" do
|
46
|
+
begin
|
47
|
+
@box.save!
|
48
|
+
rescue Ripple::DocumentInvalid => invalid
|
49
|
+
invalid.document.should == @box
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should not raise an error when save! is called and the document is valid" do
|
55
|
+
@box.stub!(:save).and_return(true)
|
56
|
+
@box.stub!(:valid?).and_return(true)
|
57
|
+
lambda { @box.save! }.should_not raise_error(Ripple::DocumentInvalid)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should return true from save! when no exception is raised" do
|
61
|
+
@box.stub!(:save).and_return(true)
|
62
|
+
@box.stub!(:valid?).and_return(true)
|
63
|
+
@box.save!.should be_true
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should allow unexpected exceptions to be raised" do
|
67
|
+
robject = mock("robject", :key => @box.key, "data=" => true, "content_type=" => true)
|
68
|
+
robject.should_receive(:store).and_raise(Riak::HTTPFailedRequest.new(:post, 200, 404, {}, "404 not found"))
|
69
|
+
@box.stub!(:robject).and_return(robject)
|
70
|
+
@box.stub!(:valid?).and_return(true)
|
71
|
+
lambda { @box.save! }.should raise_error(Riak::FailedRequest)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should not raise an error when creating a box with create! succeeds" do
|
75
|
+
@box.stub!(:new?).and_return(false)
|
76
|
+
Box.stub(:create).and_return(@box)
|
77
|
+
lambda { @new_box = Box.create! }.should_not raise_error(Ripple::DocumentInvalid)
|
78
|
+
@new_box.should == @box
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should raise an error when creating a box with create! fails" do
|
82
|
+
@box.stub!(:new?).and_return(true)
|
83
|
+
Box.stub(:create).and_return(@box)
|
84
|
+
lambda { Box.create! }.should raise_error(Ripple::DocumentInvalid)
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should automatically add validations from property options" do
|
88
|
+
Box.property :size, Integer, :inclusion => {:in => 1..30 }
|
89
|
+
@box.size = 0
|
90
|
+
@box.should_not be_valid
|
91
|
+
Box.properties.delete :size
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should run validations at the correct lifecycle state" do
|
95
|
+
Box.property :size, Integer, :inclusion => {:in => 1..30, :on => :update }
|
96
|
+
@box.size = 0
|
97
|
+
@box.should be_valid
|
98
|
+
Box.properties.delete :size
|
99
|
+
end
|
100
|
+
|
101
|
+
after :each do
|
102
|
+
Box.reset_callbacks(:validate)
|
103
|
+
end
|
104
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', '..', 'riak-client','lib'))
|
4
|
+
|
5
|
+
require 'rubygems' # Use the gems path only for the spec suite
|
6
|
+
require 'ripple'
|
7
|
+
require 'ripple/conflict/test_helper'
|
8
|
+
require 'rspec'
|
9
|
+
|
10
|
+
# Only the tests should really get away with this.
|
11
|
+
Riak.disable_list_keys_warnings = true
|
12
|
+
|
13
|
+
Dir[File.join(File.dirname(__FILE__), "support", "*.rb")].sort.each {|f| require f }
|
14
|
+
|
15
|
+
RSpec.configure do |config|
|
16
|
+
config.mock_with :rspec
|
17
|
+
config.after(:each) do
|
18
|
+
$test_server.recycle if $test_server
|
19
|
+
end
|
20
|
+
|
21
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
22
|
+
config.filter_run :focus
|
23
|
+
config.run_all_when_everything_filtered = true
|
24
|
+
config.debug = true
|
25
|
+
config.include Ripple::Conflict::TestHelper
|
26
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require File.expand_path("../associations/proxies", __FILE__)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
class FakeNilProxy < Ripple::Associations::Proxy
|
4
|
+
def find_target; nil end
|
5
|
+
end
|
6
|
+
|
7
|
+
class FakeBlankProxy < Ripple::Associations::Proxy
|
8
|
+
def find_target; '' end
|
9
|
+
end
|
10
|
+
|
11
|
+
class FakeNumberProxy < Ripple::Associations::Proxy
|
12
|
+
def find_target; 17 end
|
13
|
+
end
|
14
|
+
|
15
|
+
class FakeProxy < Ripple::Associations::Proxy
|
16
|
+
def find_target; [1, 2] end
|
17
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# auto-tag all integration specs with :integration => true
|
2
|
+
module IntegrationSpecs
|
3
|
+
def self.included(klass)
|
4
|
+
klass.metadata[:integration] = true
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
RSpec.configure do |config|
|
9
|
+
config.include IntegrationSpecs, :example_group => { :file_path => %r{spec/integration} }
|
10
|
+
end
|
11
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
|
2
|
+
class Car
|
3
|
+
include Ripple::Document
|
4
|
+
|
5
|
+
property :make, String
|
6
|
+
property :model, String
|
7
|
+
|
8
|
+
one :driver # linked, key_on :name
|
9
|
+
many :passengers # linked, standard :key
|
10
|
+
one :engine # embedded
|
11
|
+
many :seats # embedded
|
12
|
+
many :wheels
|
13
|
+
|
14
|
+
accepts_nested_attributes_for :driver, :passengers, :engine, :seats
|
15
|
+
accepts_nested_attributes_for :wheels, :reject_if => proc{|attrs| attrs['diameter'] < 12 }, :allow_destroy => true
|
16
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Company
|
2
|
+
include Ripple::Document
|
3
|
+
property :name, String
|
4
|
+
many :departments
|
5
|
+
many :invoices # linked
|
6
|
+
one :ceo, :class_name => 'CEO'
|
7
|
+
end
|
8
|
+
|
9
|
+
class Department
|
10
|
+
include Ripple::EmbeddedDocument
|
11
|
+
property :name, String
|
12
|
+
many :managers
|
13
|
+
end
|
14
|
+
|
15
|
+
class Manager
|
16
|
+
include Ripple::EmbeddedDocument
|
17
|
+
property :name, String
|
18
|
+
end
|
19
|
+
|
20
|
+
class CEO
|
21
|
+
include Ripple::EmbeddedDocument
|
22
|
+
property :name, String
|
23
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class Parent
|
2
|
+
include Ripple::Document
|
3
|
+
one :child
|
4
|
+
validates_associated :child
|
5
|
+
end
|
6
|
+
|
7
|
+
class Child
|
8
|
+
include Ripple::EmbeddedDocument
|
9
|
+
property :name, String, :presence => true
|
10
|
+
property :age, Integer, :presence => true
|
11
|
+
one :gchild, :class_name => 'Grandchild'
|
12
|
+
end
|
13
|
+
|
14
|
+
class Grandchild
|
15
|
+
include Ripple::EmbeddedDocument
|
16
|
+
end
|