og 0.23.0 → 0.24.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/ProjectInfo +58 -0
  2. data/README +5 -4
  3. data/Rakefile +2 -2
  4. data/doc/AUTHORS +10 -7
  5. data/doc/RELEASES +108 -0
  6. data/lib/og.rb +1 -3
  7. data/lib/og/collection.rb +4 -4
  8. data/lib/og/entity.rb +96 -27
  9. data/lib/og/evolution.rb +78 -0
  10. data/lib/og/manager.rb +29 -32
  11. data/lib/og/mixin/hierarchical.rb +1 -1
  12. data/lib/og/mixin/optimistic_locking.rb +5 -8
  13. data/lib/og/mixin/orderable.rb +15 -2
  14. data/lib/og/mixin/schema_inheritance_base.rb +12 -0
  15. data/lib/og/mixin/taggable.rb +29 -25
  16. data/lib/og/mixin/timestamped.rb +4 -2
  17. data/lib/og/mixin/tree.rb +0 -1
  18. data/lib/og/relation.rb +161 -116
  19. data/lib/og/relation/all.rb +6 -0
  20. data/lib/og/relation/belongs_to.rb +4 -1
  21. data/lib/og/relation/has_many.rb +6 -5
  22. data/lib/og/relation/joins_many.rb +13 -12
  23. data/lib/og/relation/refers_to.rb +3 -3
  24. data/lib/og/store.rb +9 -9
  25. data/lib/og/store/{filesys.rb → alpha/filesys.rb} +0 -0
  26. data/lib/og/store/alpha/kirby.rb +284 -0
  27. data/lib/og/store/{memory.rb → alpha/memory.rb} +2 -0
  28. data/lib/og/store/{sqlserver.rb → alpha/sqlserver.rb} +6 -6
  29. data/lib/og/store/kirby.rb +145 -162
  30. data/lib/og/store/mysql.rb +58 -27
  31. data/lib/og/store/psql.rb +15 -13
  32. data/lib/og/store/sql.rb +136 -135
  33. data/lib/og/store/sqlite.rb +13 -12
  34. data/lib/og/validation.rb +2 -2
  35. data/lib/vendor/kbserver.rb +20 -0
  36. data/lib/vendor/kirbybase.rb +2790 -1601
  37. data/test/og/CONFIG.rb +79 -0
  38. data/test/og/mixin/tc_hierarchical.rb +1 -1
  39. data/test/og/mixin/tc_optimistic_locking.rb +1 -3
  40. data/test/og/mixin/tc_orderable.rb +42 -1
  41. data/test/og/mixin/tc_taggable.rb +1 -1
  42. data/test/og/mixin/tc_timestamped.rb +1 -1
  43. data/test/og/store/tc_filesys.rb +1 -2
  44. data/test/og/tc_delete_all.rb +45 -0
  45. data/test/og/tc_inheritance.rb +10 -38
  46. data/test/og/tc_join.rb +2 -11
  47. data/test/og/tc_multiple.rb +3 -16
  48. data/test/og/tc_override.rb +3 -3
  49. data/test/og/tc_polymorphic.rb +3 -13
  50. data/test/og/tc_relation.rb +8 -6
  51. data/test/og/tc_reverse.rb +2 -11
  52. data/test/og/tc_select.rb +2 -15
  53. data/test/og/tc_store.rb +4 -63
  54. data/test/og/tc_types.rb +1 -2
  55. metadata +80 -77
@@ -2,7 +2,7 @@ begin
2
2
  require 'mysql'
3
3
  rescue Object => ex
4
4
  Logger.error 'Ruby-Mysql bindings are not installed!'
5
- Logger.error 'Trying to uses the pure-Ruby binding included in Og'
5
+ Logger.error 'Trying to use the pure-Ruby binding included in Og'
6
6
  begin
7
7
  # Attempt to use the included pure ruby version.
8
8
  require 'vendor/mysql'
@@ -87,8 +87,19 @@ module MysqlUtils
87
87
  end
88
88
 
89
89
  # A Store that persists objects into a MySQL database.
90
- # To read documentation about the methods, consult the documentation
91
- # for SqlStore and Store.
90
+ # To read documentation about the methods, consult
91
+ # the documentation for SqlStore and Store.
92
+ #
93
+ # Here is some useful code to initialize your MySQL
94
+ # RDBMS for development. You probably want to be
95
+ # more careful with provileges on your production
96
+ # environment.
97
+ #
98
+ # mysql> GRANT ALL PRIVELEGES
99
+ # ON keystone.*
100
+ # TO <$sys_dbuser name>@localhost
101
+ # IDENTIFIED BY '(password)'
102
+ # WITH GRANT OPTION;
92
103
 
