ricordami 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +13 -0
- data/ISSUES.md +0 -0
- data/MIT-LICENSE +21 -0
- data/README.md +454 -0
- data/TODO.md +21 -0
- data/examples/calls.rb +64 -0
- data/examples/singers.rb +42 -0
- data/lib/ricordami/attribute.rb +52 -0
- data/lib/ricordami/can_be_queried.rb +133 -0
- data/lib/ricordami/can_be_validated.rb +27 -0
- data/lib/ricordami/can_have_relationships.rb +152 -0
- data/lib/ricordami/configuration.rb +39 -0
- data/lib/ricordami/connection.rb +22 -0
- data/lib/ricordami/exceptions.rb +14 -0
- data/lib/ricordami/has_attributes.rb +159 -0
- data/lib/ricordami/has_indices.rb +105 -0
- data/lib/ricordami/is_lockable.rb +52 -0
- data/lib/ricordami/is_persisted.rb +123 -0
- data/lib/ricordami/is_retrievable.rb +35 -0
- data/lib/ricordami/key_namer.rb +63 -0
- data/lib/ricordami/model.rb +29 -0
- data/lib/ricordami/query.rb +68 -0
- data/lib/ricordami/relationship.rb +40 -0
- data/lib/ricordami/unique_index.rb +59 -0
- data/lib/ricordami/unique_validator.rb +21 -0
- data/lib/ricordami/value_index.rb +26 -0
- data/lib/ricordami/version.rb +3 -0
- data/lib/ricordami.rb +26 -0
- data/spec/acceptance/manage_relationships_spec.rb +42 -0
- data/spec/acceptance/model_with_validation_spec.rb +78 -0
- data/spec/acceptance/query_model_spec.rb +93 -0
- data/spec/acceptance_helper.rb +2 -0
- data/spec/ricordami/attribute_spec.rb +113 -0
- data/spec/ricordami/can_be_queried_spec.rb +254 -0
- data/spec/ricordami/can_be_validated_spec.rb +115 -0
- data/spec/ricordami/can_have_relationships_spec.rb +255 -0
- data/spec/ricordami/configuration_spec.rb +45 -0
- data/spec/ricordami/connection_spec.rb +25 -0
- data/spec/ricordami/exceptions_spec.rb +43 -0
- data/spec/ricordami/has_attributes_spec.rb +266 -0
- data/spec/ricordami/has_indices_spec.rb +73 -0
- data/spec/ricordami/is_lockable_spec.rb +45 -0
- data/spec/ricordami/is_persisted_spec.rb +186 -0
- data/spec/ricordami/is_retrievable_spec.rb +55 -0
- data/spec/ricordami/key_namer_spec.rb +56 -0
- data/spec/ricordami/model_spec.rb +65 -0
- data/spec/ricordami/query_spec.rb +156 -0
- data/spec/ricordami/relationship_spec.rb +123 -0
- data/spec/ricordami/unique_index_spec.rb +87 -0
- data/spec/ricordami/unique_validator_spec.rb +41 -0
- data/spec/ricordami/value_index_spec.rb +40 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/support/constants.rb +43 -0
- data/spec/support/db_manager.rb +18 -0
- data/test/bin/data_loader.rb +107 -0
- data/test/data/domains.txt +462 -0
- data/test/data/first_names.txt +1220 -0
- data/test/data/last_names.txt +1028 -0
- data/test/data/people_100_000.csv.bz2 +0 -0
- data/test/data/people_10_000.csv.bz2 +0 -0
- data/test/data/people_1_000_000.csv.bz2 +0 -0
- metadata +258 -0
@@ -0,0 +1,93 @@
|
|
1
|
+
require "acceptance_helper"
|
2
|
+
require "ricordami/can_be_queried"
|
3
|
+
|
4
|
+
class Singer
|
5
|
+
include Ricordami::Model
|
6
|
+
include Ricordami::CanBeQueried
|
7
|
+
|
8
|
+
attribute :username
|
9
|
+
attribute :email
|
10
|
+
attribute :first_name
|
11
|
+
attribute :last_name
|
12
|
+
attribute :deceased, :default => "false", :indexed => :value
|
13
|
+
|
14
|
+
index :unique => :username, :get_by => true
|
15
|
+
end
|
16
|
+
|
17
|
+
feature "Query model" do
|
18
|
+
def create_12_singers
|
19
|
+
[
|
20
|
+
["Serge", "Gainsbourg"], ["Alain", "Bashung"], ["Benjamin", "Biolay"],
|
21
|
+
["Charles", "Aznavour"], ["Yves", "Montand", true], ["Nino", "Ferrer", true],
|
22
|
+
["Johnny", "Hallyday"], ["David", "Guetta"], ["Bruno", "Benabar"],
|
23
|
+
["Alain", "Souchon"], ["Jacques", "Dutronc"], ["Georges", "Brasens"]
|
24
|
+
].each do |first, last, deceased|
|
25
|
+
Singer.create(:username => (first + last[0..0]).downcase, :first_name => first,
|
26
|
+
:last_name => last, :email => "#{first.downcase}@#{last.downcase}.fr",
|
27
|
+
:deceased => deceased ? "true" : "false").should be_persisted
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
scenario "retrieve models" do
|
32
|
+
Singer.create(:username => "lucien", :email => "serge@gainsbourg.com",
|
33
|
+
:first_name => "Serge", :last_name => "Gainsbourg")
|
34
|
+
Singer.get_by_username("lucien").id.should == "1"
|
35
|
+
|
36
|
+
Singer.create(:username => "bashung", :email => "alain@bashung.com",
|
37
|
+
:first_name => "Alain", :last_name => "Bashung")
|
38
|
+
Singer.get_by_username("bashung").id.should == "2"
|
39
|
+
|
40
|
+
Singer.create(:username => "ben", :email => "benjamin@biolay.com",
|
41
|
+
:first_name => "Benjamin", :last_name => "Biolay")
|
42
|
+
Singer.get_by_username("ben").id.should == "3"
|
43
|
+
|
44
|
+
Singer.count.should == 3
|
45
|
+
Singer["1"].email.should == "serge@gainsbourg.com"
|
46
|
+
|
47
|
+
Singer.get_by_username("bashung").first_name.should == "Alain"
|
48
|
+
end
|
49
|
+
|
50
|
+
scenario "finding models using basic queries" do
|
51
|
+
Singer.create(:username => "ben", :email => "benjamin@biolay.com",
|
52
|
+
:first_name => "Benjamin", :last_name => "Biolay", :deceased => "false")
|
53
|
+
Singer.create(:username => "lucien", :email => "serge@gainsbourg.com",
|
54
|
+
:first_name => "Serge", :last_name => "Gainsbourg", :deceased => "true")
|
55
|
+
Singer.create(:username => "bashung", :email => "alain@bashung.com",
|
56
|
+
:first_name => "Alain", :last_name => "Bashung", :deceased => "true")
|
57
|
+
|
58
|
+
deceased = Singer.and(:deceased => "true").all
|
59
|
+
deceased.map(&:username).should =~ %w(lucien bashung)
|
60
|
+
q = Singer.where(:deceased => "true")
|
61
|
+
q.sort(:first_name, :asc_alpha).first.email.should == "alain@bashung.com"
|
62
|
+
q.sort(:first_name, :desc_alpha).last.email.should == "alain@bashung.com"
|
63
|
+
Singer.not(:deceased => true).map(&:username).should == ["ben"]
|
64
|
+
first = Singer.first.username
|
65
|
+
last = Singer.last.username
|
66
|
+
rand = Singer.rand.username
|
67
|
+
first.should_not == last
|
68
|
+
Singer.all.map(&:username).should include(first)
|
69
|
+
Singer.all.map(&:username).should include(last)
|
70
|
+
Singer.all.map(&:username).should include(rand)
|
71
|
+
end
|
72
|
+
|
73
|
+
scenario "paginate list of models" do
|
74
|
+
create_12_singers
|
75
|
+
|
76
|
+
Singer.paginate(:page => 1, :per_page => 5).should have(5).singers
|
77
|
+
Singer.paginate(:page => 2, :per_page => 5).should have(5).singers
|
78
|
+
Singer.paginate(:page => 3, :per_page => 5).should have(2).singers
|
79
|
+
Singer.paginate(:page => 4, :per_page => 5).should have(0).singers
|
80
|
+
|
81
|
+
page1 = Singer.sort(:last_name, :asc_alpha).paginate(:page => 1, :per_page => 5)
|
82
|
+
page1.map(&:last_name).should == ["Aznavour", "Bashung", "Benabar", "Biolay", "Brasens"]
|
83
|
+
page3 = Singer.sort(:last_name, :asc_alpha).paginate(:page => 3, :per_page => 5)
|
84
|
+
page3.map(&:last_name).should == ["Montand", "Souchon"]
|
85
|
+
end
|
86
|
+
|
87
|
+
scenario "query using #and, #any and #not filters" do
|
88
|
+
Singer.index :value => :first_name
|
89
|
+
create_12_singers
|
90
|
+
|
91
|
+
Singer.where(:first_name => "Alain").all.map(&:last_name).should =~ %w(Bashung Souchon)
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Ricordami::Attribute do
|
4
|
+
subject { Ricordami::Attribute }
|
5
|
+
|
6
|
+
describe "an instance" do
|
7
|
+
it "has a name" do
|
8
|
+
attribute = subject.new(:singer)
|
9
|
+
attribute.name.should == :singer
|
10
|
+
end
|
11
|
+
|
12
|
+
it "accepts a string for its name" do
|
13
|
+
attribute = subject.new("string")
|
14
|
+
attribute.name.should == :string
|
15
|
+
end
|
16
|
+
|
17
|
+
it "accepts also a symbol for its name" do
|
18
|
+
attribute = subject.new(:string)
|
19
|
+
attribute.name.should == :string
|
20
|
+
end
|
21
|
+
|
22
|
+
it "has an option :default for a default value" do
|
23
|
+
attribute = subject.new(:georges, :default => "jungle")
|
24
|
+
attribute.default_value.should == "jungle"
|
25
|
+
end
|
26
|
+
|
27
|
+
it "doesn't have a default value if :default is not specified" do
|
28
|
+
subject.new(:no_defaults).default_value.should be_nil
|
29
|
+
end
|
30
|
+
|
31
|
+
it "allows to use a block as a default value for dynamic values" do
|
32
|
+
i = 0
|
33
|
+
attribute = subject.new(:sequence, :default => Proc.new { i += 1 })
|
34
|
+
attribute.default_value.should == 1
|
35
|
+
attribute.default_value.should == 2
|
36
|
+
attribute.default_value.should == 3
|
37
|
+
end
|
38
|
+
|
39
|
+
it "retuns if a default value is set with #default_value?" do
|
40
|
+
subject.new(:blah, :default => "1").default_value?.should be_true
|
41
|
+
without = subject.new(:foo).default_value?.should be_false
|
42
|
+
end
|
43
|
+
|
44
|
+
it "has an option :read_only when the attribute value can only be set once" do
|
45
|
+
attribute = subject.new(:georges, :read_only => true)
|
46
|
+
attribute.should be_read_only
|
47
|
+
end
|
48
|
+
|
49
|
+
it "its value can be set more than once when :read_only is not set" do
|
50
|
+
subject.new(:not_read_only).should_not be_read_only
|
51
|
+
end
|
52
|
+
|
53
|
+
it "has an option :indexed to index the attribute as unique" do
|
54
|
+
attribute = subject.new(:georges, :indexed => :unique)
|
55
|
+
attribute.indexed.should == :unique
|
56
|
+
end
|
57
|
+
|
58
|
+
it "has an option :indexed to index the attribute by value" do
|
59
|
+
attribute = subject.new(:georges, :indexed => :value)
|
60
|
+
attribute.indexed.should == :value
|
61
|
+
end
|
62
|
+
|
63
|
+
it "raises an error if :indexed is not :unique or :value" do
|
64
|
+
lambda {
|
65
|
+
subject.new(:georges, :indexed => :blah)
|
66
|
+
}.should raise_error(Ricordami::InvalidIndexDefinition)
|
67
|
+
lambda {
|
68
|
+
subject.new(:georges)
|
69
|
+
}.should_not raise_error
|
70
|
+
end
|
71
|
+
|
72
|
+
it "its value can be used for queries when :indexed is not set" do
|
73
|
+
subject.new(:not_indexed).should_not be_indexed
|
74
|
+
end
|
75
|
+
|
76
|
+
it "has an option :initial for an initial value set when saved the first time" do
|
77
|
+
attribute = subject.new(:id, :initial => "123")
|
78
|
+
attribute.initial_value.should == "123"
|
79
|
+
end
|
80
|
+
|
81
|
+
it "doesn't have an initial value if :initial is not specified" do
|
82
|
+
subject.new(:no_initials).initial_value.should be_nil
|
83
|
+
end
|
84
|
+
|
85
|
+
it "allows to use a block as an initial value for dynamic values" do
|
86
|
+
i = 1
|
87
|
+
attribute = subject.new(:id, :initial => Proc.new { i *= 2 })
|
88
|
+
attribute.initial_value.should == 2
|
89
|
+
attribute.initial_value.should == 4
|
90
|
+
attribute.initial_value.should == 8
|
91
|
+
end
|
92
|
+
|
93
|
+
it "retuns if an initial value is set with #initial_value?" do
|
94
|
+
subject.new(:id, :initial => "1").initial_value?.should be_true
|
95
|
+
without = subject.new(:foo).initial_value?.should be_false
|
96
|
+
end
|
97
|
+
|
98
|
+
it "can spefify the attribute type with :type option" do
|
99
|
+
attribute = subject.new(:id, :type => :integer)
|
100
|
+
attribute.type.should == :integer
|
101
|
+
end
|
102
|
+
|
103
|
+
it "has a default type :string if not specified" do
|
104
|
+
subject.new(:name).type.should == :string
|
105
|
+
end
|
106
|
+
|
107
|
+
it "returns the converter method with #converter" do
|
108
|
+
subject.new(:name, :type => :string).converter.should == :to_s
|
109
|
+
subject.new(:name, :type => :integer).converter.should == :to_i
|
110
|
+
subject.new(:name, :type => :float).converter.should == :to_f
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,254 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "ricordami/can_be_queried"
|
3
|
+
|
4
|
+
describe Ricordami::CanBeQueried do
|
5
|
+
uses_constants("Customer")
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
Customer.send(:include, Ricordami::CanBeQueried)
|
9
|
+
Customer.attribute :country, :indexed => :value
|
10
|
+
Customer.attribute :sex, :indexed => :value
|
11
|
+
Customer.attribute :name, :indexed => :value
|
12
|
+
Customer.attribute :kind, :indexed => :value
|
13
|
+
Customer.attribute :no_index
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "building queries" do
|
17
|
+
describe "#and" do
|
18
|
+
it "returns a new query" do
|
19
|
+
query = Customer.and
|
20
|
+
query.should be_a(Ricordami::Query)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "passes self as the query runner to the query" do
|
24
|
+
query = Customer.and
|
25
|
+
query.runner.should == Customer
|
26
|
+
end
|
27
|
+
|
28
|
+
it "delegates #and to the new query" do
|
29
|
+
query = Customer.and(:key => "value")
|
30
|
+
query.expressions.should == [[:and, {:key => "value"}]]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "#not" do
|
35
|
+
it "returns a new query" do
|
36
|
+
query = Customer.not
|
37
|
+
query.should be_a(Ricordami::Query)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "delegates #not to the new query" do
|
41
|
+
query = Customer.not(:key => "value")
|
42
|
+
query.expressions.should == [[:not, {:key => "value"}]]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#any" do
|
47
|
+
it "returns a new query" do
|
48
|
+
query = Customer.any
|
49
|
+
query.should be_a(Ricordami::Query)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "delegates #any to the new query" do
|
53
|
+
query = Customer.any(:key => "value")
|
54
|
+
query.expressions.should == [[:any, {:key => "value"}]]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "#sort" do
|
59
|
+
it "returns a new query" do
|
60
|
+
query = Customer.sort(:sex)
|
61
|
+
query.should be_a(Ricordami::Query)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "delegates #sort to the new query" do
|
65
|
+
query = Customer.sort(:sex, :desc_alpha)
|
66
|
+
query.sort_by.should == :sex
|
67
|
+
query.sort_dir.should == :desc_alpha
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "running queries" do
|
73
|
+
before(:each) do
|
74
|
+
Customer.create(:name => "Zhanna", :sex => "F", :country => "Latvia", :kind => "human")
|
75
|
+
Customer.create(:name => "Mathieu", :sex => "M", :country => "France", :kind => "human")
|
76
|
+
Customer.create(:name => "Sophie", :sex => "F", :country => "USA", :kind => "human")
|
77
|
+
Customer.create(:name => "Brioche", :sex => "F", :country => "USA", :kind => "dog")
|
78
|
+
end
|
79
|
+
|
80
|
+
describe ":and" do
|
81
|
+
it "raises an error if there's no value index for one of the conditions" do
|
82
|
+
lambda {
|
83
|
+
Customer.and(:no_index => "Blah").all
|
84
|
+
}.should raise_error(Ricordami::MissingIndex)
|
85
|
+
end
|
86
|
+
|
87
|
+
it "returns all entries if no conditions where passed" do
|
88
|
+
Customer.and.all.map(&:name).should =~ %w(Zhanna Mathieu Sophie Brioche)
|
89
|
+
end
|
90
|
+
|
91
|
+
it "returns the models found with #all (1 condition, 1 result)" do
|
92
|
+
Customer.index :value => :name
|
93
|
+
found = Customer.and(:name => "Zhanna").all
|
94
|
+
found.map(&:name).should == ["Zhanna"]
|
95
|
+
end
|
96
|
+
|
97
|
+
it "returns the models found with #all (2 conditions, 2 results)" do
|
98
|
+
found = Customer.and(:country => "USA", :sex => "F").all
|
99
|
+
found.map(&:name).should =~ ["Sophie", "Brioche"]
|
100
|
+
end
|
101
|
+
|
102
|
+
it "returns the models found with #all for a composed query" do
|
103
|
+
found = Customer.and(:country => "USA").and(:sex => "F").all
|
104
|
+
found.map(&:name).should =~ ["Sophie", "Brioche"]
|
105
|
+
end
|
106
|
+
|
107
|
+
it "can use #where instead of #and" do
|
108
|
+
found = Customer.where(:country => "USA").and(:sex => "F").all
|
109
|
+
found.map(&:name).should =~ ["Sophie", "Brioche"]
|
110
|
+
end
|
111
|
+
|
112
|
+
it "doesn't require #all if another method call is chained" do
|
113
|
+
Customer.where(:country => "USA").and(:sex => "F").map(&:name).should =~ ["Sophie", "Brioche"]
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe ":any" do
|
118
|
+
it "raises an error if there's no value index for one of the conditions" do
|
119
|
+
lambda {
|
120
|
+
Customer.any(:no_index => "Blah").all
|
121
|
+
}.should raise_error(Ricordami::MissingIndex)
|
122
|
+
end
|
123
|
+
|
124
|
+
it "returns all entries if no conditions where passed" do
|
125
|
+
Customer.any.all.map(&:name).should =~ %w(Zhanna Mathieu Sophie Brioche)
|
126
|
+
end
|
127
|
+
|
128
|
+
it "returns the models found with #all (1 condition, 1 result)" do
|
129
|
+
Customer.index :value => :name
|
130
|
+
found = Customer.any(:name => "Zhanna").all
|
131
|
+
found.map(&:name).should == ["Zhanna"]
|
132
|
+
end
|
133
|
+
|
134
|
+
it "returns the models found with #all (2 conditions, 3 results)" do
|
135
|
+
found = Customer.any(:country => "USA", :sex => "F").all
|
136
|
+
found.map(&:name).should =~ ["Sophie", "Brioche", "Zhanna"]
|
137
|
+
end
|
138
|
+
|
139
|
+
it "returns the models found with #all for a composed query" do
|
140
|
+
found = Customer.where(:country => "USA").any(:name => "Sophie", :kind => "dog").all
|
141
|
+
found.map(&:name).should =~ ["Sophie", "Brioche"]
|
142
|
+
found = Customer.where(:country => "USA").any(:name => "Sophie", :kind => "human").all
|
143
|
+
found.map(&:name).should == ["Sophie"]
|
144
|
+
end
|
145
|
+
|
146
|
+
it "doesn't require #all if another method call is chained" do
|
147
|
+
Customer.any(:country => "USA", :sex => "F").map(&:name).should =~ ["Sophie", "Brioche", "Zhanna"]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe ":not" do
|
152
|
+
it "raises an error if there's no value index for one of the conditions" do
|
153
|
+
lambda {
|
154
|
+
Customer.not(:no_index => "Blah").all
|
155
|
+
}.should raise_error(Ricordami::MissingIndex)
|
156
|
+
end
|
157
|
+
|
158
|
+
it "returns all entries if no conditions where passed" do
|
159
|
+
Customer.not.all.map(&:name).should =~ %w(Zhanna Mathieu Sophie Brioche)
|
160
|
+
end
|
161
|
+
|
162
|
+
it "returns the models found with #all (1 condition, 1 result)" do
|
163
|
+
Customer.index :value => :name
|
164
|
+
found = Customer.not(:name => "Zhanna").all
|
165
|
+
found.map(&:name).should =~ ["Sophie", "Brioche", "Mathieu"]
|
166
|
+
end
|
167
|
+
|
168
|
+
it "returns the models found with #all (2 conditions, 1 result)" do
|
169
|
+
found = Customer.not(:country => "USA", :sex => "F").all
|
170
|
+
found.map(&:name).should == ["Mathieu"]
|
171
|
+
end
|
172
|
+
|
173
|
+
it "returns the models found with #all for a composed query" do
|
174
|
+
found = Customer.where(:country => "USA").not(:name => "Sophie", :kind => "dog").all
|
175
|
+
found.map(&:name).should be_empty
|
176
|
+
found = Customer.where(:country => "USA").not(:name => "Sophie", :kind => "human").all
|
177
|
+
found.map(&:name).should == ["Brioche"]
|
178
|
+
end
|
179
|
+
|
180
|
+
it "doesn't require #all if another method call is chained" do
|
181
|
+
Customer.not(:country => "USA", :sex => "F").map(&:name).should == ["Mathieu"]
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
describe "sorting result" do
|
187
|
+
uses_constants("Student")
|
188
|
+
|
189
|
+
before(:each) do
|
190
|
+
Student.send(:include, Ricordami::CanBeQueried)
|
191
|
+
Student.attribute :name, :indexed => :value
|
192
|
+
Student.attribute :grade, :indexed => :value
|
193
|
+
Student.attribute :school, :indexed => :value
|
194
|
+
[["Zhanna", 12], ["Sophie", 19],
|
195
|
+
["Brioche", 4], ["Mathieu", 15]].each do |name, grade|
|
196
|
+
Student.create(:name => name, :grade => grade, :school => "Lajoo")
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
it "can sort the result alphanumerically with #sort" do
|
201
|
+
result = Student.where(:school => "Lajoo").sort(:name, :asc_alpha)
|
202
|
+
result.map(&:name).should == %w(Brioche Mathieu Sophie Zhanna)
|
203
|
+
end
|
204
|
+
|
205
|
+
it "can sort the result numerically with #sort" do
|
206
|
+
result = Student.where(:school => "Lajoo").sort(:grade, :asc_num)
|
207
|
+
result.map(&:name).should == %w(Brioche Zhanna Mathieu Sophie)
|
208
|
+
end
|
209
|
+
|
210
|
+
it "defaults to sorting ascending / alphanumerically" do
|
211
|
+
result = Student.where(:school => "Lajoo").sort(:name)
|
212
|
+
result.map(&:name).should == %w(Brioche Mathieu Sophie Zhanna)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
describe "fetching result" do
|
217
|
+
before(:each) do
|
218
|
+
Student.send(:include, Ricordami::CanBeQueried)
|
219
|
+
Student.attribute :name, :indexed => :value
|
220
|
+
Student.attribute :grade, :indexed => :value
|
221
|
+
Student.attribute :school, :indexed => :value
|
222
|
+
[["Zhanna", 12], ["Sophie", 19],
|
223
|
+
["Brioche", 4], ["Mathieu", 15]].each do |name, grade|
|
224
|
+
Student.create(:name => name, :grade => grade, :school => "Lajoo")
|
225
|
+
end
|
226
|
+
@query = Student.where(:school => "Lajoo").sort(:name)
|
227
|
+
end
|
228
|
+
let(:query) { @query }
|
229
|
+
|
230
|
+
it "fetches all the results with #all" do
|
231
|
+
query.all.map(&:name).should == %w(Brioche Mathieu Sophie Zhanna)
|
232
|
+
end
|
233
|
+
|
234
|
+
it "fetches the requested page of the results with #paginate" do
|
235
|
+
fetched = query.paginate(:page => 1, :per_page => 2)
|
236
|
+
fetched.map(&:name).should == %w(Brioche Mathieu)
|
237
|
+
fetched = query.paginate(:page => 2, :per_page => 2)
|
238
|
+
fetched.map(&:name).should == %w(Sophie Zhanna)
|
239
|
+
query.paginate(:page => 3, :per_page => 2).should be_empty
|
240
|
+
end
|
241
|
+
|
242
|
+
it "fetches the first instance with #first" do
|
243
|
+
query.first.name.should == "Brioche"
|
244
|
+
end
|
245
|
+
|
246
|
+
it "fetches the last instance with #last" do
|
247
|
+
query.last.name.should == "Zhanna"
|
248
|
+
end
|
249
|
+
|
250
|
+
it "fetches a random instance with #rand" do
|
251
|
+
%w(Brioche Mathieu Sophie Zhanna).should include(query.rand.name)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "ricordami/can_be_validated"
|
3
|
+
|
4
|
+
describe Ricordami::CanBeValidated do
|
5
|
+
describe "Active Model validations" do
|
6
|
+
uses_constants("Call")
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
Call.class_eval do
|
10
|
+
include Ricordami::CanBeValidated
|
11
|
+
attribute :ani
|
12
|
+
attribute :dnis
|
13
|
+
validates_presence_of :ani
|
14
|
+
validates_numericality_of :dnis, :allow_blank => true, :message => "is not numeric"
|
15
|
+
validate :cant_call_iself
|
16
|
+
|
17
|
+
private
|
18
|
+
def cant_call_iself
|
19
|
+
return true if ani.blank?
|
20
|
+
errors.add(:ani, "can't be the same as dnis") if ani == dnis
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it "can validate the presence of an attribute value" do
|
26
|
+
call = Call.new
|
27
|
+
call.should_not be_valid
|
28
|
+
call.should have(1).errors
|
29
|
+
call.errors[:ani].should == ["can't be blank"]
|
30
|
+
end
|
31
|
+
|
32
|
+
it "can validate that a field is numeric" do
|
33
|
+
call = Call.new(:ani => "123", :dnis => "nope")
|
34
|
+
call.should_not be_valid
|
35
|
+
call.should have(1).errors
|
36
|
+
call.errors[:dnis].should == ["is not numeric"]
|
37
|
+
end
|
38
|
+
|
39
|
+
it "can do custom validation" do
|
40
|
+
call = Call.new(:ani => "123", :dnis => "123")
|
41
|
+
call.should_not be_valid
|
42
|
+
call.should have(1).errors
|
43
|
+
call.errors[:ani].should == ["can't be the same as dnis"]
|
44
|
+
end
|
45
|
+
|
46
|
+
it "can't validate a model that was deleted" do
|
47
|
+
call = Call.create(:ani => "123", :dnis => "456")
|
48
|
+
call.delete
|
49
|
+
lambda { call.valid? }.should raise_error(Ricordami::ModelHasBeenDeleted)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#save" do
|
54
|
+
uses_constants("Post")
|
55
|
+
|
56
|
+
before(:each) do
|
57
|
+
Post.class_eval do
|
58
|
+
include Ricordami::CanBeValidated
|
59
|
+
attribute :title
|
60
|
+
validates_presence_of :title
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
it "can't #save if it is not valid" do
|
65
|
+
post = Post.new
|
66
|
+
post.save.should be_false
|
67
|
+
end
|
68
|
+
|
69
|
+
it "can save if it is not valid but passed :validate => false" do
|
70
|
+
Post.new.save(:validate => false).should be_true
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "validate attribute uniqueness" do
|
75
|
+
uses_constants("User")
|
76
|
+
|
77
|
+
before(:each) do
|
78
|
+
User.class_eval do
|
79
|
+
include Ricordami::CanBeValidated
|
80
|
+
attribute :username, :read_only => true
|
81
|
+
attribute :wife
|
82
|
+
validates_uniqueness_of :username
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
it "is valid if no other instance uses the same attribute value" do
|
87
|
+
user = User.new(:username => "Serge Gainsbourg")
|
88
|
+
user.should be_valid
|
89
|
+
end
|
90
|
+
|
91
|
+
it "is not valid if another instance uses the same attribute value" do
|
92
|
+
serge = User.new(:username => "Gainsbourg")
|
93
|
+
serge.save.should be_true
|
94
|
+
serge.should be_valid
|
95
|
+
|
96
|
+
usurpateur = User.new(:username => "Gainsbourg")
|
97
|
+
usurpateur.should_not be_valid
|
98
|
+
usurpateur.should have(1).error
|
99
|
+
usurpateur.errors[:username].should == ["is already used"]
|
100
|
+
end
|
101
|
+
|
102
|
+
it "allows to validate the uniqueness of an attribute that can be changed" do
|
103
|
+
User.validates_uniqueness_of(:wife)
|
104
|
+
User.create(:id => "serge", :username => "Gainsbourg", :wife => "Rita")
|
105
|
+
serge = User["serge"]
|
106
|
+
serge.should be_valid
|
107
|
+
serge.save
|
108
|
+
|
109
|
+
fred = User.new(:username => "Chichin", :wife => "Rita")
|
110
|
+
fred.should_not be_valid
|
111
|
+
fred.should have(1).error
|
112
|
+
fred.errors[:wife].should == ["is already used"]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|