og 0.20.0 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/CHANGELOG +796 -664
  2. data/INSTALL +24 -24
  3. data/README +39 -32
  4. data/Rakefile +41 -42
  5. data/benchmark/bench.rb +36 -36
  6. data/doc/AUTHORS +15 -12
  7. data/doc/LICENSE +3 -3
  8. data/doc/RELEASES +311 -243
  9. data/doc/config.txt +1 -1
  10. data/examples/mysql_to_psql.rb +15 -15
  11. data/examples/run.rb +92 -92
  12. data/install.rb +7 -17
  13. data/lib/og.rb +76 -75
  14. data/lib/og/collection.rb +203 -160
  15. data/lib/og/entity.rb +168 -169
  16. data/lib/og/errors.rb +5 -5
  17. data/lib/og/manager.rb +179 -178
  18. data/lib/og/mixin/hierarchical.rb +107 -107
  19. data/lib/og/mixin/optimistic_locking.rb +36 -36
  20. data/lib/og/mixin/orderable.rb +148 -148
  21. data/lib/og/mixin/timestamped.rb +8 -8
  22. data/lib/og/mixin/tree.rb +124 -124
  23. data/lib/og/relation.rb +237 -213
  24. data/lib/og/relation/belongs_to.rb +5 -5
  25. data/lib/og/relation/has_many.rb +60 -58
  26. data/lib/og/relation/joins_many.rb +93 -47
  27. data/lib/og/relation/refers_to.rb +25 -21
  28. data/lib/og/store.rb +210 -207
  29. data/lib/og/store/filesys.rb +79 -79
  30. data/lib/og/store/kirby.rb +263 -258
  31. data/lib/og/store/memory.rb +261 -261
  32. data/lib/og/store/mysql.rb +288 -284
  33. data/lib/og/store/psql.rb +261 -244
  34. data/lib/og/store/sql.rb +873 -720
  35. data/lib/og/store/sqlite.rb +177 -175
  36. data/lib/og/store/sqlserver.rb +204 -214
  37. data/lib/og/types.rb +1 -1
  38. data/lib/og/validation.rb +57 -57
  39. data/lib/vendor/mysql.rb +376 -376
  40. data/lib/vendor/mysql411.rb +10 -10
  41. data/test/og/mixin/tc_hierarchical.rb +59 -59
  42. data/test/og/mixin/tc_optimistic_locking.rb +40 -40
  43. data/test/og/mixin/tc_orderable.rb +67 -67
  44. data/test/og/mixin/tc_timestamped.rb +19 -19
  45. data/test/og/store/tc_filesys.rb +46 -46
  46. data/test/og/tc_inheritance.rb +81 -81
  47. data/test/og/tc_join.rb +67 -0
  48. data/test/og/tc_polymorphic.rb +49 -49
  49. data/test/og/tc_relation.rb +57 -30
  50. data/test/og/tc_select.rb +49 -0
  51. data/test/og/tc_store.rb +345 -337
  52. data/test/og/tc_types.rb +11 -11
  53. metadata +11 -18
@@ -1,16 +1,16 @@
1
1
  # $Id$
2
2
 
3
3
  module Og
4
-
4
+
5
5
  # This exception is thrown when a low level error happens
6
6
  # in the store.
7
7
 
8
8
  class StoreException < Exception
9
- attr_accessor :original_exception, :info
9
+ attr_accessor :original_exception, :info
10
10
 
11
- def initialize(original_exception, info = nil)
12
- @original_exception, @info = original_exception, info
13
- end
11
+ def initialize(original_exception, info = nil)
12
+ @original_exception, @info = original_exception, info
13
+ end
14
14
  end
15
15
 
16
16
  end
@@ -3,197 +3,198 @@ require 'og/store'
3
3
 
4
4
  module Og
5
5
 
6
- # A Manager defines the relations between entities, ie Objects
6
+ # A Manager defines the relations between entities, ie objects
7
7
  # managed by Og.
8
8
 
9
9
  class Manager
10
10
 
