rod 0.7.1 → 0.7.2

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 (76) hide show
  1. data/.travis.yml +1 -1
  2. data/README.rdoc +38 -10
  3. data/Rakefile +20 -9
  4. data/changelog.txt +25 -0
  5. data/contributors.txt +1 -0
  6. data/data/backward/0.7.0/_join_element.dat +0 -0
  7. data/data/backward/0.7.0/_polymorphic_join_element.dat +0 -0
  8. data/data/backward/0.7.0/char.dat +0 -0
  9. data/data/backward/0.7.0/database.yml +58 -0
  10. data/data/backward/0.7.0/rod_test__automobile.dat +0 -0
  11. data/data/backward/0.7.0/rod_test__caveman.dat +0 -0
  12. data/data/backward/0.7.0/rod_test__dog.dat +0 -0
  13. data/data/backward/0.7.0/rod_test__test_model.dat +0 -0
  14. data/data/portability/_join_element.dat +0 -0
  15. data/data/portability/_polymorphic_join_element.dat +0 -0
  16. data/data/portability/char.dat +0 -0
  17. data/data/portability/database.yml +49 -0
  18. data/data/portability/rod_test__automobile.dat +0 -0
  19. data/data/portability/rod_test__caveman.dat +0 -0
  20. data/data/portability/rod_test__dog.dat +0 -0
  21. data/data/portability/rod_test__test_model.dat +0 -0
  22. data/features/backward.feature +33 -0
  23. data/features/basic.feature +3 -0
  24. data/features/collection_proxy.feature +95 -0
  25. data/features/flat_indexing.feature +44 -2
  26. data/features/hash_indexing.feature +63 -9
  27. data/features/portability.feature +72 -0
  28. data/features/segmented_indexing.feature +45 -2
  29. data/features/steps/collection_proxy.rb +1 -1
  30. data/features/steps/model.rb +48 -5
  31. data/features/steps/rod.rb +15 -16
  32. data/lib/rod.rb +11 -1
  33. data/lib/rod/abstract_database.rb +52 -42
  34. data/lib/rod/berkeley/collection_proxy.rb +96 -0
  35. data/lib/rod/berkeley/database.rb +337 -0
  36. data/lib/rod/berkeley/environment.rb +209 -0
  37. data/lib/rod/berkeley/sequence.rb +222 -0
  38. data/lib/rod/berkeley/transaction.rb +233 -0
  39. data/lib/rod/collection_proxy.rb +76 -1
  40. data/lib/rod/constants.rb +3 -2
  41. data/lib/rod/database.rb +127 -14
  42. data/lib/rod/index/base.rb +12 -3
  43. data/lib/rod/index/hash_index.rb +295 -70
  44. data/lib/rod/index/segmented_index.rb +3 -0
  45. data/lib/rod/model.rb +154 -531
  46. data/lib/rod/property/base.rb +190 -0
  47. data/lib/rod/property/field.rb +258 -0
  48. data/lib/rod/property/plural_association.rb +145 -0
  49. data/lib/rod/property/singular_association.rb +139 -0
  50. data/rod.gemspec +6 -4
  51. data/spec/berkeley/database.rb +83 -0
  52. data/spec/berkeley/environment.rb +58 -0
  53. data/spec/berkeley/sequence.rb +101 -0
  54. data/spec/berkeley/transaction.rb +92 -0
  55. data/spec/collection_proxy.rb +38 -0
  56. data/spec/database.rb +36 -0
  57. data/spec/model.rb +26 -0
  58. data/spec/property/base.rb +73 -0
  59. data/spec/property/field.rb +244 -0
  60. data/spec/property/plural_association.rb +67 -0
  61. data/spec/property/singular_association.rb +65 -0
  62. data/tests/class_compatibility_create.rb +2 -2
  63. data/tests/eff1_test.rb +1 -1
  64. data/tests/eff2_test.rb +1 -1
  65. data/tests/full_runs.rb +1 -1
  66. data/tests/generate_classes_create.rb +14 -14
  67. data/tests/migration_create.rb +47 -47
  68. data/tests/migration_verify.rb +1 -1
  69. data/tests/missing_class_create.rb +6 -6
  70. data/tests/properties_order_create.rb +4 -4
  71. data/tests/read_on_create.rb +33 -34
  72. data/tests/save_struct.rb +40 -39
  73. data/tests/unit/database.rb +1 -1
  74. data/tests/unit/model_tests.rb +73 -65
  75. metadata +71 -15
  76. data/tests/unit/model.rb +0 -36
