massive_record 0.1.0

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 (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