og 0.20.0 → 0.21.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 (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