@@ -0,0 +1,139 @@
1
+ require 'rod/property/base'
2
+
3
+ module Rod
4
+ module Property
5
+ # This class defines the +has_one+ (singular association) property.
6
+ # A +has_one+ property has to define its +name+.
7
+ class SingularAssociation < Base
8
+ # Creates new singular association associated with +klass+,
9
+ # with given +name+ and +options+.
10
+ # Valid options are:
11
+ # * +:class_name+ - the name of the class (as String) associated
12
+ # with this class
13
+ # * +:polymorphic+ - if set to +true+ the association is polymorphic (allows to access
14
+ # objects of different classes via this association).
15
+ def initialize(klass,name,options={})
16
+ super(klass,name,options)
17
+ end
18
+
19
+ # Predicate indicating that this property is a field.
20
+ def field?
21
+ false
22
+ end
23
+
24
+ # Predicate indicating that this property is an association.
25
+ def association?
26
+ true
27
+ end
28
+
29
+ # Predicate indicating that this property is a singular association.
30
+ def singular?
31
+ true
32
+ end
33
+
34
+ # Predicate indicating that this property is not a plural association.
35
+ def plural?
36
+ false
37
+ end
38
+
39
+ # Predicate indicating that this property is polymorphic.
40
+ def polymorphic?
41
+ @options[:polymorphic]
42
+ end
43
+
44
+ # Returns the metadata of the association in form of a hash.
45
+ def metadata
46
+ @options.dup
47
+ end
48
+
49
+ # Converts the association to fields in a C struct.
50
+ def to_c_struct
51
+ result = " #{c_type(:ulong)} #{@name};\n"
52
+ if polymorphic?
53
+ result += " #{c_type(:ulong)} #{@name}__class;\n"
54
+ end
55
+ result
56
+ end
57
+
58
+ # Defines the accessor of the association's constituents
59
+ # (C struct field/fields that hold the association data).
60
+ def define_c_accessors(builder)
61
+ field_reader(@name,@klass.struct_name,c_type(:ulong),builder)
62
+ field_writer(@name,@klass.struct_name,c_type(:ulong),builder)
63
+ if polymorphic?
64
+ field_reader("#{@name}__class",@klass.struct_name,c_type(:ulong),builder)
65
+ field_writer("#{@name}__class",@klass.struct_name,c_type(:ulong),builder)
66
+ end
67
+ end
68
+
69
+ # Make the C accessors private.
70
+ def seal_c_accessors
71
+ @klass.send(:private,"_#{@name}")
72
+ @klass.send(:private,"_#{@name}=")
73
+ end
74
+
75
+ # Defines the getter of the Ruby class which corresponds to this association.
76
+ def define_getter
77
+ # optimization
78
+ name = @name.to_s
79
+ property = self
80
+ class_name =
81
+ if @options[:class_name]
82
+ @options[:class_name]
83
+ else
84
+ "#{@klass.scope_name}::#{name.camelcase}"
85
+ end
86
+ klass = options[:polymorphic] ? nil : class_name.constantize
87
+ @klass.send(:define_method,name) do
88
+ value = instance_variable_get("@#{name}")
89
+ if value.nil?
90
+ return nil if self.new?
91
+ rod_id = send("_#{name}",@rod_id)
92
+ # the indices are shifted by 1, to leave 0 for nil
93
+ if rod_id == 0
94
+ value = nil
95
+ else
96
+ if property.polymorphic?
97
+ klass = Model.get_class(send("_#{name}__class",@rod_id))
98
+ end
99
+ value = klass.find_by_rod_id(rod_id)
100
+ end
101
+ # avoid change tracking
102
+ instance_variable_set("@#{name}",value)
103
+ end
104
+ value
105
+ end
106
+ end
107
+
108
+ # Defines the settor of the Ruby class which corresponds to this association.
109
+ def define_setter
110
+ # optimization
111
+ name = @name.to_s
112
+ @klass.send(:define_method,"#{name}=") do |value|
113
+ old_value = send(name)
114
+ send("#{name}_will_change!") unless old_value == value
115
+ instance_variable_set("@#{name}", value)
116
+ value
117
+ end
118
+ end
119
+
120
+ # Returns the memory layout of the C struct fields that
121
+ # correspond to this association.
122
+ def layout
123
+ unless polymorphic?
124
+ "#{@name}[value:#{sizeof(:ulong)}]"
125
+ else
126
+ "#{@name}[value:#{sizeof(:ulong)}+" +
127
+ "class:#{sizeof(:ulong)}]"
128
+ end
129
+ end
130
+
131
+ protected
132
+ # Check if the property has valid options.
133
+ # An exceptions is raised if they are not.
134
+ def check_options(options)
135
+ #TODO implement
136
+ end
137
+ end
138
+ end
139
+ end
@@ -11,8 +11,8 @@ Gem::Specification.new do |s|
11
11
  s.authors = ['Aleksander Pohl']
