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.
- data/CHANGELOG +796 -664
- data/INSTALL +24 -24
- data/README +39 -32
- data/Rakefile +41 -42
- data/benchmark/bench.rb +36 -36
- data/doc/AUTHORS +15 -12
- data/doc/LICENSE +3 -3
- data/doc/RELEASES +311 -243
- data/doc/config.txt +1 -1
- data/examples/mysql_to_psql.rb +15 -15
- data/examples/run.rb +92 -92
- data/install.rb +7 -17
- data/lib/og.rb +76 -75
- data/lib/og/collection.rb +203 -160
- data/lib/og/entity.rb +168 -169
- data/lib/og/errors.rb +5 -5
- data/lib/og/manager.rb +179 -178
- data/lib/og/mixin/hierarchical.rb +107 -107
- data/lib/og/mixin/optimistic_locking.rb +36 -36
- data/lib/og/mixin/orderable.rb +148 -148
- data/lib/og/mixin/timestamped.rb +8 -8
- data/lib/og/mixin/tree.rb +124 -124
- data/lib/og/relation.rb +237 -213
- data/lib/og/relation/belongs_to.rb +5 -5
- data/lib/og/relation/has_many.rb +60 -58
- data/lib/og/relation/joins_many.rb +93 -47
- data/lib/og/relation/refers_to.rb +25 -21
- data/lib/og/store.rb +210 -207
- data/lib/og/store/filesys.rb +79 -79
- data/lib/og/store/kirby.rb +263 -258
- data/lib/og/store/memory.rb +261 -261
- data/lib/og/store/mysql.rb +288 -284
- data/lib/og/store/psql.rb +261 -244
- data/lib/og/store/sql.rb +873 -720
- data/lib/og/store/sqlite.rb +177 -175
- data/lib/og/store/sqlserver.rb +204 -214
- data/lib/og/types.rb +1 -1
- data/lib/og/validation.rb +57 -57
- data/lib/vendor/mysql.rb +376 -376
- data/lib/vendor/mysql411.rb +10 -10
- data/test/og/mixin/tc_hierarchical.rb +59 -59
- data/test/og/mixin/tc_optimistic_locking.rb +40 -40
- data/test/og/mixin/tc_orderable.rb +67 -67
- data/test/og/mixin/tc_timestamped.rb +19 -19
- data/test/og/store/tc_filesys.rb +46 -46
- data/test/og/tc_inheritance.rb +81 -81
- data/test/og/tc_join.rb +67 -0
- data/test/og/tc_polymorphic.rb +49 -49
- data/test/og/tc_relation.rb +57 -30
- data/test/og/tc_select.rb +49 -0
- data/test/og/tc_store.rb +345 -337
- data/test/og/tc_types.rb +11 -11
- metadata +11 -18
data/lib/og/errors.rb
CHANGED
@@ -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
|
-
|
9
|
+
attr_accessor :original_exception, :info
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
def initialize(original_exception, info = nil)
|
12
|
+
@original_exception, @info = original_exception, info
|
13
|
+
end
|
14
14
|
end
|
15
15
|
|
16
16
|
end
|
data/lib/og/manager.rb
CHANGED
@@ -3,197 +3,198 @@ require 'og/store'
|
|
3
3
|
|
4
4
|
module Og
|
5
5
|
|
6
|
-
# A Manager defines the relations between entities, ie
|
6
|
+
# A Manager defines the relations between entities, ie objects
|
7
7
|
# managed by Og.
|
8
8
|
|
9
9
|
class Manager
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
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
|
-
|
193
|
-
|
194
|
-
|
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
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
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
|
-
#
|
124
|
+
# include Hierarchical, :method => :nested_sets
|
125
125
|
# end
|
126
126
|
#
|
127
127
|
# [+:method+]
|
128
|
-
#
|
129
|
-
#
|
130
|
-
#
|
128
|
+
# :simple
|
129
|
+
# :nested_sets
|
130
|
+
# :nested_intervals
|
131
131
|
|
132
132
|
module Hierarchical
|
133
133
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
134
|
+
def self.append_dynamic_features(base, options)
|
135
|
+
o = {
|
136
|
+
:method => :nested_sets,
|
137
|
+
}
|
138
|
+
o.update(options) if options
|
139
139
|
|
140
|
-
|
141
|
-
|
140
|
+
base.include(NestedSets, o)
|
141
|
+
end
|
142
142
|
|
143
143
|
end
|
144
144
|
|