massive_record 0.1.1 → 0.2.0.beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.md +28 -5
- data/Gemfile.lock +12 -12
- data/README.md +29 -1
- data/lib/massive_record/adapters/initialize.rb +18 -0
- data/lib/massive_record/adapters/thrift/adapter.rb +25 -0
- data/lib/massive_record/adapters/thrift/column_family.rb +24 -0
- data/lib/massive_record/adapters/thrift/connection.rb +73 -0
- data/lib/massive_record/{thrift → adapters/thrift/hbase}/hbase.rb +0 -0
- data/lib/massive_record/{thrift → adapters/thrift/hbase}/hbase_constants.rb +0 -0
- data/lib/massive_record/{thrift → adapters/thrift/hbase}/hbase_types.rb +0 -0
- data/lib/massive_record/adapters/thrift/row.rb +150 -0
- data/lib/massive_record/adapters/thrift/scanner.rb +59 -0
- data/lib/massive_record/adapters/thrift/table.rb +169 -0
- data/lib/massive_record/orm/attribute_methods/read.rb +2 -1
- data/lib/massive_record/orm/base.rb +61 -3
- data/lib/massive_record/orm/coders/chained.rb +71 -0
- data/lib/massive_record/orm/coders/json.rb +17 -0
- data/lib/massive_record/orm/coders/yaml.rb +15 -0
- data/lib/massive_record/orm/coders.rb +3 -0
- data/lib/massive_record/orm/errors.rb +15 -2
- data/lib/massive_record/orm/finders/scope.rb +166 -0
- data/lib/massive_record/orm/finders.rb +45 -24
- data/lib/massive_record/orm/persistence.rb +4 -4
- data/lib/massive_record/orm/relations/interface.rb +170 -0
- data/lib/massive_record/orm/relations/metadata.rb +150 -0
- data/lib/massive_record/orm/relations/proxy/references_many.rb +229 -0
- data/lib/massive_record/orm/relations/proxy/references_one.rb +40 -0
- data/lib/massive_record/orm/relations/proxy/references_one_polymorphic.rb +49 -0
- data/lib/massive_record/orm/relations/proxy.rb +174 -0
- data/lib/massive_record/orm/relations.rb +6 -0
- data/lib/massive_record/orm/schema/column_interface.rb +1 -1
- data/lib/massive_record/orm/schema/field.rb +62 -27
- data/lib/massive_record/orm/single_table_inheritance.rb +21 -0
- data/lib/massive_record/version.rb +1 -1
- data/lib/massive_record/wrapper/adapter.rb +6 -0
- data/lib/massive_record/wrapper/base.rb +6 -7
- data/lib/massive_record/wrapper/cell.rb +9 -32
- data/lib/massive_record/wrapper/column_families_collection.rb +2 -2
- data/lib/massive_record/wrapper/errors.rb +10 -0
- data/lib/massive_record/wrapper/tables_collection.rb +1 -1
- data/lib/massive_record.rb +5 -12
- data/spec/orm/cases/attribute_methods_spec.rb +5 -1
- data/spec/orm/cases/base_spec.rb +77 -4
- data/spec/orm/cases/column_spec.rb +1 -1
- data/spec/orm/cases/finder_default_scope.rb +53 -0
- data/spec/orm/cases/finder_scope_spec.rb +288 -0
- data/spec/orm/cases/finders_spec.rb +56 -13
- data/spec/orm/cases/persistence_spec.rb +20 -5
- data/spec/orm/cases/single_table_inheritance_spec.rb +26 -0
- data/spec/orm/cases/table_spec.rb +1 -1
- data/spec/orm/cases/timestamps_spec.rb +16 -16
- data/spec/orm/coders/chained_spec.rb +73 -0
- data/spec/orm/coders/json_spec.rb +6 -0
- data/spec/orm/coders/yaml_spec.rb +6 -0
- data/spec/orm/models/best_friend.rb +7 -0
- data/spec/orm/models/friend.rb +4 -0
- data/spec/orm/models/person.rb +20 -6
- data/spec/orm/models/{person_with_timestamps.rb → person_with_timestamp.rb} +1 -1
- data/spec/orm/models/test_class.rb +3 -0
- data/spec/orm/relations/interface_spec.rb +207 -0
- data/spec/orm/relations/metadata_spec.rb +202 -0
- data/spec/orm/relations/proxy/references_many_spec.rb +624 -0
- data/spec/orm/relations/proxy/references_one_polymorphic_spec.rb +106 -0
- data/spec/orm/relations/proxy/references_one_spec.rb +111 -0
- data/spec/orm/relations/proxy_spec.rb +13 -0
- data/spec/orm/schema/field_spec.rb +101 -2
- data/spec/shared/orm/coders/an_orm_coder.rb +14 -0
- data/spec/shared/orm/relations/proxy.rb +154 -0
- data/spec/shared/orm/relations/singular_proxy.rb +68 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/thrift/cases/encoding_spec.rb +28 -7
- data/spec/wrapper/cases/adapter_spec.rb +9 -0
- data/spec/wrapper/cases/connection_spec.rb +13 -10
- data/spec/wrapper/cases/table_spec.rb +85 -85
- metadata +74 -22
- data/TODO.md +0 -8
- data/lib/massive_record/exceptions.rb +0 -11
- data/lib/massive_record/wrapper/column_family.rb +0 -22
- data/lib/massive_record/wrapper/connection.rb +0 -71
- data/lib/massive_record/wrapper/row.rb +0 -173
- data/lib/massive_record/wrapper/scanner.rb +0 -61
- data/lib/massive_record/wrapper/table.rb +0 -149
- data/spec/orm/cases/hbase/connection_spec.rb +0 -13
@@ -7,7 +7,7 @@ describe "finders" do
|
|
7
7
|
include MockMassiveRecordConnection
|
8
8
|
|
9
9
|
before do
|
10
|
-
@mocked_table = mock(MassiveRecord::Wrapper::Table).as_null_object
|
10
|
+
@mocked_table = mock(MassiveRecord::Wrapper::Table, :to_ary => []).as_null_object
|
11
11
|
Person.stub(:table).and_return(@mocked_table)
|
12
12
|
|
13
13
|
@row = MassiveRecord::Wrapper::Row.new
|
@@ -28,9 +28,9 @@ describe "finders" do
|
|
28
28
|
Person.first.should be_nil
|
29
29
|
end
|
30
30
|
|
31
|
-
it "should
|
31
|
+
it "should raise record not found error on find if table does not exists" do
|
32
32
|
Person.table.should_receive(:exists?).and_return false
|
33
|
-
Person.find(1).should
|
33
|
+
lambda { Person.find(1) }.should raise_error MassiveRecord::ORM::RecordNotFound
|
34
34
|
end
|
35
35
|
|
36
36
|
it "should simply return empty array if table does not exists" do
|
@@ -98,7 +98,6 @@ describe "finders" do
|
|
98
98
|
end
|
99
99
|
|
100
100
|
it "should return nil on first if no results was found" do
|
101
|
-
@mocked_table.should_receive(:first).and_return(nil)
|
102
101
|
Person.first.should be_nil
|
103
102
|
end
|
104
103
|
|
@@ -113,20 +112,37 @@ describe "finders" do
|
|
113
112
|
end
|
114
113
|
end
|
115
114
|
|
116
|
-
|
117
|
-
it "should respond to
|
118
|
-
TestClass.should respond_to
|
115
|
+
describe "all" do
|
116
|
+
it "should respond to all" do
|
117
|
+
TestClass.should respond_to :all
|
119
118
|
end
|
120
119
|
|
121
|
-
it "should
|
122
|
-
TestClass.should_receive(:
|
123
|
-
TestClass.
|
120
|
+
it "should call find with :all" do
|
121
|
+
TestClass.should_receive(:do_find).with(:all, anything)
|
122
|
+
TestClass.all
|
124
123
|
end
|
125
124
|
|
126
|
-
it "should delegate
|
125
|
+
it "should delegate all's call to find with it's args as second argument" do
|
127
126
|
options = {:foo => :bar}
|
128
|
-
TestClass.should_receive(:
|
129
|
-
TestClass.
|
127
|
+
TestClass.should_receive(:do_find).with(anything, options)
|
128
|
+
TestClass.all options
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe "first" do
|
133
|
+
it "should respond to first" do
|
134
|
+
TestClass.should respond_to :first
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should call find with :first" do
|
138
|
+
TestClass.should_receive(:do_find).with(:all, {:limit => 1}).and_return([])
|
139
|
+
TestClass.first
|
140
|
+
end
|
141
|
+
|
142
|
+
it "should delegate first's call to find with it's args as second argument" do
|
143
|
+
options = {:foo => :bar}
|
144
|
+
TestClass.should_receive(:do_find).with(anything, hash_including(options)).and_return([])
|
145
|
+
TestClass.first options
|
130
146
|
end
|
131
147
|
end
|
132
148
|
|
@@ -167,6 +183,19 @@ describe "finders" do
|
|
167
183
|
all.should include @person, @bob
|
168
184
|
all.length.should == 2
|
169
185
|
end
|
186
|
+
|
187
|
+
it "should find all persons, even if it is more than 10" do
|
188
|
+
15.times { |i| Person.create! :id => "id-#{i}", :name => "Going to die :-(", :age => i + 20 }
|
189
|
+
Person.all.length.should > 10
|
190
|
+
end
|
191
|
+
|
192
|
+
it "should raise error if not all requested records was found" do
|
193
|
+
lambda { Person.find(["ID1", "not exists"]) }.should raise_error MassiveRecord::ORM::RecordNotFound
|
194
|
+
end
|
195
|
+
|
196
|
+
it "should return what it finds if asked to" do
|
197
|
+
lambda { Person.find(["ID1", "not exists"], :skip_expected_result_check => true) }.should_not raise_error MassiveRecord::ORM::RecordNotFound
|
198
|
+
end
|
170
199
|
end
|
171
200
|
|
172
201
|
describe "#find_in_batches" do
|
@@ -183,6 +212,20 @@ describe "finders" do
|
|
183
212
|
end
|
184
213
|
group_number.should == @table_size / 3
|
185
214
|
end
|
215
|
+
|
216
|
+
it "should not do a thing if table does not exist" do
|
217
|
+
Person.table.should_receive(:exists?).and_return false
|
218
|
+
|
219
|
+
counter = 0
|
220
|
+
|
221
|
+
Person.find_in_batches(:batch_size => 3) do |rows|
|
222
|
+
rows.each do |row|
|
223
|
+
counter += 1
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
counter.should == 0
|
228
|
+
end
|
186
229
|
|
187
230
|
it "should iterate through a collection of rows using a batch process" do
|
188
231
|
rows_number = 0
|
@@ -76,8 +76,9 @@ describe "persistence" do
|
|
76
76
|
@person.reload.should == @person
|
77
77
|
end
|
78
78
|
|
79
|
-
it "should
|
80
|
-
|
79
|
+
it "should not do anything on reload when record is not persisted" do
|
80
|
+
Person.should_not_receive :find
|
81
|
+
Person.new.reload
|
81
82
|
end
|
82
83
|
end
|
83
84
|
|
@@ -104,8 +105,8 @@ describe "persistence" do
|
|
104
105
|
end
|
105
106
|
|
106
107
|
it "should include the 'pts' field in the database which has 'points' as an alias" do
|
107
|
-
@person.send(:attributes_to_row_values_hash)["
|
108
|
-
@person.send(:attributes_to_row_values_hash)["
|
108
|
+
@person.send(:attributes_to_row_values_hash)["base"].keys.should include("pts")
|
109
|
+
@person.send(:attributes_to_row_values_hash)["base"].keys.should_not include("points")
|
109
110
|
end
|
110
111
|
end
|
111
112
|
|
@@ -181,6 +182,9 @@ describe "persistence" do
|
|
181
182
|
column_family :bar do
|
182
183
|
field :foo
|
183
184
|
end
|
185
|
+
|
186
|
+
column_family :empty_family do
|
187
|
+
end
|
184
188
|
end
|
185
189
|
|
186
190
|
@new_instance = @new_class.new :id => "id_of_foo", :foo => "bar"
|
@@ -202,7 +206,7 @@ describe "persistence" do
|
|
202
206
|
|
203
207
|
it "should create correct column families" do
|
204
208
|
@new_instance.save
|
205
|
-
@new_class.table.fetch_column_families.collect(&:name).should
|
209
|
+
@new_class.table.fetch_column_families.collect(&:name).should include "bar", "empty_family"
|
206
210
|
end
|
207
211
|
|
208
212
|
it "should store the new instance" do
|
@@ -343,6 +347,11 @@ describe "persistence" do
|
|
343
347
|
@person.should be_destroyed
|
344
348
|
Person.all.length.should == 0
|
345
349
|
end
|
350
|
+
|
351
|
+
it "should be able to call destroy on new records" do
|
352
|
+
person = Person.new
|
353
|
+
person.destroy
|
354
|
+
end
|
346
355
|
|
347
356
|
describe "#destroy_all" do
|
348
357
|
it "should remove all when calling remove_all" do
|
@@ -354,6 +363,12 @@ describe "persistence" do
|
|
354
363
|
it "should return an array of all removed objects" do
|
355
364
|
Person.destroy_all.should == [@person]
|
356
365
|
end
|
366
|
+
|
367
|
+
it "should destroy all even if it is above 10 rows (obviously)" do
|
368
|
+
15.times { |i| Person.create! :id => "id-#{i}", :name => "Going to die :-(", :age => i + 20 }
|
369
|
+
Person.destroy_all
|
370
|
+
Person.all.length.should == 0
|
371
|
+
end
|
357
372
|
end
|
358
373
|
end
|
359
374
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'orm/models/friend'
|
3
|
+
require 'orm/models/best_friend'
|
4
|
+
|
5
|
+
describe "Single table inheritance" do
|
6
|
+
include SetUpHbaseConnectionBeforeAll
|
7
|
+
include SetTableNamesToTestTable
|
8
|
+
|
9
|
+
[Friend, BestFriend, BestFriend::SuperBestFriend].each do |klass|
|
10
|
+
describe klass do
|
11
|
+
let(:subject) { klass.new(:id => "ID1", :name => "Person1", :email => "one@person.com", :age => 11, :points => 111, :status => true) }
|
12
|
+
|
13
|
+
its(:type) { should == klass.to_s }
|
14
|
+
|
15
|
+
it "should instantiate correct class when reading from database" do
|
16
|
+
subject.save!
|
17
|
+
Person.find(subject.id).should == subject
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should not set type if record being saved is base class" do
|
23
|
+
person = Person.new :id => "ID1", :name => "Person1", :email => "one@person.com", :age => 11, :points => 111, :status => true
|
24
|
+
person.type.should be_nil
|
25
|
+
end
|
26
|
+
end
|
@@ -52,7 +52,7 @@ describe "Person which is a table" do
|
|
52
52
|
end
|
53
53
|
|
54
54
|
it "should keep different column families per sub class" do
|
55
|
-
Person.column_families.collect(&:name).should
|
55
|
+
Person.column_families.collect(&:name).should include "info", "base"
|
56
56
|
TestClass.column_families.collect(&:name).should == ["test_family"]
|
57
57
|
end
|
58
58
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'orm/models/person'
|
3
|
-
require 'orm/models/
|
3
|
+
require 'orm/models/person_with_timestamp'
|
4
4
|
|
5
5
|
describe "timestamps" do
|
6
6
|
include CreatePersonBeforeEach
|
@@ -56,11 +56,11 @@ describe "timestamps" do
|
|
56
56
|
|
57
57
|
describe "#created_at" do
|
58
58
|
before do
|
59
|
-
@
|
59
|
+
@person_with_timestamp = PersonWithTimestamp.create! :name => "John Doe", :email => "john@base.com", :age => "20"
|
60
60
|
end
|
61
61
|
|
62
62
|
it "should have created at" do
|
63
|
-
@
|
63
|
+
@person_with_timestamp.should be_set_created_at_on_create
|
64
64
|
end
|
65
65
|
|
66
66
|
it "should not have created at on create if model does not have created at" do
|
@@ -68,13 +68,13 @@ describe "timestamps" do
|
|
68
68
|
end
|
69
69
|
|
70
70
|
it "should have updated at equal to nil on new records" do
|
71
|
-
|
71
|
+
PersonWithTimestamp.new.created_at.should be_nil
|
72
72
|
end
|
73
73
|
|
74
74
|
it "should not be possible to set updated at" do
|
75
|
-
lambda { @
|
76
|
-
lambda { @
|
77
|
-
lambda { @
|
75
|
+
lambda { @person_with_timestamp.created_at = Time.now }.should raise_error MassiveRecord::ORM::CantBeManuallyAssigned
|
76
|
+
lambda { @person_with_timestamp['created_at'] = Time.now }.should raise_error MassiveRecord::ORM::CantBeManuallyAssigned
|
77
|
+
lambda { @person_with_timestamp.write_attribute(:created_at, Time.now) }.should raise_error MassiveRecord::ORM::CantBeManuallyAssigned
|
78
78
|
end
|
79
79
|
|
80
80
|
it "should not raise cant-set-error if object has no timestamps" do
|
@@ -82,24 +82,24 @@ describe "timestamps" do
|
|
82
82
|
end
|
83
83
|
|
84
84
|
it "should have created_at on a persisted record" do
|
85
|
-
@
|
85
|
+
@person_with_timestamp.created_at.should be_a_kind_of Time
|
86
86
|
end
|
87
87
|
|
88
88
|
it "should not alter created at on update" do
|
89
|
-
created_at_was = @
|
89
|
+
created_at_was = @person_with_timestamp.created_at
|
90
90
|
|
91
91
|
sleep(1)
|
92
92
|
|
93
|
-
@
|
94
|
-
@
|
93
|
+
@person_with_timestamp.update_attribute :name, @person_with_timestamp.name + "NEW"
|
94
|
+
@person_with_timestamp.created_at.should == created_at_was
|
95
95
|
end
|
96
96
|
|
97
97
|
it "should be included in the list of known_attribute_names_for_inspect" do
|
98
|
-
@
|
98
|
+
@person_with_timestamp.send(:known_attribute_names_for_inspect).should include 'created_at'
|
99
99
|
end
|
100
100
|
|
101
101
|
it "should include created_at in inspect" do
|
102
|
-
@
|
102
|
+
@person_with_timestamp.inspect.should include(%q{created_at:})
|
103
103
|
end
|
104
104
|
|
105
105
|
it "should not include created_at if object does not have it" do
|
@@ -107,11 +107,11 @@ describe "timestamps" do
|
|
107
107
|
end
|
108
108
|
|
109
109
|
it "should raise error if created_at is not time" do
|
110
|
-
|
110
|
+
PersonWithTimestamp.attributes_schema['created_at'].type = :string
|
111
111
|
|
112
|
-
lambda {
|
112
|
+
lambda { PersonWithTimestamp.create! }.should raise_error "created_at must be of type time"
|
113
113
|
|
114
|
-
|
114
|
+
PersonWithTimestamp.attributes_schema['created_at'].type = :time
|
115
115
|
end
|
116
116
|
end
|
117
117
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MassiveRecord::ORM::Coders::Chained do
|
4
|
+
describe "initialize" do
|
5
|
+
it "should be able to assign coder to load- and dump with" do
|
6
|
+
coders = MassiveRecord::ORM::Coders::JSON.new
|
7
|
+
coder = MassiveRecord::ORM::Coders::Chained.new(coders)
|
8
|
+
coder.loaders.should include coders
|
9
|
+
coder.dumpers.should include coders
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should be able to assign array of coders to load- and dump with" do
|
13
|
+
coders = [MassiveRecord::ORM::Coders::JSON.new, MassiveRecord::ORM::Coders::YAML.new]
|
14
|
+
coder = MassiveRecord::ORM::Coders::Chained.new(coders)
|
15
|
+
coder.loaders.should include *coders
|
16
|
+
coder.dumpers.should include *coders
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should be possible to assign load_with explicitly" do
|
20
|
+
coders = MassiveRecord::ORM::Coders::JSON.new
|
21
|
+
load_with = MassiveRecord::ORM::Coders::YAML.new
|
22
|
+
coder = MassiveRecord::ORM::Coders::Chained.new(coders, :load_with => load_with)
|
23
|
+
coder.loaders.should include load_with
|
24
|
+
coder.dumpers.should include coders
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should be possible to assign dump_with explicitly" do
|
28
|
+
coders = MassiveRecord::ORM::Coders::JSON.new
|
29
|
+
dump_with = MassiveRecord::ORM::Coders::YAML.new
|
30
|
+
coder = MassiveRecord::ORM::Coders::Chained.new(coders, :dump_with => dump_with)
|
31
|
+
coder.loaders.should include coders
|
32
|
+
coder.dumpers.should include dump_with
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
describe "one coder in chain" do
|
39
|
+
let(:subject) { MassiveRecord::ORM::Coders::Chained.new(MassiveRecord::ORM::Coders::JSON.new) }
|
40
|
+
let(:code_with) { lambda { |value| ActiveSupport::JSON.encode(value) } }
|
41
|
+
|
42
|
+
it_should_behave_like "an orm coder"
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
describe "two loaders in chain" do
|
48
|
+
let(:subject) do
|
49
|
+
MassiveRecord::ORM::Coders::Chained.new({
|
50
|
+
:load_with => [MassiveRecord::ORM::Coders::JSON.new, MassiveRecord::ORM::Coders::YAML.new],
|
51
|
+
:dump_with => MassiveRecord::ORM::Coders::YAML.new
|
52
|
+
})
|
53
|
+
end
|
54
|
+
|
55
|
+
let(:code_with) { lambda { |value| YAML.dump(value) } }
|
56
|
+
|
57
|
+
it_should_behave_like "an orm coder"
|
58
|
+
|
59
|
+
it "it should try the next loader in the line if the first one fails" do
|
60
|
+
data = {:foo => {'sdf' => "wef"}} # Will make the json encoder fail when YAML serialized
|
61
|
+
subject.load(YAML.dump(data)).should == data
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should raise ParseError if all fails" do
|
65
|
+
lambda { subject.load("{{}]") }.should raise_error MassiveRecord::ORM::Coders::ParseError
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should raise EncodeError if all fails" do
|
69
|
+
subject.dumpers.first.should_receive(:dump).and_raise(StandardError)
|
70
|
+
lambda { subject.dump("unable to dump") }.should raise_error MassiveRecord::ORM::Coders::EncodeError
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/spec/orm/models/person.rb
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
class Person < MassiveRecord::ORM::Table
|
2
|
-
validates_presence_of :name, :age
|
3
|
-
validates_numericality_of :age, :greater_than_or_equal_to => 0
|
4
|
-
validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :allow_blank => true
|
5
|
-
|
6
2
|
column_family :info do
|
7
3
|
field :name
|
8
4
|
field :email
|
9
5
|
field :age, :integer
|
10
|
-
field :points, :integer, :default => 1, :column => :pts
|
11
6
|
field :date_of_birth, :date
|
12
|
-
field :status, :boolean, :default => false
|
13
7
|
field :addresses, :hash, :default => {}
|
8
|
+
field :type
|
9
|
+
end
|
10
|
+
|
11
|
+
column_family :base do
|
12
|
+
field :points, :integer, :default => 1, :column => :pts
|
13
|
+
field :status, :boolean, :default => false
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
references_one :boss, :class_name => "PersonWithTimestamp", :store_in => :info
|
18
|
+
references_many :test_classes, :store_in => :info
|
19
|
+
references_many :friends, :class_name => "Person", :records_starts_from => :friends_records_starts_from_id
|
20
|
+
|
21
|
+
validates_presence_of :name, :age
|
22
|
+
validates_numericality_of :age, :greater_than_or_equal_to => 0
|
23
|
+
validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :allow_blank => true
|
24
|
+
|
25
|
+
|
26
|
+
def friends_records_starts_from_id
|
27
|
+
id+'-'
|
14
28
|
end
|
15
29
|
end
|