og 0.23.0 → 0.24.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 (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