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
@@ -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