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
@@ -8,21 +8,21 @@ module Og
8
8
 
9
9
  module MemoryUtils
10
10
 
11
- # FIXME: find a neutral name.
12
-
13
- def table(klass)
14
- klass.to_s
15
- end
11
+ # FIXME: find a neutral name.
12
+
13
+ def table(klass)
14
+ klass.to_s
15
+ end
16
16
 
17
- # FIXME: find a neutral name.
17
+ # FIXME: find a neutral name.
18
18
 
19
- def join_table(class1, class2, postfix = nil)
20
- "#{class1}#{class2}"
21
- end
19
+ def join_table(class1, class2, postfix = nil)
20
+ "#{class1}#{class2}"
21
+ end
22
22
 
23
- def quote(val)
24
- val.inspect
25
- end
23
+ def quote(val)
24
+ val.inspect
25
+ end
26
26
 
27
27
  end
28
28
 
@@ -33,258 +33,258 @@ end
33
33
  #++
34
34
 
35
35
  class MemoryStore < Store
36
- extend MemoryUtils; include MemoryUtils
37
-
38
- class ObjectHash < Hash
39
- def [](key)
40
- super(key.to_s)
41
- end
42
-
43
- def []=(key, value)
44
- super(key.to_s, value)
45
- end
46
- end
47
-
48
- # This hash implements an in-memory database.
49
-
50
- @objects = ObjectHash.new
51
-
52
- def self.objects
53
- @objects
54
- end
55
-
56
- def self.objects=(val)
57
- @objects = val
58
- end
59
-
60
- # A pseudo-connection to the actual object store.
61
-
62
- attr_accessor :conn
63
-
64
- def self.destroy(options)
65
- FileUtils.rm_rf("#{options[:name]}.db")
66
- rescue
67
- Logger.info "Cannot destroy '#{name}'"
68
- end
69
-
70
- def initialize(options)
71
- super
72
- begin
73
- @conn = self.class.objects = YAML.load_file("#{@options[:name]}.db")
74
- rescue
75
- @conn = self.class.objects = ObjectHash.new
76
- end
77
- end
78
-
79
- def close
80
- File.open("#{@options[:name]}.db", 'w') { |f| f << @conn.to_yaml }
81
- end
82
-
83
- # Enchants a class.
84
-
85
- def enchant(klass, manager)
86
- klass.property :oid, Fixnum
87
-
88
- super
89
-
90
- @conn[klass] ||= {}
91
-
92
- eval_og_insert(klass)
93
- eval_og_update(klass)
94
- eval_og_read(klass)
95
- eval_og_delete(klass)
96
- end
97
-
98
- # :section: Lifecycle methods.
99
-
100
- # Loads an object from the store using the primary key.
101
-
102
- def load(pk, klass)
103
- @conn[klass][pk]
104
- end
105
-
106
- # Update selected properties of an object or class of
107
- # objects.
108
-
109
- def update_properties(target, set, options = nil)
110
- set = set.gsub(/,/, ';')
111
- if target.is_a?(Class)
112
- if options
113
- condition = options[:condition] || options[:where]
114
- else
115
- condition = 'true'
116
- end
117
- for obj in @conn[target].values
118
- obj.instance_eval %{
119
- if #{condition.gsub(/=/, '==')}
120
- #{set}
121
- end
122
- }
123
- end
124
- else
125
- target.instance_eval(set)
126
- end
127
- end
128
- alias_method :pupdate, :update_properties
129
- alias_method :update_property, :update_properties
130
-
131
- # Find a collection of objects.
132
- #
133
- # === Examples
134
- #
135
- # User.find(:condition => 'age > 15', :order => 'score ASC', :offet => 10, :limit =>10)
136
- # Comment.find(:include => :entry)
137
- # store.find(:class => User, :where => 'age > 15')
138
-
139
- def find(options)
140
- query(options)
141
- end
142
-
143
- # Find one object.
144
-
145
- def find_one(options)
146
- query(options).first
147
- end
148
-
149
- # Reloads an object from the store.
150
-
151
- def reload(obj, pk)
152
- # Nop, the memory store is always synchronized.
153
- end
154
-
155
- # Count results.
156
-
157
- def count(options)
158
- objects = 0
159
-
160
- if condition = options[:condition] || options[:where]
161
- condition = "obj." + condition.gsub(/=/, '==')
162
- else
163
- condition = true
164
- end
165
-
166
- eval %{
167
- for obj in @conn[options[:class]].values
168
- objects += 1 if #{condition}
169
- end
170
- }
171
-
172
- return objects
173
- end
174
-
175
- # Relate two objects through an intermediate join table.
176
- # Typically used in joins_many and many_to_many relations.
177
-
178
- def join(obj1, obj2, table)
179
- # nop
180
- end
181
-
182
- # Query.
183
-
184
- def query(options)
185
- objects = []
186
-
187
- if condition = options[:condition] || options[:where]
188
- condition = "obj." + condition.gsub(/=/, '==')
189
- else
190
- condition = true
191
- end
192
-
193
- eval %{
194
- for obj in @conn[options[:class]].values
195
- objects << obj if #{condition}
196
- end
197
- }
198
-
199
- if order = options[:order]
200
- desc = (order =~ /DESC/)
201
- order = order.gsub(/DESC/, '').gsub(/ASC/, '')
202
- eval "objects.sort { |x, y| x.#{order} <=> y.#{order} }"
203
- objects.reverse! if desc
204
- end
205
-
206
- return objects
207
- end
208
-
209
- # :section: Transaction methods.
210
-
211
- # Start a new transaction.
212
-
213
- def start
214
- end
215
-
216
- # Commit a transaction.
217
-
218
- def commit
219
- end
220
-
221
- # Rollback a transaction.
222
-
223
- def rollback
224
- end
36
+ extend MemoryUtils; include MemoryUtils
37
+
38
+ class ObjectHash < Hash
39
+ def [](key)
40
+ super(key.to_s)
41
+ end
42
+
43
+ def []=(key, value)
44
+ super(key.to_s, value)
45
+ end
46
+ end
47
+
48
+ # This hash implements an in-memory database.
49
+
50
+ @objects = ObjectHash.new
51
+
52
+ def self.objects
53
+ @objects
54
+ end
55
+
56
+ def self.objects=(val)
57
+ @objects = val
58
+ end
59
+
60
+ # A pseudo-connection to the actual object store.
61
+
62
+ attr_accessor :conn
63
+
64
+ def self.destroy(options)
65
+ FileUtils.rm_rf("#{options[:name]}.db")
66
+ rescue
67
+ Logger.info "Cannot destroy '#{name}'"
68
+ end
69
+
70
+ def initialize(options)
71
+ super
72
+ begin
73
+ @conn = self.class.objects = YAML.load_file("#{@options[:name]}.db")
74
+ rescue
75
+ @conn = self.class.objects = ObjectHash.new
76
+ end
77
+ end
78
+
79
+ def close
80
+ File.open("#{@options[:name]}.db", 'w') { |f| f << @conn.to_yaml }
81
+ end
82
+
83
+ # Enchants a class.
84
+
85
+ def enchant(klass, manager)
86
+ klass.property :oid, Fixnum
87
+
88
+ super
89
+
90
+ @conn[klass] ||= {}
91
+
92
+ eval_og_insert(klass)
93
+ eval_og_update(klass)
94
+ eval_og_read(klass)
95
+ eval_og_delete(klass)
96
+ end
97
+
98
+ # :section: Lifecycle methods.
99
+
100
+ # Loads an object from the store using the primary key.
101
+
102
+ def load(pk, klass)
103
+ @conn[klass][pk]
104
+ end
105
+
106
+ # Update selected properties of an object or class of
107
+ # objects.
108
+
109
+ def update_properties(target, set, options = nil)
110
+ set = set.gsub(/,/, ';')
111
+ if target.is_a?(Class)
112
+ if options
113
+ condition = options[:condition] || options[:where]
114
+ else
115
+ condition = 'true'
116
+ end
117
+ for obj in @conn[target].values
118
+ obj.instance_eval %{
119
+ if #{condition.gsub(/=/, '==')}
120
+ #{set}
121
+ end
122
+ }
123
+ end
124
+ else
125
+ target.instance_eval(set)
126
+ end
127
+ end
128
+ alias_method :pupdate, :update_properties
129
+ alias_method :update_property, :update_properties
130
+
131
+ # Find a collection of objects.
132
+ #
133
+ # === Examples
134
+ #
135
+ # User.find(:condition => 'age > 15', :order => 'score ASC', :offet => 10, :limit =>10)
136
+ # Comment.find(:include => :entry)
137
+ # store.find(:class => User, :where => 'age > 15')
138
+
139
+ def find(options)
140
+ query(options)
141
+ end
142
+
143
+ # Find one object.
144
+
145
+ def find_one(options)
146
+ query(options).first
147
+ end
148
+
149
+ # Reloads an object from the store.
150
+
151
+ def reload(obj, pk)
152
+ # Nop, the memory store is always synchronized.
153
+ end
154
+
155
+ # Count results.
156
+
157
+ def count(options)
158
+ objects = 0
159
+
160
+ if condition = options[:condition] || options[:where]
161
+ condition = "obj." + condition.gsub(/=/, '==')
162
+ else
163
+ condition = true
164
+ end
165
+
166
+ eval %{
167
+ for obj in @conn[options[:class]].values
168
+ objects += 1 if #{condition}
169
+ end
170
+ }
171
+
172
+ return objects
173
+ end
174
+
175
+ # Relate two objects through an intermediate join table.
176
+ # Typically used in joins_many and many_to_many relations.
177
+
178
+ def join(obj1, obj2, table)
179
+ # nop
180
+ end
181
+
182
+ # Query.
183
+
184
+ def query(options)
185
+ objects = []
186
+
187
+ if condition = options[:condition] || options[:where]
188
+ condition = "obj." + condition.gsub(/=/, '==')
189
+ else
190
+ condition = true
191
+ end
192
+
193
+ eval %{
194
+ for obj in @conn[options[:class]].values
195
+ objects << obj if #{condition}
196
+ end
197
+ }
198
+
199
+ if order = options[:order]
200
+ desc = (order =~ /DESC/)
201
+ order = order.gsub(/DESC/, '').gsub(/ASC/, '')
202
+ eval "objects.sort { |x, y| x.#{order} <=> y.#{order} }"
203
+ objects.reverse! if desc
204
+ end
205
+
206
+ return objects
207
+ end
208
+
209
+ # :section: Transaction methods.
210
+
211
+ # Start a new transaction.
212
+
213
+ def start
214
+ end
215
+
216
+ # Commit a transaction.
217
+
218
+ def commit
219
+ end
220
+
221
+ # Rollback a transaction.
222
+
223
+ def rollback
224
+ end
225
225
 