93
104
  class MysqlStore < SqlStore
94
105
  extend MysqlUtils
@@ -109,6 +120,16 @@ class MysqlStore < SqlStore
109
120
  super
110
121
  end
111
122
 
123
+ # Initialize the MySQL store.
124
+ #
125
+ # === Options
126
+ #
127
+ # * :address, the addres where the server is listening.
128
+ # * :socket, is useful when the pure ruby driver is used.
129
+ # this is the location of mysql.sock. For Ubuntu/Debian
130
+ # this is '/var/run/mysqld/mysqld.sock'. You can find
131
+ # the location for your system in my.cnf
132
+
112
133
  def initialize(options)
113
134
  super
114
135
 
@@ -118,9 +139,11 @@ class MysqlStore < SqlStore
118
139
  options[:address] || 'localhost',
119
140
  options[:user],
120
141
  options[:password],
121
- options[:name]
142
+ options[:name],
143
+ options[:port],
144
+ options[:socket]
122
145
  )
123
-
146
+
124
147
  # You should set recconect to true to avoid MySQL has
125
148
  # gone away errors.
126
149
 
@@ -143,9 +166,9 @@ class MysqlStore < SqlStore
143
166
  super
144
167
  end
145
168
 
146
- def enchant(klass, manager)
147
- if klass.metadata.primary_key.flatten.first == :oid
148
- unless klass.properties.find { |p| p.symbol == :oid }
169
+ def enchant(klass, manager)
170
+ if klass.ann.this.primary_key.symbol == :oid
171
+ unless klass.properties.include? :oid
149
172
  klass.property :oid, Fixnum, :sql => 'integer AUTO_INCREMENT PRIMARY KEY'
150
173
  end
151
174
  end
@@ -195,14 +218,23 @@ class MysqlStore < SqlStore
195
218
  private
196
219
 
197
220
  def create_table(klass)
221
+ # rp: fixes problems when user doesn't have
222
+ # write access to db.
223
+ # THINK, should a method more like this be
224
+ # used instead of catching database exceptions
225
+ # for 'table exists'?
226
+
227
+ return if @conn.list_tables.include?(klass::OGTABLE)
228
+
229
+
198
230
  fields = fields_for_class(klass)
199
231
 
200
232
  sql = "CREATE TABLE #{klass::OGTABLE} (#{fields.join(', ')}"
201
233
 
202
- # Create table constrains.
234
+ # Create table constraints.
203
235
 
204
- if klass.metadata and constrains = klass.metadata.sql_constrain
205
- sql << ", #{constrains.join(', ')}"
236
+ if constraints = klass.ann.this[:sql_constraint]
237
+ sql << ", #{constraints.join(', ')}"
206
238
  end
207
239
 
208
240
  if table_type = @options[:table_type]
@@ -213,7 +245,7 @@ private
213
245
 
214
246
  # Create indices.
215
247
 
216
- if klass.__meta and indices = klass.__meta[:index]
248
+ if indices = klass.ann.this[:index]
217
249
  for data in indices
218
250
  idx, options = *data
219
251
  idx = idx.to_s
@@ -240,7 +272,7 @@ private
240
272
  # Create join tables if needed. Join tables are used in
241
273
  # 'many_to_many' relations.
242
274
 
243
- if klass.__meta and join_tables = klass.__meta[:join_tables]
275
+ if join_tables = klass.ann.this[:join_tables]
244
276
  for info in join_tables
245
277
  begin
246
278
  create_join_table_sql(info).each do |sql|
@@ -248,8 +280,8 @@ private
248
280
  end
249
281
  Logger.debug "Created jointable '#{info[:table]}'."
250
282
  rescue => ex
251
- if ex.respond_to?(:errno) and ex.errno == 1050 # table already exists.
252
- Logger.debug 'Join table already exists' if $DBG
283
+ if ex.respond_to?(:errno) and ex.errno == 1050 # table already exists.
284
+ Logger.debug 'Join table already exists' if $DBG
253
285
  else
254
286
  raise
255
287
  end
@@ -274,20 +306,20 @@ private
274
306
 
275
307
  def write_prop(p)
276
308
  if p.klass.ancestors.include?(Integer)
277
- return "#\{@#{p.symbol} || 'NULL'\}"
309
+ return "#\{@#{p} || 'NULL'\}"
278
310
  elsif p.klass.ancestors.include?(Float)
279
- return "#\{@#{p.symbol} || 'NULL'\}"
311
+ return "#\{@#{p} || 'NULL'\}"
280
312
  elsif p.klass.ancestors.include?(String)
