massive_record 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. data/.autotest +15 -0
  2. data/.gitignore +6 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/Gemfile.lock +38 -0
  6. data/Manifest +24 -0
  7. data/README.md +225 -0
  8. data/Rakefile +16 -0
  9. data/TODO.md +8 -0
  10. data/autotest/discover.rb +1 -0
  11. data/lib/massive_record.rb +18 -0
  12. data/lib/massive_record/exceptions.rb +11 -0
  13. data/lib/massive_record/orm/attribute_methods.rb +61 -0
  14. data/lib/massive_record/orm/attribute_methods/dirty.rb +80 -0
  15. data/lib/massive_record/orm/attribute_methods/read.rb +23 -0
  16. data/lib/massive_record/orm/attribute_methods/write.rb +24 -0
  17. data/lib/massive_record/orm/base.rb +176 -0
  18. data/lib/massive_record/orm/callbacks.rb +52 -0
  19. data/lib/massive_record/orm/column.rb +18 -0
  20. data/lib/massive_record/orm/config.rb +47 -0
  21. data/lib/massive_record/orm/errors.rb +47 -0
  22. data/lib/massive_record/orm/finders.rb +125 -0
  23. data/lib/massive_record/orm/id_factory.rb +133 -0
  24. data/lib/massive_record/orm/persistence.rb +199 -0
  25. data/lib/massive_record/orm/schema.rb +4 -0
  26. data/lib/massive_record/orm/schema/column_families.rb +48 -0
  27. data/lib/massive_record/orm/schema/column_family.rb +102 -0
  28. data/lib/massive_record/orm/schema/column_interface.rb +91 -0
  29. data/lib/massive_record/orm/schema/common_interface.rb +48 -0
  30. data/lib/massive_record/orm/schema/field.rb +128 -0
  31. data/lib/massive_record/orm/schema/fields.rb +37 -0
  32. data/lib/massive_record/orm/schema/table_interface.rb +96 -0
  33. data/lib/massive_record/orm/table.rb +9 -0
  34. data/lib/massive_record/orm/validations.rb +52 -0
  35. data/lib/massive_record/spec/support/simple_database_cleaner.rb +52 -0
  36. data/lib/massive_record/thrift/hbase.rb +2307 -0
  37. data/lib/massive_record/thrift/hbase_constants.rb +14 -0
  38. data/lib/massive_record/thrift/hbase_types.rb +225 -0
  39. data/lib/massive_record/version.rb +3 -0
  40. data/lib/massive_record/wrapper/base.rb +28 -0
  41. data/lib/massive_record/wrapper/cell.rb +45 -0
  42. data/lib/massive_record/wrapper/column_families_collection.rb +19 -0
  43. data/lib/massive_record/wrapper/column_family.rb +22 -0
  44. data/lib/massive_record/wrapper/connection.rb +71 -0
  45. data/lib/massive_record/wrapper/row.rb +170 -0
  46. data/lib/massive_record/wrapper/scanner.rb +50 -0
  47. data/lib/massive_record/wrapper/table.rb +148 -0
  48. data/lib/massive_record/wrapper/tables_collection.rb +13 -0
  49. data/massive_record.gemspec +28 -0
  50. data/spec/config.yml.example +4 -0
  51. data/spec/orm/cases/attribute_methods_spec.rb +47 -0
  52. data/spec/orm/cases/auto_generate_id_spec.rb +54 -0
  53. data/spec/orm/cases/base_spec.rb +176 -0
  54. data/spec/orm/cases/callbacks_spec.rb +309 -0
  55. data/spec/orm/cases/column_spec.rb +49 -0
  56. data/spec/orm/cases/config_spec.rb +103 -0
  57. data/spec/orm/cases/dirty_spec.rb +129 -0
  58. data/spec/orm/cases/encoding_spec.rb +49 -0
  59. data/spec/orm/cases/finders_spec.rb +208 -0
  60. data/spec/orm/cases/hbase/connection_spec.rb +13 -0
  61. data/spec/orm/cases/i18n_spec.rb +32 -0
  62. data/spec/orm/cases/id_factory_spec.rb +75 -0
  63. data/spec/orm/cases/persistence_spec.rb +479 -0
  64. data/spec/orm/cases/table_spec.rb +81 -0
  65. data/spec/orm/cases/validation_spec.rb +92 -0
  66. data/spec/orm/models/address.rb +7 -0
  67. data/spec/orm/models/person.rb +15 -0
  68. data/spec/orm/models/test_class.rb +5 -0
  69. data/spec/orm/schema/column_families_spec.rb +186 -0
  70. data/spec/orm/schema/column_family_spec.rb +131 -0
  71. data/spec/orm/schema/column_interface_spec.rb +115 -0
  72. data/spec/orm/schema/field_spec.rb +196 -0
  73. data/spec/orm/schema/fields_spec.rb +126 -0
  74. data/spec/orm/schema/table_interface_spec.rb +171 -0
  75. data/spec/spec_helper.rb +15 -0
  76. data/spec/support/connection_helpers.rb +76 -0
  77. data/spec/support/mock_massive_record_connection.rb +80 -0
  78. data/spec/thrift/cases/encoding_spec.rb +48 -0
  79. data/spec/wrapper/cases/connection_spec.rb +53 -0
  80. data/spec/wrapper/cases/table_spec.rb +231 -0
  81. metadata +228 -0