11
- # Information about an Entity class.
12
-
13
- class EntityInfo
14
- attr_accessor :klass
15
- attr_accessor :enchanted
16
- attr_accessor :options
17
-
18
- def initialize(klass = nil)
19
- @klass = klass
20
- @enchanted = false
21
- @options = {}
22
- end
23
- end
24
-
25
- # The configuration options.
26
-
27
- attr_accessor :options
28
-
29
- # The store used for persistence. This is a virtual field
30
- # when running in thread_safe mode.
31
-
32
- attr_accessor :store
33
-
34
- # The collection of Entities managed by this manager.
35
-
36
- attr_accessor :entities
37
-
38
- def initialize(options)
39
- @options = options
40
- @entities = {}
41
- @pool = Glue::Pool.new
42
-
43
- store_class = Store.for_name(options[:store])
44
- store_class.destroy(options) if options[:destroy]
45
-
46
- if Og.thread_safe
47
- (options[:connection_count] || 5).times do
48
- @pool << store_class.new(options)
49
- end
50
- else
51
- @store = store_class.new(options)
52
- end
53
- end
54
-
55
- # Get a store from the pool.
56
-
57
- def get_store
58
- if Og.thread_safe
59
- thread = Thread.current
60
-
61
- unless conn = thread[:og_store]
62
- conn = @pool.pop()
63
- thread[:og_store] = conn
64
- end
65
-
66
- return conn
67
- else
68
- return @store
69
- end
70
- end
71
- alias_method :store, :get_store
72
-
73
- # Return a store to the pool.
74
-
75
- def put_store
76
- if Og.thread_safe
77
- thread = Thread.current
78
-
79
- if conn = thread[:og_store]
80
- thread[:og_store] = nil
81
- return @pool.push(conn)
82
- end
83
- end
84
- end
85
-
86
- # Resolves the inheritance for a class.
87
-
88
- def resolve_inheritance(klass)
89
- if has_super?(klass)
90
- sclass = klass.superclass
91
- klass.meta :superclass, sclass
92
- klass.meta :schema_inheritance
93
- sclass.meta :subclasses, klass
94
- end
95
- end
96
-
97
- # Resolve polymorphic relations.
98
-
99
- def resolve_polymorphic(klass)
100
- Relations.resolve_polymorphic(klass)
101
- end
102
-
103
- # Manage a class. Converts the class to an Entity.
104
-
105
- def manage(klass)
106
- return if managed?(klass) or klass.ancestors.include?(Unmanageable)
107
-
108
- info = EntityInfo.new(klass)
109
-
110
- # ensure that the superclass is managed before the
111
- # subclass.
112
-
113
- manage(klass.superclass) if has_super?(klass)
114
-
115
- klass.module_eval %{
116
- def ==(other)
117
- other ? @#{klass.primary_key.first} == other.#{klass.primary_key.first} : false
118
- end
119
- }
120
-
121
- klass.instance_variable_set '@ogmanager', self
122
- klass.class.send(:attr_accessor, :ogmanager)
123
-
124
- Relation.enchant(klass)
125
-
126
- # FIXME: uggly!
127
- store.enchant(klass, self); put_store
128
-
129
- # Call special class enchanting code.
130
-
131
- klass.enchant if klass.respond_to?(:enchant)
132
-
133
- @entities[klass] = info
134
- end
135
-
136
- # Is this class manageable by Og?
137
-
138
- def manageable?(klass)
139
- klass.respond_to?(:__props) and (!klass.__props.empty?)
140
- end
141
-
142
- # Is the class managed by Og?
143
-
144
- def managed?(klass)
145
- @entities.include?(klass)
146
- end
147
- alias_method :entity?, :managed?
148
-
149
- # Has this class a superclass?
150
-
151
- def has_super?(klass)
152
- manageable?(sclass = klass.superclass) and
153
- (klass.metadata.schema_inheritance || sclass.metadata.schema_inheritance)
154
- end
155
-
156
- # Use Ruby's advanced reflection features to find
157
- # all manageable classes. Managable are all classes that
158
- # define Properties.
159
-
160
- def manageable_classes
161
- classes = []
162
-
163
- ObjectSpace.each_object(Class) do |c|
164
- if manageable?(c)
165
- classes << c
166
- end
167
- end
168
-
169
- Logger.debug "Og manageable classes: #{classes.inspect}" if $DBG
170
-
171
- return classes
172
- end
173
-
174
- # Manage a collection of classes.
175
-
176
- def manage_classes(*classes)
177
- classes = manageable_classes.flatten if classes.empty?
178
- classes.each { |c| resolve_inheritance(c) }
179
- classes.each { |c| Relation.resolve(c, :resolve_target) }
180
- classes.each { |c| Relation.resolve(c, :resolve_polymorphic) }
181
- classes.each { |c| Relation.resolve(c, :resolve_options) }
182
- classes.each { |c| manage(c) }
183
- end
11
+ # Information about an Entity class.
12
+
13
+ class EntityInfo
14
+ attr_accessor :klass
15
+ attr_accessor :enchanted
16
+ attr_accessor :options
17
+
18
+ def initialize(klass = nil)
19
+ @klass = klass
20
+ @enchanted = false
21
+ @options = {}
22
+ end
23
+ end
24
+
25
+ # The configuration options.
26
+
27
+ attr_accessor :options
28
+
29
+ # The store used for persistence. This is a virtual field
30
+ # when running in thread_safe mode.
31
+
32
+ attr_accessor :store
33
+
34
+ # The collection of Entities managed by this manager.
35
+
36
+ attr_accessor :entities
37
+
38
+ def initialize(options)
39
+ @options = options
40
+ @entities = {}
41
+ @pool = Glue::Pool.new
42
+
43
+ store_class = Store.for_name(options[:store])
44
+ store_class.destroy(options) if options[:destroy]
45
+
46
+ if Og.thread_safe
47
+ (options[:connection_count] || 5).times do
48
+ @pool << store_class.new(options)
49
+ end
50
+ else
51
+ @store = store_class.new(options)
52
+ end
53
+ end
54
+
55
+ # Get a store from the pool.
56
+
57
+ def get_store
58
+ if Og.thread_safe
59
+ thread = Thread.current
60
+
61
+ unless conn = thread[:og_store]
62
+ conn = @pool.pop()
63
+ thread[:og_store] = conn
64
+ end
65
+
66
+ return conn
67
+ else
68
+ return @store
69
+ end
70
+ end
71
+ alias_method :store, :get_store
72
+
73
+ # Return a store to the pool.
74
+
75
+ def put_store
76
+ if Og.thread_safe
77
+ thread = Thread.current
78
+
79
+ if conn = thread[:og_store]
80
+ thread[:og_store] = nil
81
+ return @pool.push(conn)
82
+ end
83
+ end
84
+ end
85
+
86
+ # Resolves the inheritance for a class.
87
+
88
+ def resolve_inheritance(klass)
89
+ if has_super?(klass)
90
+ sclass = klass.superclass
91
+ klass.meta :superclass, sclass
92
+ klass.meta :schema_inheritance
93
+ sclass.meta :subclasses, klass
94
+ end
95
+ end
96
+
97
+ # Resolve polymorphic relations.
98
+
99
+ def resolve_polymorphic(klass)
100
+ Relations.resolve_polymorphic(klass)
101
+ end
102
+
103
+ # Manage a class. Converts the class to an Entity.
104
+
105
+ def manage(klass)
106
+ return if managed?(klass) or klass.ancestors.include?(Unmanageable)
107
+
108
+ info = EntityInfo.new(klass)
109
+
110
+ # ensure that the superclass is managed before the
111
+ # subclass.
112
+
113
+ manage(klass.superclass) if has_super?(klass)
114
+
115
+ klass.module_eval %{
116
+ def ==(other)
117
+ other ? @#{klass.primary_key.first} == other.#{klass.primary_key.first} : false
118
+ end
119
+ }
120
+
121
+ klass.instance_variable_set '@ogmanager', self
122
+ klass.class.send(:attr_accessor, :ogmanager)
123
+
124
+ Relation.enchant(klass)
125
+
126
+ # FIXME: uggly!
127
+ store.enchant(klass, self); put_store
128
+
129
+ # Call special class enchanting code.
130
+
131
+ klass.enchant if klass.respond_to?(:enchant)
132
+
133
+ @entities[klass] = info
134
+ end
135
+
136
+ # Is this class manageable by Og?
137
+
138
+ def manageable?(klass)
139
+ klass.respond_to?(:__props) and (!klass.__props.empty?)
140
+ end
141
+
142
+ # Is the class managed by Og?
143
+
144
+ def managed?(klass)
145
+ @entities.include?(klass)
146
+ end
147
+ alias_method :entity?, :managed?
148
+
149
+ # Has this class a superclass?
150
+
151
+ def has_super?(klass)
152
+ manageable?(sclass = klass.superclass) and
153
+ (klass.metadata.schema_inheritance || sclass.metadata.schema_inheritance)
154
+ end
155
+
156
+ # Use Ruby's advanced reflection features to find
157
+ # all manageable classes. Managable are all classes that
158
+ # define Properties.
159
+
160
+ def manageable_classes
161
+ classes = []
162
+
163
+ ObjectSpace.each_object(Class) do |c|
164
+ if manageable?(c)
165
+ classes << c
166
+ end
167
+ end
168
+
169
+ Logger.debug "Og manageable classes: #{classes.inspect}" if $DBG
170
+
171
+ return classes
172
+ end
173
+
174
+ # Manage a collection of classes.
175
+
176
+ def manage_classes(*classes)
177
+ classes = manageable_classes.flatten if classes.empty?
178
+ classes.each { |c| resolve_inheritance(c) }
179
+ classes.each { |c| Relation.resolve(c, :resolve_target) }
180
+ classes.each { |c| Relation.resolve(c, :resolve_polymorphic) }
181
+ classes.each { |c| Relation.resolve(c, :resolve_options) }
182
+ classes.each { |c| manage(c) }
183
+ end
184
184
 
