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.
Files changed (83) hide show
  1. data/CHANGELOG.md +28 -5
  2. data/Gemfile.lock +12 -12
  3. data/README.md +29 -1
  4. data/lib/massive_record/adapters/initialize.rb +18 -0
  5. data/lib/massive_record/adapters/thrift/adapter.rb +25 -0
  6. data/lib/massive_record/adapters/thrift/column_family.rb +24 -0
  7. data/lib/massive_record/adapters/thrift/connection.rb +73 -0
  8. data/lib/massive_record/{thrift → adapters/thrift/hbase}/hbase.rb +0 -0
  9. data/lib/massive_record/{thrift → adapters/thrift/hbase}/hbase_constants.rb +0 -0
  10. data/lib/massive_record/{thrift → adapters/thrift/hbase}/hbase_types.rb +0 -0
  11. data/lib/massive_record/adapters/thrift/row.rb +150 -0
  12. data/lib/massive_record/adapters/thrift/scanner.rb +59 -0
  13. data/lib/massive_record/adapters/thrift/table.rb +169 -0
  14. data/lib/massive_record/orm/attribute_methods/read.rb +2 -1
  15. data/lib/massive_record/orm/base.rb +61 -3
  16. data/lib/massive_record/orm/coders/chained.rb +71 -0
  17. data/lib/massive_record/orm/coders/json.rb +17 -0
  18. data/lib/massive_record/orm/coders/yaml.rb +15 -0
  19. data/lib/massive_record/orm/coders.rb +3 -0
  20. data/lib/massive_record/orm/errors.rb +15 -2
  21. data/lib/massive_record/orm/finders/scope.rb +166 -0
  22. data/lib/massive_record/orm/finders.rb +45 -24
  23. data/lib/massive_record/orm/persistence.rb +4 -4
  24. data/lib/massive_record/orm/relations/interface.rb +170 -0
  25. data/lib/massive_record/orm/relations/metadata.rb +150 -0
  26. data/lib/massive_record/orm/relations/proxy/references_many.rb +229 -0
  27. data/lib/massive_record/orm/relations/proxy/references_one.rb +40 -0
  28. data/lib/massive_record/orm/relations/proxy/references_one_polymorphic.rb +49 -0
  29. data/lib/massive_record/orm/relations/proxy.rb +174 -0
  30. data/lib/massive_record/orm/relations.rb +6 -0
  31. data/lib/massive_record/orm/schema/column_interface.rb +1 -1
  32. data/lib/massive_record/orm/schema/field.rb +62 -27
  33. data/lib/massive_record/orm/single_table_inheritance.rb +21 -0
  34. data/lib/massive_record/version.rb +1 -1
  35. data/lib/massive_record/wrapper/adapter.rb +6 -0
  36. data/lib/massive_record/wrapper/base.rb +6 -7
  37. data/lib/massive_record/wrapper/cell.rb +9 -32
  38. data/lib/massive_record/wrapper/column_families_collection.rb +2 -2
  39. data/lib/massive_record/wrapper/errors.rb +10 -0
  40. data/lib/massive_record/wrapper/tables_collection.rb +1 -1
  41. data/lib/massive_record.rb +5 -12
  42. data/spec/orm/cases/attribute_methods_spec.rb +5 -1
  43. data/spec/orm/cases/base_spec.rb +77 -4
  44. data/spec/orm/cases/column_spec.rb +1 -1
  45. data/spec/orm/cases/finder_default_scope.rb +53 -0
  46. data/spec/orm/cases/finder_scope_spec.rb +288 -0
  47. data/spec/orm/cases/finders_spec.rb +56 -13
  48. data/spec/orm/cases/persistence_spec.rb +20 -5
  49. data/spec/orm/cases/single_table_inheritance_spec.rb +26 -0
  50. data/spec/orm/cases/table_spec.rb +1 -1
  51. data/spec/orm/cases/timestamps_spec.rb +16 -16
  52. data/spec/orm/coders/chained_spec.rb +73 -0
  53. data/spec/orm/coders/json_spec.rb +6 -0
  54. data/spec/orm/coders/yaml_spec.rb +6 -0
  55. data/spec/orm/models/best_friend.rb +7 -0
  56. data/spec/orm/models/friend.rb +4 -0
  57. data/spec/orm/models/person.rb +20 -6
  58. data/spec/orm/models/{person_with_timestamps.rb → person_with_timestamp.rb} +1 -1
  59. data/spec/orm/models/test_class.rb +3 -0
  60. data/spec/orm/relations/interface_spec.rb +207 -0
  61. data/spec/orm/relations/metadata_spec.rb +202 -0
  62. data/spec/orm/relations/proxy/references_many_spec.rb +624 -0
  63. data/spec/orm/relations/proxy/references_one_polymorphic_spec.rb +106 -0
  64. data/spec/orm/relations/proxy/references_one_spec.rb +111 -0
  65. data/spec/orm/relations/proxy_spec.rb +13 -0
  66. data/spec/orm/schema/field_spec.rb +101 -2
  67. data/spec/shared/orm/coders/an_orm_coder.rb +14 -0
  68. data/spec/shared/orm/relations/proxy.rb +154 -0
  69. data/spec/shared/orm/relations/singular_proxy.rb +68 -0
  70. data/spec/spec_helper.rb +1 -0
  71. data/spec/thrift/cases/encoding_spec.rb +28 -7
  72. data/spec/wrapper/cases/adapter_spec.rb +9 -0
  73. data/spec/wrapper/cases/connection_spec.rb +13 -10
  74. data/spec/wrapper/cases/table_spec.rb +85 -85
  75. metadata +74 -22
  76. data/TODO.md +0 -8
  77. data/lib/massive_record/exceptions.rb +0 -11
  78. data/lib/massive_record/wrapper/column_family.rb +0 -22
  79. data/lib/massive_record/wrapper/connection.rb +0 -71
  80. data/lib/massive_record/wrapper/row.rb +0 -173
  81. data/lib/massive_record/wrapper/scanner.rb +0 -61
  82. data/lib/massive_record/wrapper/table.rb +0 -149
  83. 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 simply return nil on find if table does not exists" do
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 be_nil
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
- %w(first all).each do |method|
117
- it "should respond to #{method}" do
118
- TestClass.should respond_to method
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 delegate #{method} to find with first argument as :#{method}" do
122
- TestClass.should_receive(:find).with(method.to_sym)
123
- TestClass.send(method)
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 #{method}'s call to find with it's args as second argument" do
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(:find).with(anything, options)
129
- TestClass.send(method, options)
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 raise error on new record" do
80
- lambda { Person.new.reload }.should raise_error MassiveRecord::ORM::RecordNotFound
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)["info"].keys.should include("pts")
108
- @person.send(:attributes_to_row_values_hash)["info"].keys.should_not include("points")
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 == ["bar"]
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 == ["info"]
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/person_with_timestamps'
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
- @person_with_timestamps = PersonWithTimestamps.create! :name => "John Doe", :email => "john@base.com", :age => "20"
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
- @person_with_timestamps.should be_set_created_at_on_create
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
- PersonWithTimestamps.new.created_at.should be_nil
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 { @person_with_timestamps.created_at = Time.now }.should raise_error MassiveRecord::ORM::CantBeManuallyAssigned
76
- lambda { @person_with_timestamps['created_at'] = Time.now }.should raise_error MassiveRecord::ORM::CantBeManuallyAssigned
77
- lambda { @person_with_timestamps.write_attribute(:created_at, Time.now) }.should raise_error MassiveRecord::ORM::CantBeManuallyAssigned
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
- @person_with_timestamps.created_at.should be_a_kind_of Time
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 = @person_with_timestamps.created_at
89
+ created_at_was = @person_with_timestamp.created_at
90
90
 