12
12
  s.email = ["apohllo@o2.pl"]
13
13
  s.homepage = "http://github.com/apohllo/rod"
14
- s.summary = "Ruby object database"
15
- s.description = "Ruby object database is designed for large amount of data, whose structure rarely changes."
14
+ s.summary = "Ruby Object Database"
15
+ s.description = "Ruby Object Database is designed for large amounts of data, whose structure rarely changes."
16
16
 
17
17
  s.rubyforge_project = "rod"
18
18
  s.rdoc_options = ["--main", "README.rdoc"]
@@ -24,10 +24,12 @@ Gem::Specification.new do |s|
24
24
 
25
25
  s.add_dependency("RubyInline", [">= 3.10.0","< 4.0.0"])
26
26
  s.add_dependency("english", [">= 0.5.0","< 0.6.0"])
27
- s.add_dependency("activemodel", [">= 3.0.7","< 3.1.0"])
27
+ s.add_dependency("activemodel", ["~> 3.2.2"])
28
28
  s.add_dependency("bsearch", [">= 1.5.0","< 1.6.0"])
29
- s.add_development_dependency("mocha", [">= 0.9.8","< 1.0.0"])
29
+
30
+ s.add_development_dependency("mocha", "~> 0.9.8")
30
31
  s.add_development_dependency("cucumber", "~> 1.0.0")
31
32
  s.add_development_dependency("rspec", [">= 2.2.0","< 2.3.0"])
32
33
  s.add_development_dependency("rake", [">= 0.9.0","< 1.0.0"])
34
+ s.add_development_dependency("minitest", "~> 2.7.0")
33
35
  end