185
185
  end
186
186
 
187
187
  class << self
188
-
188
+
189
189
  # Helper method, useful to initialize Og.
190
190
 
191
191
  def setup(options = {})
192
- m = @@manager = Manager.new(options)
193
- m.manage_classes
194
- return m
192
+ m = @@manager = Manager.new(options)
193
+ m.manage_classes
194
+ return m
195
195
  end
196
196
  alias_method :connect, :setup
197
+ alias_method :options=, :setup
197
198
 
198
199
  end
199
200
 
@@ -14,103 +14,103 @@ module Og
14
14
 
15
15
  module NestedSets
16
16
 
17
- def self.append_dynamic_features(base, options)
18
- c = {
19
- :left => 'lft',
20
- :right => 'rgt',
21
- :type => Fixnum,
22
- :parent => base.to_s.demodulize.underscore.downcase,
23
- :children => base.to_s.demodulize.underscore.downcase.plural
24
- }
25
- c.update(options) if options
26
-
27
- parent = "#{c[:parent]}_oid"
28
- left = c[:left]
29
- right = c[:right]
30
- children = c[:children]
31
- child = children.singular
32
-
33
- if c[:scope].is_a?(Symbol) && c[:scope].to_s !~ /_oid$/
34
- c[:scope] = "#{c[:scope]}_oid".intern
35
- end
36
-
37
- scope = c[:scope]
38
-
39
- if scope
40
- if scope.is_a?(Symbol)
41
- scope = %{(#{scope} ? "#{scope} = \#{@#{scope}}" : "#{scope} IS NULL")}
42
- end
43
-
44
- cond = 'condition => ' + scope
45
- cond_and = ':condition => ' + scope + ' + " AND " +'
46
- else
47
- cond = ':condition => nil'
48
- cond_and = ':condition => '
49
- end
50
-
51
- base.module_eval <<-EOE, __FILE__, __LINE__
52
- property :#{parent}, Fixnum, :sql_index => true
53
- property :#{left}, :#{right}, #{c[:type]}
54
-
55
- def root?
56
- (@#{parent}.nil? || @#{parent} == 0) && (@#{left} == 1) && (@#{right} > @#{left})
57
- end
58
-
59
- def child?
60
- (@#{parent} && @#{parent} != 0) && (@#{left} > 1) && (@#{right} > @#{left})
61
- end
62
-
63
- def parent
64
- if root?
65
- nil
66
- else
67
- #{base}[@#{parent}]
68
- end
69
- end
17
+ def self.append_dynamic_features(base, options)
18
+ c = {
19
+ :left => 'lft',
20
+ :right => 'rgt',
21
+ :type => Fixnum,
22
+ :parent => base.to_s.demodulize.underscore.downcase,
23
+ :children => base.to_s.demodulize.underscore.downcase.plural
24
+ }
25
+ c.update(options) if options
26
+
27
+ parent = "#{c[:parent]}_oid"
28
+ left = c[:left]
29
+ right = c[:right]
30
+ children = c[:children]
31
+ child = children.singular
32
+
33
+ if c[:scope].is_a?(Symbol) && c[:scope].to_s !~ /_oid$/
34
+ c[:scope] = "#{c[:scope]}_oid".intern
35
+ end
36
+
37
+ scope = c[:scope]
38
+
39
+ if scope
40
+ if scope.is_a?(Symbol)
41
+ scope = %{(#{scope} ? "#{scope} = \#{@#{scope}}" : "#{scope} IS NULL")}
42
+ end
43
+
44
+ cond = 'condition => ' + scope
45
+ cond_and = ':condition => ' + scope + ' + " AND " +'
46
+ else
47
+ cond = ':condition => nil'
48
+ cond_and = ':condition => '
49
+ end
50
+
51
+ base.module_eval <<-EOE, __FILE__, __LINE__
52
+ property :#{parent}, Fixnum, :sql_index => true
53
+ property :#{left}, :#{right}, #{c[:type]}
54
+
55
+ def root?
56
+ (@#{parent}.nil? || @#{parent} == 0) && (@#{left} == 1) && (@#{right} > @#{left})
57
+ end
58
+
59
+ def child?
60
+ (@#{parent} && @#{parent} != 0) && (@#{left} > 1) && (@#{right} > @#{left})
61
+ end
62
+
63
+ def parent
64
+ if root?
65
+ nil
66
+ else
67
+ #{base}[@#{parent}]
68
+ end
69
+ end
70
70
 
71
- def #{children}_count
72
- return (@#{right} - @#{left} - 1)/2
73
- end
74
-
75
- def full_#{children}(options = {})
76
- options.update(#{cond_and}"(#{left} BETWEEN \#\{@#{left}\} AND \#{@#{right}})")
77
- #{base}.all(options)
78
- end
79
-
80
- def #{children}(options = {})
81
- options.update(#{cond_and}"(#{left} > \#\{@#{left}\}) AND (#{right} < \#{@#{right}})")
82
- #{base}.all(options)
83
- end
84
-
85
- def direct_#{children}(options = {})
86
- options.update(#{cond_and}"#{parent} = \#{pk}")
87
- #{base}.all(options)
88
- end
89
-
90
- def add_#{child}(child)
91
- self.reload if pk
92
- child.reload if child.pk
93
-
94
- if @#{left}.nil? || @#{left} == 0 || @#{right}.nil? || @#{right} == 0
95
- @#{left} = 1
96
- @#{right} = 2
97
- end
98
-
99
- child.#{parent} = pk
100
- child.#{left} = pivot = @#{right}
101
- child.#{right} = pivot + 1
102
- @#{right} = pivot + 2
103
-
104
- #{base}.transaction do
105
- #{base}.update_property("#{left} = (#{left} + 2)", #{cond_and}"#{left} >= \#{pivot}")
106
- #{base}.update_property("#{right} = (#{right} + 2)", #{cond_and}"#{right} >= \#{pivot}")
107
- end
108
-
109
- self.save
110
- child.save
111
- end
112
- EOE
113
- end
71
+ def #{children}_count
72
+ return (@#{right} - @#{left} - 1)/2
73
+ end
74
+
75
+ def full_#{children}(options = {})
76
+ options.update(#{cond_and}"(#{left} BETWEEN \#\{@#{left}\} AND \#{@#{right}})")
77
+ #{base}.all(options)
78
+ end
79
+
80
+ def #{children}(options = {})
81
+ options.update(#{cond_and}"(#{left} > \#\{@#{left}\}) AND (#{right} < \#{@#{right}})")
82
+ #{base}.all(options)
83
+ end
84
+
85
+ def direct_#{children}(options = {})
86
+ options.update(#{cond_and}"#{parent} = \#{pk}")
87
+ #{base}.all(options)
88
+ end
89
+
90
+ def add_#{child}(child)
91
+ self.reload if pk
92
+ child.reload if child.pk
93
+
94
+ if @#{left}.nil? || @#{left} == 0 || @#{right}.nil? || @#{right} == 0
95
+ @#{left} = 1
96
+ @#{right} = 2
97
+ end
98
+
99
+ child.#{parent} = pk
100
+ child.#{left} = pivot = @#{right}
101
+ child.#{right} = pivot + 1
102
+ @#{right} = pivot + 2
103
+
104
+ #{base}.transaction do
105
+ #{base}.update_property("#{left} = (#{left} + 2)", #{cond_and}"#{left} >= \#{pivot}")
106
+ #{base}.update_property("#{right} = (#{right} + 2)", #{cond_and}"#{right} >= \#{pivot}")
107
+ end
108
+
109
+ self.save
110
+ child.save
111
+ end
112
+ EOE
113
+ end
114
114
 
115
115
  end
116
116
 
@@ -121,24 +121,24 @@ end
121
121
  # === Example
122
122
  #
123
123
  # class Comment
124
- # include Hierarchical, :method => :nested_sets
124
+ # include Hierarchical, :method => :nested_sets
125
125
  # end
126
126
  #
127
127
  # [+:method+]
128
- # :simple
129
- # :nested_sets
130
- # :nested_intervals
128
+ # :simple
129
+ # :nested_sets
130
+ # :nested_intervals
131
131
 
132
132
  module Hierarchical
133
133
 
134
- def self.append_dynamic_features(base, options)
135
- o = {
136
- :method => :nested_sets,
137
- }
138
- o.update(options) if options
134
+ def self.append_dynamic_features(base, options)
135
+ o = {
136
+ :method => :nested_sets,
137
+ }
138
+ o.update(options) if options
139
139
 
140
- base.include(NestedSets, o)
141
- end
140
+ base.include(NestedSets, o)
141
+ end
142
142
 
143
143
  end
144
144