91
91
  sleep(1)
92
92
 
93
- @person_with_timestamps.update_attribute :name, @person_with_timestamps.name + "NEW"
94
- @person_with_timestamps.created_at.should == created_at_was
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
- @person_with_timestamps.send(:known_attribute_names_for_inspect).should include 'created_at'
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
- @person_with_timestamps.inspect.should include(%q{created_at:})
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
- PersonWithTimestamps.attributes_schema['created_at'].type = :string
110
+ PersonWithTimestamp.attributes_schema['created_at'].type = :string
111
111
 
112
- lambda { PersonWithTimestamps.create! }.should raise_error "created_at must be of type time"
112
+ lambda { PersonWithTimestamp.create! }.should raise_error "created_at must be of type time"
113
113
 
114
- PersonWithTimestamps.attributes_schema['created_at'].type = :time
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
@@ -0,0 +1,6 @@
1
+ require 'spec_helper'
2
+
3
+ describe MassiveRecord::ORM::Coders::JSON do
4
+ let(:code_with) { lambda { |value| ActiveSupport::JSON.encode(value) } }
5
+ it_should_behave_like "an orm coder"
6
+ end
@@ -0,0 +1,6 @@
1
+ require 'spec_helper'
2
+
3
+ describe MassiveRecord::ORM::Coders::YAML do
4
+ let(:code_with) { lambda { |value| value.to_yaml } }
5
+ it_should_behave_like "an orm coder"
6
+ end
@@ -0,0 +1,7 @@
1
+ require 'orm/models/friend'
2
+
3
+ class BestFriend < Friend
4
+ class SuperBestFriend < BestFriend
5
+ end
6
+ end
7
+
@@ -0,0 +1,4 @@
1
+ require 'orm/models/person'
2
+
3
+ class Friend < Person
4
+ end
@@ -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
@@ -1,4 +1,4 @@
1
- class PersonWithTimestamps < MassiveRecord::ORM::Table
1
+ class PersonWithTimestamp < MassiveRecord::ORM::Table
2
2
  column_family :info do
3
3
  field :name
4
4
  field :email