@@ -0,0 +1,83 @@
1
+ require 'bundler/setup'
2
+ require 'minitest/autorun'
3
+ require 'rod'
4
+
5
+ describe Rod::Berkeley::Database do
6
+ describe "a database" do
7
+ before do
8
+ environment = Rod::Berkeley::Environment.new
9
+ environment.open("tmp/env_db", :create => true, :cache => true)
10
+ @database = Rod::Berkeley::Database.new(environment)
11
+ end
12
+
13
+ after do
14
+ @database.close
15
+ @database.environment.close
16
+ end
17
+
18
+ it "should not be in opened state befor being opened" do
19
+ @database.opened?.must_equal false
20
+ end
21
+
22
+ it "should allow to open itself" do
23
+ proc {@database.open("db1.db", :hash, :create => true, :truncate => true)}.must_be_silent
24
+ end
25
+
26
+ it "should be in opened state after being opened" do
27
+ @database.open("db1.db", :hash, :create => true, :truncate => true)
28
+ @database.opened?.must_equal true
29
+ end
30
+
31
+ it "should not allow to open itself twice" do
32
+ @database.open("db1.db", :hash, :create => true, :truncate => true)
33
+ proc {@database.open("db1.db", :hash, :create => true, :truncate => true)}.must_raise Rod::DatabaseError
34
+ end
35
+
36
+ it "should allow to close itself" do
37
+ proc {@database.close}.must_be_silent
38
+ end
39
+
40
+ it "should not be in opened state after being closed" do
41
+ @database.open("db1.db", :hash, :create => true, :truncate => true)
42
+ @database.close
43
+ @database.opened?.must_equal false
44
+ end
45
+ end
46
+
47
+ describe "a hash database with transactions enabled" do
48
+ before do
49
+ environment = Rod::Berkeley::Environment.new
50
+ environment.open("tmp/env_txn", :create => true, :cache => true, :transactions => true,
51
+ :logging => true, :locking => true)
52
+ @database = Rod::Berkeley::Database.new(environment)
53
+ @database.open("db1.db", :hash, :create => true, :auto_commit => true)
54
+ end
55
+
56
+ after do
57
+ @database.close
58
+ @database.environment.close
59
+ end
60
+
61
+ it "should allow to add data with transactional protection" do
62
+ transaction = Rod::Berkeley::Transaction.new(@database.environment)
63
+ transaction.begin
64
+ @database.put("Ruby","Matz",transaction)
65
+ transaction.commit
66
+
67
+ transaction.reset
68
+ transaction.begin
69
+ @database.get("Ruby").must_equal "Matz"
70
+ transaction.commit
71
+
72
+ transaction.reset
73
+ transaction.begin
74
+ @database.put("Ruby","Matsumoto San",transaction)
75
+ transaction.abort
76
+
77
+ transaction.reset
78
+ transaction.begin
79
+ @database.get("Ruby").must_equal "Matz"
80
+ transaction.commit
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,58 @@
1
+ require 'bundler/setup'
2
+ require 'minitest/autorun'
3
+ require 'rod'
4
+
5
+ describe Rod::Berkeley::Environment do
6
+ describe "an environment" do
7
+ before do
8
+ @environment = Rod::Berkeley::Environment.new
9
+ end
10
+
11
+ after do
12
+ @environment.close
13
+ end
14
+
15
+ it "should not be in opened state before being opened" do
16
+ @environment.opened?.must_equal false
17
+ end
18
+
19
+ it "should allow to open itself" do
20
+ proc {@environment.open("tmp/env", :create => true)}.must_be_silent
21
+ end
22
+
23
+ it "should not allow to open it twice" do
24
+ @environment.open("tmp/env", :create => true)
25
+ proc {@environment.open("tmp/env")}.must_raise Rod::DatabaseError
26
+ end
27
+
28
+ it "should be in opened state after being opened" do
29
+ @environment.open("tmp/env", :create => true)
30
+ @environment.opened?.must_equal true
31
+ end
32
+
33
+ it "should not be in opened state after being closed" do
34
+ @environment.open("tmp/env", :create => true)
35
+ @environment.close
36
+ @environment.opened?.must_equal false
37
+ end
38
+ end
39
+
40
+ describe "an opened environment" do
41
+ before do
42
+ @environment = Rod::Berkeley::Environment.new
43
+ @environment.open("tmp/env", :create => true)
44
+ end
45
+
46
+ after do
47
+ @environment.close
48
+ end
49
+
50
+ it "should be in opened state" do
51
+ @environment.opened?.must_equal true
52
+ end
53
+
54
+ it "should allow to close it" do
55
+ proc {@environment.close}.must_be_silent
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,101 @@
1
+ require 'bundler/setup'
2
+ require 'minitest/autorun'
3
+ require 'rod'
4
+
5
+ describe Rod::Berkeley::Sequence do
6
+ describe "a sequence" do
7
+ before do
8
+ environment = Rod::Berkeley::Environment.new
9
+ environment.open("tmp/env_seq", :create => true, :cache => true)
10
+ database = Rod::Berkeley::Database.new(environment)
11
+ database.open("db1.db", :hash, :create => true, :truncate => true)
12
+ @sequence = Rod::Berkeley::Sequence.new(database)
13
+ end
14
+
15
+ after do
16
+ @sequence.close
17
+ @sequence.database.close
18
+ @sequence.database.environment.close
19
+ end
20
+
21
+ it "should allow to open itself with 'sequence' as the key" do
22
+ proc {@sequence.open("sequence", nil, :create => true)}.must_be_silent
23
+ end
24
+
25
+ it "should allow to close itself" do
26
+ @sequence.open("sequence", nil, :create => true)
27
+ proc {@sequence.close}.must_be_silent
28
+ end
29
+ end
30
+
31
+ describe "an opened sequence" do
32
+ before do
33
+ environment = Rod::Berkeley::Environment.new
34
+ environment.open("tmp/env_seq", :create => true, :cache => true)
35
+ database = Rod::Berkeley::Database.new(environment)
36
+ database.open("db1.db", :hash, :create => true, :truncate => true)
37
+ @sequence = Rod::Berkeley::Sequence.new(database)
38
+ @sequence.open("sequence", nil, :create => true)
39
+ end
40
+
41
+ after do
42
+ @sequence.close
43
+ @sequence.database.close
44
+ @sequence.database.environment.close
45
+ end
46
+
47
+ it "should return 1 as its first value" do
48
+ @sequence.next.must_equal 1
49
+ end
50
+
51
+ it "should return 3 as the second value with delta set to 2" do
52
+ @sequence.next(nil,:delta => 2)
53
+ @sequence.next.must_equal 3
54
+ end
55
+ end
56
+
57
+ describe "an opened sequence with large cache" do
58
+ before do
59
+ environment = Rod::Berkeley::Environment.new
60
+ environment.open("tmp/env_seq_txn", :create => true, :cache => true,
61
+ :transactions => true, :logging => true, :locking => true)
62
+ database = Rod::Berkeley::Database.new(environment)
63
+ database.open("db1.db", :hash, :create => true, :auto_commit => true)
64
+ @sequence = Rod::Berkeley::Sequence.new(database)
65
+ @sequence.open("sequence", nil, :create => true, :cache_size => 1000)
66
+ end
67
+
68
+ after do
69
+ @sequence.close
70
+ @sequence.database.close
71
+ @sequence.database.environment.close
72
+ end
73
+
74
+ it "should retrive 100 000 values fast" do
75
+ 100_000.times{ @sequence.next}
76
+ end
77
+ end
78
+
79
+ describe "an opened sequence with small cache" do
80
+ before do
81
+ environment = Rod::Berkeley::Environment.new
82
+ environment.open("tmp/env_seq_txn", :create => true, :cache => true,
83
+ :transactions => true, :logging => true, :locking => true)
84
+ database = Rod::Berkeley::Database.new(environment)
85
+ database.open("db1.db", :hash, :create => true, :auto_commit => true)
86
+ @sequence = Rod::Berkeley::Sequence.new(database)
87
+ @sequence.open("sequence", nil, :create => true)
88
+ end
89
+
90
+ after do
91
+ @sequence.close
92
+ @sequence.database.close
93
+ @sequence.database.environment.close
94
+ end
95
+
96
+ it "should retrive 100 000 values fast is syncing is disabled" do
97
+ 100_000.times{ @sequence.next(nil, :no_sync => true)}
98
+ end
99
+ end
100
+
101
+ end
@@ -0,0 +1,92 @@
1
+ require 'bundler/setup'
2
+ require 'minitest/autorun'
3
+ require 'rod'
4
+
5
+ describe Rod::Berkeley::Transaction do
6
+ describe "a transaction" do
7
+ before do
8
+ environment = Rod::Berkeley::Environment.new
9
+ environment.open("tmp/env_txn", :create => true, :cache => true, :transactions => true,
10
+ :logging => true, :locking => true)
11
+ @transaction = Rod::Berkeley::Transaction.new(environment)
12
+ end
13
+
14
+ after do
15
+ @transaction.finish
16
+ @transaction.environment.close
17
+ end
18
+
19
+ it "should not be in started state after creation" do
20
+ @transaction.started?.must_equal false
21
+ end
22
+
23
+ it "should not be in finished state after creation" do
24
+ @transaction.finished?.must_equal false
25
+ end
26
+
27
+ it "should allow to start itself" do
28
+ proc {@transaction.begin}.must_be_silent
29
+ end
30
+
31
+ it "should allow to start itself in no_sync mode" do
32
+ proc {@transaction.begin(:no_sync => true)}.must_be_silent
33
+ end
34
+
35
+ it "should allow to start itself in sync mode" do
36
+ proc {@transaction.begin(:sync => true)}.must_be_silent
37
+ end
38
+
39
+ it "should allow to start itself in write_no_sync mode" do
40
+ proc {@transaction.begin(:write_no_sync => true)}.must_be_silent
41
+ end
42
+
43
+ it "should be in started state after being started" do
44
+ @transaction.begin
45
+ @transaction.started?.must_equal true
46
+ end
47
+
48
+ it "should not be in finished state after being started" do
49
+ @transaction.begin
50
+ @transaction.finished?.must_equal false
51
+ end
52
+
53
+ it "should be in started state after being committed" do
54
+ @transaction.begin
55
+ @transaction.commit
56
+ @transaction.started?.must_equal true
57
+ end
58
+
59
+ it "should be in finished state after being committed" do
60
+ @transaction.begin
61
+ @transaction.commit
62
+ @transaction.finished?.must_equal true
63
+ end
64
+
65
+ it "should be in started state after being aborted" do
66
+ @transaction.begin
67
+ @transaction.abort
68
+ @transaction.started?.must_equal true
69
+ end
70
+
71
+ it "should be in finished state after being aborted" do
72
+ @transaction.begin
73
+ @transaction.abort
74
+ @transaction.finished?.must_equal true
75
+ end
76
+
77
+ it "should allow to reset itself before being started" do
78
+ proc {@transaction.reset}.must_be_silent
79
+ end
80
+
81
+ it "should allow to reset itself after being finished" do
82
+ @transaction.begin
83
+ @transaction.abort
84
+ proc {@transaction.reset}.must_be_silent
85
+ end
86
+
87
+ it "should not allow to reset itself before being finished" do
88
+ @transaction.begin
89
+ proc {@transaction.reset}.must_raise Rod::DatabaseError
90
+ end
91
+ end
92
+ end