281
- return %|#\{@#{p.symbol} ? "'#\{#{self.class}.escape(@#{p.symbol})\}'" : 'NULL'\}|
313
+ return %|#\{@#{p} ? "'#\{#{self.class}.escape(@#{p})\}'" : 'NULL'\}|
282
314
  elsif p.klass.ancestors.include?(Time)
283
- return %|#\{@#{p.symbol} ? "'#\{#{self.class}.timestamp(@#{p.symbol})\}'" : 'NULL'\}|
315
+ return %|#\{@#{p} ? "'#\{#{self.class}.timestamp(@#{p})\}'" : 'NULL'\}|
284
316
  elsif p.klass.ancestors.include?(Date)
285
- return %|#\{@#{p.symbol} ? "'#\{#{self.class}.date(@#{p.symbol})\}'" : 'NULL'\}|
317
+ return %|#\{@#{p} ? "'#\{#{self.class}.date(@#{p})\}'" : 'NULL'\}|
286
318
  elsif p.klass.ancestors.include?(TrueClass)
287
- return "#\{@#{p.symbol} ? \"'1'\" : 'NULL' \}"
319
+ return "#\{@#{p} ? \"'1'\" : 'NULL' \}"
288
320
  else
289
321
  # gmosx: keep the '' for nil symbols.
290
- return %|#\{@#{p.symbol} ? "'#\{#{self.class}.escape(@#{p.symbol}.to_yaml)\}'" : "''"\}|
322
+ return %|#\{@#{p} ? "'#\{#{self.class}.escape(@#{p}.to_yaml)\}'" : "''"\}|
291
323
  end
292
324
  end
293
325
 
@@ -303,18 +335,18 @@ private
303
335
  elsif p.klass.ancestors.include?(Date)
304
336
  return "#{self.class}.parse_date(res[#{col} + offset])"
305
337
  elsif p.klass.ancestors.include?(TrueClass)
306
- return "('0' != res[#{col} + offset])"
338
+ return "('1' == res[#{col} + offset])"
307
339
  else
308
340
  return "YAML.load(res[#{col} + offset])"
309
341
  end
310
342
  end
311
343
 
312
344
  def eval_og_insert(klass)
313
- props = klass.properties.dup
345
+ props = klass.properties.values
314
346
  values = props.collect { |p| write_prop(p) }.join(',')
315
347
 
316
- if klass.metadata.superclass or klass.metadata.subclasses
317
- props << Property.new(:ogtype, String)
348
+ if klass.schema_inheritance?
349
+ props << Property.new(:symbol => :ogtype, :klass => String)
318
350
  values << ", '#{klass}'"
319
351
  end
320
352
 