226
226
  private
227
227
 
228
- # :section: Lifecycle method compilers.
229
-
230
- # Compile the og_update method for the class.
231
-
232
- def eval_og_insert(klass)
233
- pk = klass.primary_key.first
234
-
235
- klass.class_eval %{
236
- def og_insert(store)
237
- #{Aspects.gen_advice_code(:og_insert, klass.advices, :pre) if klass.respond_to?(:advices)}
238
- @#{pk} = store.conn[#{klass}].size + 1
239
- store.conn[#{klass}][@#{pk}] = self
240
- #{Aspects.gen_advice_code(:og_insert, klass.advices, :post) if klass.respond_to?(:advices)}
241
- end
242
- }
243
- end
244
-
245
- # Compile the og_update method for the class.
246
-
247
- def eval_og_update(klass)
248
- pk = klass.primary_key.first
249
-
250
- klass.class_eval %{
251
- def og_update(store)
252
- #{Aspects.gen_advice_code(:og_update, klass.advices, :pre) if klass.respond_to?(:advices)}
253
- store.conn[#{klass}][@#{pk}] = self
254
- #{Aspects.gen_advice_code(:og_update, klass.advices, :post) if klass.respond_to?(:advices)}
255
- end
256
- }
257
- end
258
-
259
- # Not really useful in this store, kept for compatibility,
260
- # just to call the aspects.
261
-
262
- def eval_og_read(klass)
263
- klass.class_eval %{
264
- def og_read
265
- #{Aspects.gen_advice_code(:og_read, klass.advices, :pre) if klass.respond_to?(:advices)}
266
- #{Aspects.gen_advice_code(:og_read, klass.advices, :post) if klass.respond_to?(:advices)}
267
- end
268
- }
269
- end
270
-
271
- def eval_og_delete(klass)
272
- klass.module_eval %{
273
- def og_delete(store, pk, cascade = true)
274
- #{Aspects.gen_advice_code(:og_delete, klass.advices, :pre) if klass.respond_to?(:advices)}
275
- pk ||= @#{klass.primary_key.first}
276
- transaction do |tx|
277
- tx.conn[#{klass}].delete(pk)
278
- if cascade and #{klass}.__meta[:descendants]
279
- #{klass}.__meta[:descendants].each do |dclass, foreign_key|
280
- eval "tx.conn[dclass].delete_if { |dobj| dobj.\#{foreign_key} == \#{pk} }"
281
- end
282
- end
283
- end
284
- #{Aspects.gen_advice_code(:og_delete, klass.advices, :post) if klass.respond_to?(:advices)}
285
- end
286
- }
287
- end
228
+ # :section: Lifecycle method compilers.
229
+
230
+ # Compile the og_update method for the class.
231
+
232
+ def eval_og_insert(klass)
233
+ pk = klass.primary_key.first
234
+
235
+ klass.class_eval %{
236
+ def og_insert(store)
237
+ #{Aspects.gen_advice_code(:og_insert, klass.advices, :pre) if klass.respond_to?(:advices)}
238
+ @#{pk} = store.conn[#{klass}].size + 1
239
+ store.conn[#{klass}][@#{pk}] = self
240
+ #{Aspects.gen_advice_code(:og_insert, klass.advices, :post) if klass.respond_to?(:advices)}
241
+ end
242
+ }
243
+ end
244
+
245
+ # Compile the og_update method for the class.
246
+
247
+ def eval_og_update(klass)
248
+ pk = klass.primary_key.first
249
+
250
+ klass.class_eval %{
251
+ def og_update(store, options)
252
+ #{Aspects.gen_advice_code(:og_update, klass.advices, :pre) if klass.respond_to?(:advices)}
253
+ store.conn[#{klass}][@#{pk}] = self
254
+ #{Aspects.gen_advice_code(:og_update, klass.advices, :post) if klass.respond_to?(:advices)}
255
+ end
256
+ }
257
+ end
258
+
259
+ # Not really useful in this store, kept for compatibility,
260
+ # just to call the aspects.
261
+
262
+ def eval_og_read(klass)
263
+ klass.class_eval %{
264
+ def og_read
265
+ #{Aspects.gen_advice_code(:og_read, klass.advices, :pre) if klass.respond_to?(:advices)}
266
+ #{Aspects.gen_advice_code(:og_read, klass.advices, :post) if klass.respond_to?(:advices)}
267
+ end
268
+ }
269
+ end
270
+
271
+ def eval_og_delete(klass)
272
+ klass.module_eval %{
273
+ def og_delete(store, pk, cascade = true)
274
+ #{Aspects.gen_advice_code(:og_delete, klass.advices, :pre) if klass.respond_to?(:advices)}
275
+ pk ||= @#{klass.primary_key.first}
276
+ transaction do |tx|
277
+ tx.conn[#{klass}].delete(pk)
278
+ if cascade and #{klass}.__meta[:descendants]
279
+ #{klass}.__meta[:descendants].each do |dclass, foreign_key|
280
+ eval "tx.conn[dclass].delete_if { |dobj| dobj.\#{foreign_key} == \#{pk} }"
281
+ end
282
+ end
283
+ end
284
+ #{Aspects.gen_advice_code(:og_delete, klass.advices, :post) if klass.respond_to?(:advices)}
285
+ end
286
+ }
287
+ end
288
288
 
289
289
  end
290
290