@@ -0,0 +1,129 @@
1
+ require 'spec_helper'
2
+
3
+ describe "dirty" do
4
+ describe "dry run" do
5
+ include MockMassiveRecordConnection
6
+
7
+ before do
8
+ @person = Person.new :id => 1, :name => "Alice", :age => 20, :email => "foo@bar.com"
9
+ end
10
+
11
+ it "should not be changed after created" do
12
+ @person.should_not be_changed
13
+ end
14
+
15
+ it "should not be changed if attribute is set to what it currently is" do
16
+ @person.name = "Alice"
17
+ @person.should_not be_changed
18
+ end
19
+
20
+ it "should notice changes" do
21
+ @person.name = "Bob"
22
+ @person.should be_changed
23
+ end
24
+
25
+ it "should know when a attribute is set to it's original value" do
26
+ original_name = @person.name
27
+ @person.name = "Bob"
28
+ @person.name = original_name
29
+ @person.should_not be_changed
30
+ end
31
+
32
+ it "should always keep the objects original value as _was" do
33
+ original_name = @person.name
34
+ @person.name = "Bob"
35
+ @person.name = "Foo"
36
+ @person.name_was.should == original_name
37
+ end
38
+
39
+ it "should return what name was" do
40
+ @person.name = "Bob"
41
+ @person.name_was.should == "Alice"
42
+ end
43
+
44
+
45
+ describe "should reset changes" do
46
+ it "on save" do
47
+ @person.name = "Bob"
48
+ @person.save
49
+ @person.should_not be_changed
50
+ end
51
+
52
+ it "on save, but don't do it if save fails validation" do
53
+ @person.should_receive(:valid?).and_return(false)
54
+ @person.name = "Bob"
55
+ @person.save
56
+ @person.should be_changed
57
+ end
58
+
59
+ it "on save!" do
60
+ @person.name = "Bob"
61
+ @person.save!
62
+ @person.should_not be_changed
63
+ end
64
+
65
+ it "on reload" do
66
+ @person.name = "Bob"
67
+ @person.reload
68
+ @person.should_not be_changed
69
+ end
70
+ end
71
+
72
+ describe "previous changes" do
73
+ it "should be blank before save" do
74
+ @person.previous_changes.should be_blank
75
+ end
76
+
77
+ it "should equal to changes before save" do
78
+ @person.name = "Bob"
79
+ changes_before_save = @person.changes
80
+
81
+ @person.save
82
+
83
+ @person.changes.should be_empty
84
+ @person.previous_changes.should == changes_before_save
85
+ end
86
+
87
+ it "should equal to changes before save!" do
88
+ @person.name = "Bob"
89
+ changes_before_save = @person.changes
90
+
91
+ @person.save!
92
+
93
+ @person.changes.should be_empty
94
+ @person.previous_changes.should == changes_before_save
95
+ end
96
+
97
+ it "should be nil after a reload" do
98
+ @person.name = "Bob"
99
+ @person.save
100
+ @person.reload
101
+ @person.previous_changes.should be_blank
102
+ end
103
+ end
104
+ end
105
+
106
+
107
+ describe "database run" do
108
+ include SetUpHbaseConnectionBeforeAll
109
+ include SetTableNamesToTestTable
110
+
111
+ before do
112
+ @person = Person.new
113
+ @person.id = "test"
114
+ @person.points = "25"
115
+ @person.date_of_birth = "19850730"
116
+ @person.status = "0"
117
+ end
118
+
119
+ it "should update dirty status correctly after a reload" do
120
+ @person.addresses = {:something => "strage"}
121
+ @person.save! :validate => false
122
+ @person.reload
123
+ @person.addresses = {}
124
+ @person.save! :validate => false
125
+ @person.reload
126
+ @person.addresses.should == {}
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,49 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'spec_helper'
3
+ require 'orm/models/person'
4
+
5
+ describe "encoding" do
6
+
7
+ describe "with ORM" do
8
+ include SetUpHbaseConnectionBeforeAll
9
+ include SetTableNamesToTestTable
10
+
11
+ before do
12
+ @person = Person.create! :id => "new_id", :name => "Thorbjørn", :age => "22"
13
+ @person_from_db = Person.find(@person.id)
14
+ end
15
+
16
+ it "should be able to store UTF-8 encoded strings" do
17
+ @person_from_db.should == @person
18
+ @person_from_db.name.should == "Thorbjørn"
19
+ end
20
+
21
+ it "should return string as UTF-8 encoded strings" do
22
+ @person_from_db.name.encoding.should == Encoding::UTF_8
23
+ end
24
+ end
25
+
26
+ describe "without ORM" do
27
+ include CreatePersonBeforeEach
28
+
29
+ before do
30
+ @id = "ID-encoding-test"
31
+
32
+ @row = MassiveRecord::Wrapper::Row.new
33
+ @row.table = @table
34
+ @row.id = @id
35
+ @row.values = {:info => {:name => "Thorbjørn", :email => "john@base.com", :age => "20"}}
36
+ @row.save
37
+
38
+ @row_from_db = @table.find(@id)
39
+ end
40
+
41
+ it "should be able to store UTF-8 encoded strings" do
42
+ @row_from_db.values["info:name"].should == "Thorbjørn"
43
+ end
44
+
45
+ it "should return string as UTF-8 encoded strings" do
46
+ @row_from_db.values["info:name"].encoding.should == Encoding::UTF_8
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,208 @@
1
+ require 'spec_helper'
2
+ require 'orm/models/test_class'
3
+ require 'orm/models/person'
4
+
5
+ describe "finders" do
6
+ describe "#find dry test" do
7
+ include MockMassiveRecordConnection
8
+
9
+ before do
10
+ @mocked_table = mock(MassiveRecord::Wrapper::Table).as_null_object
11
+ Person.stub(:table).and_return(@mocked_table)
12
+
13
+ @row = MassiveRecord::Wrapper::Row.new
14
+ @row.id = "ID1"
15
+ @row.values = { :info => { :name => "John Doe", :age => "29" } }
16
+
17
+ @row_2 = MassiveRecord::Wrapper::Row.new
18
+ @row_2.id = "ID2"
19
+ @row_2.values = { :info => { :name => "Bob", :age => "18" } }
20
+ end
21
+
22
+ it "should have at least one argument" do
23
+ lambda { Person.find }.should raise_error ArgumentError
24
+ end
25
+
26
+ it "should simply return nil on first if table does not exists" do
27
+ Person.table.should_receive(:exists?).and_return false
28
+ Person.first.should be_nil
29
+ end
30
+
31
+ it "should simply return nil on find if table does not exists" do
32
+ Person.table.should_receive(:exists?).and_return false
33
+ Person.find(1).should be_nil
34
+ end
35
+
36
+ it "should simply return empty array if table does not exists" do
37
+ Person.table.should_receive(:exists?).and_return false
38
+ Person.all.should == []
39
+ end
40
+
41
+ it "should raise RecordNotFound if id is nil" do
42
+ lambda { Person.find(nil) }.should raise_error MassiveRecord::ORM::RecordNotFound
43
+ end
44
+
45
+ it "should raise an error if conditions are given to first" do
46
+ lambda { Person.first(:conditions => "foo = 'bar'") }.should raise_error ArgumentError
47
+ end
48
+
49
+ it "should raise an error if conditions are given to all" do
50
+ lambda { Person.all(:conditions => "foo = 'bar'") }.should raise_error ArgumentError
51
+ end
52
+
53
+ it "should raise an error if conditions are given to find" do
54
+ lambda { Person.find(:conditions => "foo = 'bar'") }.should raise_error ArgumentError
55
+ end
56
+
57
+ it "should ask the table to look up by it's id" do
58
+ @mocked_table.should_receive(:find).with("ID1", anything).and_return(@row)
59
+ Person.find("ID1")
60
+ end
61
+
62
+ it "should ask the table to fetch rows from a list of ids given as array" do
63
+ @mocked_table.should_receive(:find).with(["ID1", "ID2"], anything).and_return([@row, @row_2])
64
+ people = Person.find(["ID1", "ID2"])
65
+ people.should be_instance_of Array
66
+ people.first.should be_instance_of Person
67
+ people.first.id.should == "ID1"
68
+ people.last.id.should == "ID2"
69
+ end
70
+
71
+ it "should ask table to fetch rows from a list of ids given as arguments" do
72
+ @mocked_table.should_receive(:find).with(["ID1", "ID2"], anything).and_return([@row, @row_2])
73
+ people = Person.find("ID1", "ID2")
74
+ people.should be_instance_of Array
75
+ people.first.should be_instance_of Person
76
+ people.first.id.should == "ID1"
77
+ people.last.id.should == "ID2"
78
+ end
79
+
80
+ it "should raise error if not all multiple ids are found" do
81
+ @mocked_table.should_receive(:find).with(["ID1", "ID2"], anything).and_return([@row])
82
+ lambda { Person.find("ID1", "ID2") }.should raise_error MassiveRecord::ORM::RecordNotFound
83
+ end
84
+
85
+ it "should call table's first on find(:first)" do
86
+ @mocked_table.should_receive(:first).and_return(@row)
87
+ Person.find(:first)
88
+ end
89
+
90
+ it "should call table's all on find(:all)" do
91
+ @mocked_table.should_receive(:all).and_return([@row])
92
+ Person.find(:all)
93
+ end
94
+
95
+ it "should return empty array on all if no results was found" do
96
+ @mocked_table.should_receive(:all).and_return([])
97
+ Person.all.should == []
98
+ end
99
+
100
+ it "should return nil on first if no results was found" do
101
+ @mocked_table.should_receive(:first).and_return(nil)
102
+ Person.first.should be_nil
103
+ end
104
+
105
+ it "should raise an error if not exactly the id is found" do
106
+ @mocked_table.should_receive(:find).and_return(@row)
107
+ lambda { Person.find("ID") }.should raise_error(MassiveRecord::ORM::RecordNotFound)
108
+ end
109
+
110
+ it "should raise error if not all ids are found" do
111
+ @mocked_table.should_receive(:find).and_return([@row, @row_2])
112
+ lambda { Person.find("ID", "ID2") }.should raise_error(MassiveRecord::ORM::RecordNotFound)
113
+ end
114
+ end
115
+
116
+ %w(first all).each do |method|
117
+ it "should respond to #{method}" do
118
+ TestClass.should respond_to method
119
+ end
120
+
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)
124
+ end
125
+
126
+ it "should delegate #{method}'s call to find with it's args as second argument" do
127
+ options = {:foo => :bar}
128
+ TestClass.should_receive(:find).with(anything, options)
129
+ TestClass.send(method, options)
130
+ end
131
+ end
132
+
133
+
134
+
135
+
136
+ describe "#find database test" do
137
+ include CreatePersonBeforeEach
138
+
139
+ before do
140
+ @person = Person.find("ID1")
141
+
142
+ @row = MassiveRecord::Wrapper::Row.new
143
+ @row.id = "ID2"
144
+ @row.values = {:info => {:name => "Bob", :email => "bob@base.com", :age => "26"}}
145
+ @row.table = @table
146
+ @row.save
147
+
148
+ @bob = Person.find("ID2")
149
+ end
150
+
151
+ it "should return nil if id is not found" do
152
+ lambda { Person.find("not_found") }.should raise_error MassiveRecord::ORM::RecordNotFound
153
+ end
154
+
155
+ it "should return the person object when found" do
156
+ @person.name.should == "John Doe"
157
+ @person.email.should == "john@base.com"
158
+ @person.age.should == 20
159
+ end
160
+
161
+ it "should find first person" do
162
+ Person.first.should == @person
163
+ end
164
+
165
+ it "should find all" do
166
+ all = Person.all
167
+ all.should include @person, @bob
168
+ all.length.should == 2
169
+ end
170
+ end
171
+
172
+ describe "#find_in_batches" do
173
+ include CreatePeopleBeforeEach
174
+
175
+ it "should iterate through a collection of group of rows using a batch process" do
176
+ group_number = 0
177
+ batch_size = 3
178
+ Person.find_in_batches(:batch_size => batch_size) do |rows|
179
+ group_number += 1
180
+ rows.each do |row|
181
+ row.id.should_not be_nil
182
+ end
183
+ end
184
+ group_number.should == @table_size / 3
185
+ end
186
+
187
+ it "should iterate through a collection of rows using a batch process" do
188
+ rows_number = 0
189
+ Person.find_each(:batch_size => 3) do |row|
190
+ row.id.should_not be_nil
191
+ rows_number += 1
192
+ end
193
+ rows_number.should == @table_size
194
+ end
195
+ end
196
+
197
+ describe "#exists?" do
198
+ include CreatePersonBeforeEach
199
+
200
+ it "should return true if a row exists with given id" do
201
+ Person.exists?("ID1").should be_true
202
+ end
203
+
204
+ it "should return false if a row does not exists with given id" do
205
+ Person.exists?("unkown").should be_false
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe "connection" do
4
+
5
+ describe "a new connection" do
6
+
7
+ before do
8
+ @connection ||= MassiveRecord::Wrapper::Connection.new(:host => MR_CONFIG['host'], :port => MR_CONFIG['port'])
9
+ end
10
+
11
+ end
12
+
13
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+ require 'orm/models/person'
3
+
4
+ describe "translation and naming" do
5
+ before do
6
+ I18n.backend = I18n::Backend::Simple.new
7
+ end
8
+
9
+ describe "of an attribute" do
10
+ before do
11
+ I18n.backend.store_translations 'en', :activemodel => {:attributes => {:person => {:name => "person's name"} } }
12
+ end
13
+
14
+ it "should look up an by a string" do
15
+ Person.human_attribute_name("name").should == "person's name"
16
+ end
17
+
18
+ it "should look up an by a symbol" do
19
+ Person.human_attribute_name(:name).should == "person's name"
20
+ end
21
+ end
22
+
23
+ describe "of a model" do
24
+ before do
25
+ I18n.backend.store_translations 'en', :activemodel => {:models => {:person => 'A person object'}}
26
+ end
27
+
28
+ it "should return it's human name" do
29
+ Person.model_name.human.should == "A person object"
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+ require 'orm/models/person'
3
+
4
+ describe "id factory" do
5
+ it "should be a singleton" do
6
+ MassiveRecord::ORM::IdFactory.included_modules.should include(Singleton)
7
+ end
8
+
9
+ describe "#next_for" do
10
+ describe "dry" do
11
+ include MockMassiveRecordConnection
12
+
13
+ before do
14
+ @factory = MassiveRecord::ORM::IdFactory.instance
15
+ end
16
+
17
+ it "should respond to next_for" do
18
+ @factory.should respond_to :next_for
19
+ end
20
+
21
+ it "should use incomming table name if it's a string" do
22
+ @factory.should_receive(:next_id).with(hash_including(:table => "test_table"))
23
+ @factory.next_for "test_table"
24
+ end
25
+
26
+ it "should use incomming table name if it's a symbol" do
27
+ @factory.should_receive(:next_id).with(hash_including(:table => "test_table"))
28
+ @factory.next_for :test_table
29
+ end
30
+
31
+ it "should ask object for it's table name if it responds to that" do
32
+ Person.should_receive(:table_name).and_return("people")
33
+ @factory.should_receive(:next_id).with(hash_including(:table => "people"))
34
+ @factory.next_for(Person)
35
+ end
36
+
37
+ it "should have class method next_for and delegate it to it's instance" do
38
+ @factory.should_receive(:next_for).with("cars")
39
+ MassiveRecord::ORM::IdFactory.next_for("cars")
40
+ end
41
+ end
42
+
43
+
44
+
45
+ describe "database" do
46
+ include SetUpHbaseConnectionBeforeAll
47
+ include SetTableNamesToTestTable
48
+
49
+ before do
50
+ @factory = MassiveRecord::ORM::IdFactory.instance
51
+ end
52
+
53
+ after do
54
+ MassiveRecord::ORM::IdFactory.destroy_all
55
+ MassiveRecord::ORM::IdFactory.instance_variable_set(:@instance, nil)
56
+ end
57
+
58
+ it "should increment start a new sequence on 1" do
59
+ @factory.next_for(Person).should == 1
60
+ end
61
+
62
+ it "should increment value one by one" do
63
+ 5.times do |index|
64
+ expected_id = index + 1
65
+ @factory.next_for(Person).should == expected_id
66
+ end
67
+ end
68
+
69
+ it "should maintain ids separate for each table" do
70
+ 3.times { @factory.next_for(Person) }
71
+ @factory.next_for("cars").should == 1
72
+ end
73
+ end
74
+ end
75
+ end