@@ -323,7 +355,6 @@ private
323
355
  klass.class_eval %{
324
356
  def og_insert(store)
325
357
  #{Aspects.gen_advice_code(:og_insert, klass.advices, :pre) if klass.respond_to?(:advices)}
326
- puts "#{sql}"
327
358
  store.conn.query_with_result = false
328
359
  store.conn.query "#{sql}"
329
360
  @#{klass.pk_symbol} = store.conn.insert_id
@@ -59,6 +59,8 @@ module PsqlUtils
59
59
  end
60
60
 
61
61
  def parse_blob(val)
62
+ return '' unless val
63
+
62
64
  val.gsub(/\\(\\|'|[0-3][0-7][0-7])/) do |s|
63
65
  if s.size == 2 then s[1,1] else s[1,3].oct.chr end
64
66
  end
@@ -126,14 +128,14 @@ class PsqlStore < SqlStore
126
128
  end
127
129
 
128
130
  def enchant(klass, manager)
129
- if sclass = klass.metadata.superclass
130
- klass.const_set 'OGSEQ', "#{table(sclass.first)}_oid_seq"
131
+ if klass.schema_inheritance_child?
132
+ klass.const_set 'OGSEQ', "#{table(klass.schema_inheritance_root_class)}_oid_seq"
131
133
  else
132
134
  klass.const_set 'OGSEQ', "#{table(klass)}_oid_seq"
133
135
  end
134
136
 
135
- if klass.metadata.primary_key.flatten.first == :oid
136
- unless klass.properties.find { |p| p.symbol == :oid }
137
+ if klass.ann.this.primary_key.symbol == :oid
138
+ unless klass.properties.include? :oid
137
139
  klass.property :oid, Fixnum, :sql => 'serial PRIMARY KEY'
138
140
  end
139
141
  end
@@ -177,17 +179,17 @@ private
177
179
 
178
180
  sql = "CREATE TABLE #{klass::OGTABLE} (#{fields.join(', ')}"
179
181
 
180
- # Create table constrains.
182
+ # Create table constraints.
181
183
 
182
- if klass.__meta and constrains = klass.__meta[:sql_constrain]
183
- sql << ", #{constrains.join(', ')}"
184
+ if constraints = klass.ann.this[:sql_constraint]
185
+ sql << ", #{constraints.join(', ')}"
184
186
  end
185
187
 
186
188
  sql << ") WITHOUT OIDS;"
187
189
 
188
190
  # Create indices.
189
191
 
190
- if klass.__meta and indices = klass.__meta[:index]
192
+ if indices = klass.ann.this[:index]
191
193
  for data in indices
192
194
  idx, options = *data
193
195
  idx = idx.to_s
@@ -213,7 +215,7 @@ private
213
215
  # Create join tables if needed. Join tables are used in
214
216
  # 'many_to_many' relations.
215
217
 
216
- if klass.__meta and join_tables = klass.__meta[:join_tables]
218
+ if join_tables = klass.ann.this[:join_tables]
217
219
  for info in join_tables
218
220
  begin
219
221
  create_join_table_sql(info).each do |sql|
@@ -275,11 +277,11 @@ private
275
277
  #++
276
278
 
277
279
  def eval_og_insert(klass)
278
- props = klass.properties.dup
280
+ props = klass.properties.values.dup
279
281
  values = props.collect { |p| write_prop(p) }.join(',')
280
282
 
281
- if klass.metadata.superclass or klass.metadata.subclasses
282
- props << Property.new(:ogtype, String)
283
+ if klass.schema_inheritance?
284
+ props << Property.new(:symbol => :ogtype, :klass => String)
283
285
  values << ", '#{klass}'"
284
286
  end
285
287
 
@@ -298,7 +300,7 @@ private
298
300
  end
299
301
 
300
302
  def eval_og_allocate(klass)
301
- if klass.metadata.subclasses
303
+ if klass.ann.this[:subclasses]
302
304
  klass.module_eval %{
303
305
  def self.og_allocate(res, row = 0)
304
306
  Object.constant(res.getvalue(row, 0)).allocate
@@ -1,6 +1,7 @@
1
1
  require 'yaml'
2
+ require 'time'
2
3
 
3
- require 'nano/object/constant'
4
+ require 'nano/kernel/constant'
4
5
 
5
6
  module Og
6
7
 
@@ -105,53 +106,50 @@ module SqlUtils
105
106
 
106
107
  # Apply table name conventions to a class name.
107
108
 
108
- def tableize(klass)
109
- "#{klass.to_s.gsub(/::/, "_").downcase}"
110
- end
109
+ def tableize(klass)
110
+ "#{klass.to_s.gsub(/::/, "_").downcase}"
111
+ end
111
112
 
112
113
  def table(klass)
113
- if t = klass.metadata.sql_table
114
- return t.first
114
+ klass.ann.this[:sql_table] || klass.ann.this[:table] || "#{Og.table_prefix}#{tableize(klass)}"
115
+ end
116
+
117
+ def join_object_ordering(obj1, obj2)
118
+ if obj1.class.to_s <= obj2.class.to_s
119
+ return obj1, obj2
115
120
  else
116
- "#{Og.table_prefix}#{tableize(klass)}"
117
- end
121
+ return obj2, obj1, true
122
+ end
118
123
  end
119
124
 
120
- def join_object_ordering(obj1, obj2)
121
- if obj1.class.to_s <= obj2.class.to_s
122
- return obj1, obj2
123
- else
124
- return obj2, obj1, true
125
- end
126
- end
127
-
128
- def join_class_ordering(class1, class2)
125
+ def join_class_ordering(class1, class2)
129
126
  if class1.to_s <= class2.to_s
130
- return class1, class2
127
+ return class1, class2
131
128
  else
132
- return class2, class1, true
129
+ return class2, class1, true
133
130
  end
134
131
  end
135
-
136
- def build_join_name(class1, class2, postfix = nil)
137
- # Don't reorder arguments, as this is used in places that
138
- # have already determined the order they want.
139
- "#{Og.table_prefix}j_#{tableize(class1)}_#{tableize(class2)}#{postfix}"
140
- end
141
-
142
- def join_table(class1, class2, postfix = nil)
143
- first, second = join_class_ordering(class1, class2)
144
- build_join_name(first, second, postfix)
145
- end
146
-
147
- def join_table_index(key)
148
- "#{key}_idx"
149
- end
150
-
151
- def join_table_key(klass)
152
- "#{klass.to_s.split('::').last.downcase}_oid"
153
- end
154
-
132
+
133
+ def build_join_name(class1, class2, postfix = nil)
134
+ # Don't reorder arguments, as this is used in places that
135
+ # have already determined the order they want.
136
+ "#{Og.table_prefix}j_#{tableize(class1)}_#{tableize(class2)}#{postfix}"
137
+ end
138
+
139
+ def join_table(class1, class2, postfix = nil)
140
+ first, second = join_class_ordering(class1, class2)
141
+ build_join_name(first, second, postfix)
142
+ end
143
+
144
+ def join_table_index(key)
145
+ "#{key}_idx"
146
+ end
147
+
148
+ def join_table_key(klass)
149
+ klass = klass.schema_inheritance_root_class if klass.schema_inheritance_child?
150
+ "#{klass.to_s.split('::').last.downcase}_oid"
151
+ end
152
+
155
153
  def join_table_keys(class1, class2)
156
154
  if class1 == class2
157
155
  # Fix for the self-join case.
@@ -165,8 +163,14 @@ module SqlUtils
165
163
  first, second = join_class_ordering(class1, class2)
166
164
  return join_table_keys(first, second)
167
165
  end
168
-
169
- def join_table_info(owner_class, target_class, postfix = nil)
166
+
167
+ def join_table_info(owner_class, target_class, postfix = nil)
168
+
169
+ # some fixes for schema inheritance.
170
+
171
+ owner_class = owner_class.schema_inheritance_root_class if owner_class.schema_inheritance_child?
172
+ target_class = target_class.schema_inheritance_root_class if target_class.schema_inheritance_child?
173
+
170
174
  owner_key, target_key = join_table_keys(owner_class, target_class)
171
175
  first, second, changed = join_class_ordering(owner_class, target_class)
172
176
 
@@ -175,35 +179,35 @@ module SqlUtils
175
179
  else
176
180
  first_key, second_key = owner_key, target_key
177
181
  end
178
-
179
- return {
180
- :table => join_table(owner_class, target_class, postfix),
181
- :owner_key => owner_key,
182
- :target_key => target_key,
183
- :first_table => table(first),
184
- :first_key => first_key,
185
- :first_index => join_table_index(first_key),
186
- :second_table => table(second),
187
- :second_key => second_key,
188
- :second_index => join_table_index(second_key)
189
- }
190
- end
191
-
182
+
183
+ return {
184
+ :table => join_table(owner_class, target_class, postfix),
185
+ :owner_key => owner_key,
186
+ :target_key => target_key,
187
+ :first_table => table(first),
188
+ :first_key => first_key,
189
+ :first_index => join_table_index(first_key),
190
+ :second_table => table(second),
191
+ :second_key => second_key,
192
+ :second_index => join_table_index(second_key)
193
+ }
194
+ end
195
+
192
196
  # Subclasses can override this if they need a different
193
197
  # syntax.
194
-
195
- def create_join_table_sql(join_table_info, suffix = 'NOT NULL', key_type = 'integer')
198
+
199
+ def create_join_table_sql(join_table_info, suffix = 'NOT NULL', key_type = 'integer')
196
200
  join_table = join_table_info[:table]
197
- first_index = join_table_info[:first_index]
198
- first_key = join_table_info[:first_key]
199
- second_key = join_table_info[:second_key]
201
+ first_index = join_table_info[:first_index]
202
+ first_key = join_table_info[:first_key]
203
+ second_key = join_table_info[:second_key]
200
204
  second_index = join_table_info[:second_index]
201
205
 
202
206
  sql = []
203
207
 
204
208
  sql << %{
205
- CREATE TABLE #{join_table} (
206
- #{first_key} integer NOT NULL,
209
+ CREATE TABLE #{join_table} (
210
+ #{first_key} integer NOT NULL,
207
211
  #{second_key} integer NOT NULL,
208
212
  PRIMARY KEY(#{first_key}, #{second_key})
209
213
  )
@@ -213,9 +217,9 @@ module SqlUtils
213
217
  # sql << "CREATE INDEX #{first_index} ON #{join_table} (#{first_key})"
214
218
  # sql << "CREATE INDEX #{second_index} ON #{join_table} (#{second_key})"
215
219
 
216
- return sql
217
- end
218
-
220
+ return sql
221
+ end
222
+
219
223
  end
220
224
 
221
225
  # An abstract SQL powered store.
@@ -267,8 +271,9 @@ class SqlStore < Store
267
271
 
268
272
  # setup the table where this class is mapped.
269
273
 
270
- if sclass = klass.metadata.superclass
271
- klass.const_set 'OGTABLE', table(sclass.first)
274
+ if klass.schema_inheritance_child?
275
+ # farms: allow deeper inheritance (TODO: use annotation :superclass)
276
+ klass.const_set 'OGTABLE', table(klass.schema_inheritance_root_class)
272
277
  else
273
278
  klass.const_set 'OGTABLE', table(klass)
274
279
  end
@@ -431,24 +436,28 @@ class SqlStore < Store
431
436
  # Typically used in joins_many and many_to_many relations.
432
437
 
433
438
  def join(obj1, obj2, table, options = nil)
434
- first, second = join_object_ordering(obj1, obj2)
439
+ first, second = join_object_ordering(obj1, obj2)
435
440
  first_key, second_key = ordered_join_table_keys(obj1.class, obj2.class)
436
441
  if options
437
442
  exec "INSERT INTO #{table} (#{first_key},#{second_key}, #{options.keys.join(',')}) VALUES (#{first.pk},#{second.pk}, #{options.values.map { |v| quote(v) }.join(',')})"
438
443
  else
439
444
  exec "INSERT INTO #{table} (#{first_key},#{second_key}) VALUES (#{first.pk}, #{second.pk})"
440
- end
445
+ end
441
446
  end
442
447
 
443
448
  # Unrelate two objects be removing their relation from the
444
449
  # join table.
445
450
 
446
451
  def unjoin(obj1, obj2, table)
447
- first, second = join_object_ordering(obj1, obj2)
448
- first_key, second_key = ordered_join_table_keys(obj1.class, obj2.class)
452
+ first, second = join_object_ordering(obj1, obj2)
453
+ first_key, second_key = ordered_join_table_keys(obj1.class, obj2.class)
449
454
  exec "DELETE FROM #{table} WHERE #{first_key}=#{first.pk} AND #{second_key}=#{second.pk}"
450
455
  end
451
456
 
457
+ def delete_all(klass)
458
+ exec "DELETE FROM #{klass.table}"
459
+ end
460
+
452
461
  # :section: Transaction methods.
453
462
 
454
463
  # Start a new transaction.
@@ -496,6 +505,7 @@ private
496
505
  def drop_table(klass)
497
506
  exec "DROP TABLE #{klass.table}"
498
507
  end
508
+ alias_method :destroy, :drop_table
499
509
 
500
510
  # Evolve (recreate) the sql table where objects of this class
501
511
  # are persisted.
@@ -509,57 +519,46 @@ private
509
519
  # Return the field for the given property.
510
520
 
511
521
  def field_for_property(property)
512
- if f = property.meta[:field]
522
+ if f = property.field
513
523
  return f.to_s
514
524
  else
515
- return property.symbol.to_s
525
+ return property.to_s
516
526
  end
517
527
  end
518
528
 
519
529
  # Create the fields that correpsond to the klass properties.
520
530
  # The generated fields array is used in create_table.
521
- # If the property has an :sql metadata this overrides the
522
- # default mapping. If the property has an :extra_sql metadata
531
+ # If the property has an :sql annotation this overrides the
532
+ # default mapping. If the property has an :extra_sql annotation
523
533
  # the extra sql is appended after the default mapping.
524
534
 
525
535
  def fields_for_class(klass)
526
536
  fields = []
527
- properties = klass.properties
537
+ properties = klass.properties.dup
528
538
 
529
- if subclasses = klass.metadata.subclasses
539
+ if klass.ancestors.include? SchemaInheritanceBase
530
540
  # This class as a superclass in a single table inheritance
531
541
  # chain. So inject a special class ogtype field that
532
542
  # holds the class name.
533
543
  fields << "ogtype VARCHAR(30)"
534
-
535
- for subclass in subclasses
536
- properties.concat(subclass.properties)
544
+
545
+ for desc in klass.descendents
546
+ properties.update(desc.properties)
537
547
  end
538
-
539
- properties.uniq!
540
548
  end
541
-
542
- properties.each do |p|
543
- klass.index(p.symbol) if p.meta[:index]
549
+
550
+ for p in properties.values
551
+ klass.index(p.symbol) if p.index
544
552
 
545
553
  field = field_for_property(p)
546
554
 
547
- if p.meta and p.meta[:sql]
548
- field << " #{p.meta[:sql]}"
555
+ if p.sql
556
+ field << " #{p.sql}"
549
557
  else
550
558
  field << " #{type_for_class(p.klass)}"
551
-
552
- if p.meta
553
- field << " UNIQUE" if p.meta[:unique]
554
-
555
- if default = p.meta[:default]
556
- field << " DEFAULT #{default.inspect} NOT NULL"
557
- end
558
-
559
- if extra_sql = p.meta[:extra_sql]
560
- field << " #{extra_sql}"
561
- end
562
- end
559
+ field << " UNIQUE" if p.unique
560
+ field << " DEFAULT #{p.default.inspect} NOT NULL" if p.default
561
+ field << " #{p.extra_sql}" if p.extra_sql
563
562
  end
564
563
 
565
564
  fields << field
@@ -582,22 +581,22 @@ private
582
581
 
583
582
  def write_prop(p)
584
583
  if p.klass.ancestors.include?(Integer)
585
- return "#\{@#{p.symbol} || 'NULL'\}"
584
+ return "#\{@#{p} || 'NULL'\}"
586
585
  elsif p.klass.ancestors.include?(Float)
587
- return "#\{@#{p.symbol} || 'NULL'\}"
586
+ return "#\{@#{p} || 'NULL'\}"
588
587
  elsif p.klass.ancestors.include?(String)
589
- return %|#\{@#{p.symbol} ? "'#\{#{self.class}.escape(@#{p.symbol})\}'" : 'NULL'\}|
588
+ return %|#\{@#{p} ? "'#\{#{self.class}.escape(@#{p})\}'" : 'NULL'\}|
590
589
  elsif p.klass.ancestors.include?(Time)
591
- return %|#\{@#{p.symbol} ? "'#\{#{self.class}.timestamp(@#{p.symbol})\}'" : 'NULL'\}|
590
+ return %|#\{@#{p} ? "'#\{#{self.class}.timestamp(@#{p})\}'" : 'NULL'\}|
592
591
  elsif p.klass.ancestors.include?(Date)
593
- return %|#\{@#{p.symbol} ? "'#\{#{self.class}.date(@#{p.symbol})\}'" : 'NULL'\}|
592
+ return %|#\{@#{p} ? "'#\{#{self.class}.date(@#{p})\}'" : 'NULL'\}|
594
593
  elsif p.klass.ancestors.include?(TrueClass)
595
- return "#\{@#{p.symbol} ? \"'t'\" : 'NULL' \}"
594
+ return "#\{@#{p} ? \"'t'\" : 'NULL' \}"
596
595
  elsif p.klass.ancestors.include?(Og::Blob)
597
- return %|#\{@#{p.symbol} ? "'#\{#{self.class}.escape(#{self.class}.blob(@#{p.symbol}))\}'" : 'NULL'\}|
596
+ return %|#\{@#{p} ? "'#\{#{self.class}.escape(#{self.class}.blob(@#{p}))\}'" : 'NULL'\}|
598
597
  else
599
598
  # gmosx: keep the '' for nil symbols.
600
- return %|#\{@#{p.symbol} ? "'#\{#{self.class}.escape(@#{p.symbol}.to_yaml)\}'" : "''"\}|
599
+ return %|#\{@#{p} ? "'#\{#{self.class}.escape(@#{p}.to_yaml)\}'" : "''"\}|
601
600
  end
602
601
  end
603
602
 
@@ -630,11 +629,11 @@ private
630
629
 
631
630
  def eval_og_insert(klass)
632
631
  pk = klass.pk_symbol
633
- props = klass.properties.dup
632
+ props = klass.properties.values.dup
634
633
  values = props.collect { |p| write_prop(p) }.join(',')
635
634
 
636
- if klass.metadata.superclass or klass.metadata.subclasses
637
- props << Property.new(:ogtype, String)
635
+ if klass.ann.this[:superclass] or klass.ann.this[:subclasses]
636
+ props << Property.new(:symbol => :ogtype, :klass => String)
638
637
  values << ", '#{klass}'"
639
638
  end
640
639
 
@@ -653,7 +652,7 @@ private
653
652
 
654
653
  def eval_og_update(klass)
655
654
  pk = klass.pk_symbol
656
- props = klass.properties.reject { |p| pk == p.symbol }
655
+ props = klass.properties.values.reject { |p| pk == p.symbol }
657
656
 
658
657
  updates = props.collect { |p|
659
658
  "#{field_for_property(p)}=#{write_prop(p)}"
@@ -680,14 +679,14 @@ private
680
679
 
681
680
  def eval_og_read(klass)
682
681
  code = []
683
- props = klass.properties
682
+ props = klass.properties.values
684
683
  field_map = create_field_map(klass)
685
684
 
686
- props.each do |p|
687
- f = field_for_property(p).intern
685
+ for p in props
686
+ f = field_for_property(p).to_sym
688
687
 
689
688
  if col = field_map[f]
690
- code << "@#{p.symbol} = #{read_prop(p, col)}"
689
+ code << "@#{p} = #{read_prop(p, col)}"
691
690
  end
692
691
  end
693
692
 
@@ -713,8 +712,8 @@ private
713
712
  pk ||= @#{klass.pk_symbol}
714
713
  transaction do |tx|
715
714
  tx.exec "DELETE FROM #{klass.table} WHERE #{klass.pk_symbol}=\#{pk}"
716
- if cascade and #{klass}.metadata[:descendants]
717
- #{klass}.metadata[:descendants].each do |dclass, foreign_key|
715
+ if cascade and #{klass}.ann.this[:descendants]
716
+ #{klass}.ann.this.descendants.each do |dclass, foreign_key|
718
717
  tx.exec "DELETE FROM \#{dclass::OGTABLE} WHERE \#{foreign_key}=\#{pk}"
719
718
  end
720
719
  end
@@ -732,7 +731,9 @@ private
732
731
  def og_create_schema(store)
733
732
  if Og.create_schema
734
733
  #{Aspects.gen_advice_code(:og_create_schema, klass.advices, :pre) if klass.respond_to?(:advices)}
735
- store.send(:create_table, #{klass})
734
+ unless self.class.superclass.ancestors.include? SchemaInheritanceBase
735
+ store.send(:create_table, #{klass})
736
+ end
736
737
  #{Aspects.gen_advice_code(:og_create_schema, klass.advices, :post) if klass.respond_to?(:advices)}
737
738
  end
738
739
  end
@@ -743,7 +744,7 @@ private
743
744
  # STI parent classes it reads the class from the resultset.
744
745
 
745
746
  def eval_og_allocate(klass)
746
- if klass.metadata.subclasses
747
+ if klass.schema_inheritance?
747
748
  klass.module_eval %{
748
749
  def self.og_allocate(res, row = 0)
749
750
  Object.constant(res[0]).allocate
@@ -784,14 +785,14 @@ private
784
785
  if rel = klass.relation(name)
785
786
  target_table = rel[:target_class]::OGTABLE
786
787
  tables << target_table
787
- # join_conditions << "#{klass::OGTABLE}.#{rel[:foreign_key]}=#{target_table}.#{rel[:target_pk]}"
788
+
788
789
  if rel.is_a?(JoinsMany)
789
790
  tables << rel[:join_table]
790
791
  owner_key, target_key = klass.ogmanager.store.join_table_keys(klass, rel[:target_class])
791
- join_conditions << "#{rel[:join_table]}.#{owner_key}=#{klass::OGTABLE}.#{rel[:owner_pk]} AND \
792
- #{rel[:join_table]}.#{target_key}=#{rel[:target_class]::OGTABLE}.#{rel[:target_pk]}"
792
+ join_conditions << "#{rel.join_table}.#{owner_key}=#{klass::OGTABLE}.#{rel.owner_class.primary_key} AND \
793
+ #{rel.join_table}.#{target_key}=#{rel.target_class::OGTABLE}.#{rel.target_class.primary_key}"
793
794
  else
794
- join_conditions << "#{klass::OGTABLE}.#{rel[:foreign_key]}=#{target_table}.#{rel[:target_pk]}"
795
+ join_conditions << "#{klass::OGTABLE}.#{rel.foreign_key}=#{target_table}.#{rel.target_class.primary_key}"
795
796
  end
796
797
  else
797
798
  raise 'Unknown relation name'
@@ -826,7 +827,7 @@ private
826
827
  sql << " ORDER BY #{order}"
827
828
  end
828
829
 
829
- resolve_limit_options(options, sql)
830
+ resolve_limit_options(options, sql)
830
831
 
831
832
  if extra = options[:extra]
832
833
  sql << " #{extra}"
@@ -834,20 +835,20 @@ private
834
835
 
835
836
  return sql
836
837
  end
837
-
838
+
838
839
  # Subclasses can override this if they need some other order.
839
840
  # This is needed because different backends require different
840
841
  # order of the keywords.
841
-
842
- def resolve_limit_options(options, sql)
843
- if limit = options[:limit]
844
- sql << " LIMIT #{limit}"
845
-
846
- if offset = options[:offset]
847
- sql << " OFFSET #{offset}"
848
- end
849
- end
850
- end
842
+
843
+ def resolve_limit_options(options, sql)
844
+ if limit = options[:limit]
845
+ sql << " LIMIT #{limit}"
846
+
847
+ if offset = options[:offset]
848
+ sql << " OFFSET #{offset}"
849
+ end
850
+ end
851
+ end
851
852
 
852
853
  # :section: Deserialization methods.
853
854