og 0.24.0 → 0.25.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/ProjectInfo +2 -5
  2. data/README +2 -0
  3. data/doc/AUTHORS +4 -1
  4. data/doc/RELEASES +53 -0
  5. data/examples/run.rb +2 -2
  6. data/lib/{og/mixin → glue}/hierarchical.rb +19 -19
  7. data/lib/{og/mixin → glue}/optimistic_locking.rb +1 -1
  8. data/lib/glue/orderable.rb +235 -0
  9. data/lib/glue/revisable.rb +2 -0
  10. data/lib/glue/taggable.rb +176 -0
  11. data/lib/{og/mixin/taggable.rb → glue/taggable_old.rb} +6 -0
  12. data/lib/glue/timestamped.rb +37 -0
  13. data/lib/{og/mixin → glue}/tree.rb +3 -8
  14. data/lib/og.rb +21 -20
  15. data/lib/og/collection.rb +15 -1
  16. data/lib/og/entity.rb +256 -114
  17. data/lib/og/manager.rb +60 -27
  18. data/lib/og/{mixin/schema_inheritance_base.rb → markers.rb} +5 -2
  19. data/lib/og/relation.rb +70 -74
  20. data/lib/og/relation/belongs_to.rb +5 -3
  21. data/lib/og/relation/has_many.rb +1 -0
  22. data/lib/og/relation/joins_many.rb +5 -4
  23. data/lib/og/store.rb +25 -46
  24. data/lib/og/store/alpha/filesys.rb +1 -1
  25. data/lib/og/store/alpha/kirby.rb +30 -30
  26. data/lib/og/store/alpha/memory.rb +49 -49
  27. data/lib/og/store/alpha/sqlserver.rb +7 -7
  28. data/lib/og/store/kirby.rb +38 -38
  29. data/lib/og/store/mysql.rb +43 -43
  30. data/lib/og/store/psql.rb +222 -53
  31. data/lib/og/store/sql.rb +165 -105
  32. data/lib/og/store/sqlite.rb +29 -25
  33. data/lib/og/validation.rb +24 -14
  34. data/lib/{vendor → og/vendor}/README +0 -0
  35. data/lib/{vendor → og/vendor}/kbserver.rb +1 -1
  36. data/lib/{vendor → og/vendor}/kirbybase.rb +230 -79
  37. data/lib/{vendor → og/vendor}/mysql.rb +0 -0
  38. data/lib/{vendor → og/vendor}/mysql411.rb +0 -0
  39. data/test/og/mixin/tc_hierarchical.rb +1 -1
  40. data/test/og/mixin/tc_optimistic_locking.rb +1 -1
  41. data/test/og/mixin/tc_orderable.rb +1 -1
  42. data/test/og/mixin/tc_taggable.rb +2 -2
  43. data/test/og/mixin/tc_timestamped.rb +2 -2
  44. data/test/og/tc_finder.rb +33 -0
  45. data/test/og/tc_inheritance.rb +2 -2
  46. data/test/og/tc_scoped.rb +45 -0
  47. data/test/og/tc_store.rb +1 -7
  48. metadata +21 -18
  49. data/lib/og/mixin/orderable.rb +0 -174
  50. data/lib/og/mixin/revisable.rb +0 -0
  51. data/lib/og/mixin/timestamped.rb +0 -24
@@ -214,8 +214,8 @@ private
214
214
  # gmosx: keep the '' for nil symbols.
215
215
  return %|#\{@#{p.symbol} ? "'#\{#{self.class}.escape(@#{p.symbol}.to_yaml)\}'" : "''"\}|
216
216
  end
217
- end
218
-
217
+ end
218
+
219
219
  def read_prop(p, col)
220
220
  if p.klass.ancestors.include?(Integer)
221
221
  return "res[#{col} + offset].to_i"
@@ -229,24 +229,24 @@ private
229
229
  return "#{self.class}.parse_date(res[#{col} + offset])"
230
230
  elsif p.klass.ancestors.include?(TrueClass)
231
231
  return "('0' != res[#{col} + offset])"
232
- else
232
+ else
233
233
  return "YAML.load(res[#{col} + offset])"
234
- end
234
+ end
235
235
  end
236
236
 
237
237
  def eval_og_insert(klass)
238
238
  props = klass.properties
239
239
  values = props.collect { |p| write_prop(p) }.join(',')
240
-
240
+
241
241
  sql = "INSERT INTO #{klass::OGTABLE} (#{props.collect {|p| p.symbol.to_s}.join(',')}) VALUES (#{values})"
242
242
 
243
243
  klass.class_eval %{
244
244
  def og_insert(store)
245
- #{Aspects.gen_advice_code(:og_insert, klass.advices, :pre) if klass.respond_to?(:advices)}
245
+ #{Glue::Aspects.gen_advice_code(:og_insert, klass.advices, :pre) if klass.respond_to?(:advices)}
246
246
  store.conn.query_with_result = false
247
247
  store.conn.query "#{sql}"
248
248
  @#{klass.pk_symbol} = store.conn.insert_id
249
- #{Aspects.gen_advice_code(:og_insert, klass.advices, :post) if klass.respond_to?(:advices)}
249
+ #{Glue::Aspects.gen_advice_code(:og_insert, klass.advices, :post) if klass.respond_to?(:advices)}
250
250
  end
251
251
  }
252
252
  end
@@ -1,5 +1,5 @@
1
1
  begin
2
- require 'vendor/kirbybase'
2
+ require 'og/vendor/kirbybase'
3
3
  rescue Object => ex
4
4
  Logger.error 'KirbyBase is not installed!'
5
5
  Logger.error ex
@@ -18,11 +18,11 @@ module Og
18
18
  class KirbyStore < SqlStore
19
19
 
20
20
  # Override if needed.
21
-
21
+
22
22
  def self.base_dir(options)
23
23
  options[:base_dir] || './kirbydb'
24
24
  end
25
-
25
+
26
26
  def self.destroy(options)
27
27
  begin
28
28
  FileUtils.rm_rf(base_dir(options))
@@ -35,7 +35,7 @@ class KirbyStore < SqlStore
35
35
  def initialize(options)
36
36
  super
37
37
  mode = options[:mode] || :local
38
-
38
+
39
39
  if mode == :client
40
40
  # Use a client/server configuration.
41
41
  @conn = KirbyBase.new(:client, options[:address], options[:port])
@@ -56,14 +56,14 @@ class KirbyStore < SqlStore
56
56
  # Nothing to do.
57
57
  super
58
58
  end
59
-
59
+
60
60
  def enchant(klass, manager)
61
61
  klass.send :attr_accessor, :recno
62
62
  klass.send :alias_method, :oid, :recno
63
63
  klass.send :alias_method, :oid=, :recno=
64
64
 
65
65
  symbols = klass.properties.keys
66
-
66
+
67
67
  klass.module_eval %{
68
68
  def self.kb_create(recno, #{symbols.join(', ')})
69
69
  obj = self.allocate
@@ -72,14 +72,14 @@ class KirbyStore < SqlStore
72
72
  return obj
73
73
  end
74
74
  }
75
-
75
+
76
76
  super
77
77
  end
78
78
 
79
79
  def get_table(klass)
80
80
  @conn.get_table(klass.table.to_sym)
81
81
  end
82
-
82
+
83
83
  # :section: Lifecycle methods.
84
84
 
85
85
  def load(pk, klass)
@@ -104,19 +104,19 @@ class KirbyStore < SqlStore
104
104
  #--
105
105
  # FIXME: optimize me!
106
106
  #++
107
-
107
+
108
108
  def count(options)
109
109
  find(options).size
110
110
  end
111
111
 
112
112
  def query(options)
113
113
  Logger.debug "Querying with #{options.inspect}." if $DBG
114
-
114
+
115
115
  klass = options[:class]
116
116
  table = get_table(klass)
117
-
117
+
118
118
  objects = []
119
-
119
+
120
120
  if condition = options[:condition] || options[:where]
121
121
  condition.gsub!(/=/, '==')
122
122
  condition.gsub!(/LIKE '(.*?)'/, '=~ /\1/')
@@ -126,29 +126,29 @@ class KirbyStore < SqlStore
126
126
  else
127
127
  objects = table.select
128
128
  end
129
-
129
+
130
130
  if order = options[:order]
131
131
  desc = (order =~ /DESC/)
132
132
  order = order.gsub(/DESC/, '').gsub(/ASC/, '')
133
133
  eval "objects.sort { |x, y| x.#{order} <=> y.#{order} }"
134
134
  objects.reverse! if desc
135
135
  end
136
-
137
- return objects
136
+
137
+ return objects
138
138
  end
139
139
 
140
- def start
140
+ def start
141
141
  # nop
142
142
  end
143
-
143
+
144
144
  # Commit a transaction.
145
-
145
+
146
146
  def commit
147
147
  # nop, not supported?
148
148
  end
149
-
149
+
150
150
  # Rollback a transaction.
151
-
151
+
152
152
  def rollback
153
153
  # nop, not supported?
154
154
  end
@@ -182,15 +182,15 @@ private
182
182
 
183
183
  def fields_for_class(klass)
184
184
  fields = []
185
-
185
+
186
186
  klass.properties.values.each do |p|
187
187
  klass.index(p.symbol) if p.index
188
-
188
+
189
189
  fields << p.symbol
190
-
190
+
191
191
  type = p.klass.name.to_sym
192
192
  type = :Integer if type == :Fixnum
193
-
193
+
194
194
  fields << type
195
195
  end
196
196
 
@@ -207,14 +207,14 @@ private
207
207
 
208
208
  klass.class_eval %{
209
209
  def og_insert(store)
210
- #{Aspects.gen_advice_code(:og_insert, klass.advices, :pre) if klass.respond_to?(:advices)}
210
+ #{Glue::Aspects.gen_advice_code(:og_insert, klass.advices, :pre) if klass.respond_to?(:advices)}
211
211
  Logger.debug "Inserting \#{self}." if $DBG
212
212
  @#{pk} = store.get_table(#{klass}).insert(self)
213
- #{Aspects.gen_advice_code(:og_insert, klass.advices, :post) if klass.respond_to?(:advices)}
213
+ #{Glue::Aspects.gen_advice_code(:og_insert, klass.advices, :post) if klass.respond_to?(:advices)}
214
214
  end
215
215
  }
216
216
  end
217
-
217
+
218
218
  # Compile the og_update method for the class.
219
219
 
220
220
  def eval_og_update(klass)
@@ -223,41 +223,41 @@ private
223
223
 
224
224
  klass.module_eval %{
225
225
  def og_update(store, options = nil)
226
- #{Aspects.gen_advice_code(:og_update, klass.advices, :pre) if klass.respond_to?(:advices)}
226
+ #{Glue::Aspects.gen_advice_code(:og_update, klass.advices, :pre) if klass.respond_to?(:advices)}
227
227
  store.get_table(#{klass}).update { |r| r.recno == #{pk} }.set(#{updates.join(', ')})
228
- #{Aspects.gen_advice_code(:og_update, klass.advices, :post) if klass.respond_to?(:advices)}
228
+ #{Glue::Aspects.gen_advice_code(:og_update, klass.advices, :post) if klass.respond_to?(:advices)}
229
229
  end
230
230
  }
231
231
  end
232
-
232
+
233
233
  def eval_og_read(klass)
234
234
  klass.module_eval %{
235
235
  def og_read(res, row = 0, offset = 0)
236
- #{Aspects.gen_advice_code(:og_read, klass.advices, :pre) if klass.respond_to?(:advices)}
237
- #{Aspects.gen_advice_code(:og_read, klass.advices, :post) if klass.respond_to?(:advices)}
236
+ #{Glue::Aspects.gen_advice_code(:og_read, klass.advices, :pre) if klass.respond_to?(:advices)}
237
+ #{Glue::Aspects.gen_advice_code(:og_read, klass.advices, :post) if klass.respond_to?(:advices)}
238
238
  end
239
239
  }
240
- end
240
+ end
241
241
 
242
242
  def eval_og_delete(klass)
243
243
  klass.module_eval %{
244
244
  def og_delete(store, pk, cascade = true)
245
245
  pk ||= self.pk
246
246
  pk = pk.to_i
247
- #{Aspects.gen_advice_code(:og_delete, klass.advices, :pre) if klass.respond_to?(:advices)}
247
+ #{Glue::Aspects.gen_advice_code(:og_delete, klass.advices, :pre) if klass.respond_to?(:advices)}
248
248
  table = store.get_table(self.class)
249
249
  transaction do |tx|
250
250
  table.delete { |r| r.recno == pk }
251
- if cascade and #{klass}.ann.this[:descendants]
252
- #{klass}.ann.this.descendants.each do |dclass, foreign_key|
251
+ if cascade and #{klass}.ann.self[:descendants]
252
+ #{klass}.ann.self.descendants.each do |dclass, foreign_key|
253
253
  dtable = store.get_table(dclass)
254
254
  dtable.delete { |r| foreign_key == pk }
255
255
  end
256
256
  end
257
257
  end
258
- #{Aspects.gen_advice_code(:og_delete, klass.advices, :post) if klass.respond_to?(:advices)}
258
+ #{Glue::Aspects.gen_advice_code(:og_delete, klass.advices, :post) if klass.respond_to?(:advices)}
259
259
  end
260
- }
260
+ }
261
261
  end
262
262
 
263
263
  end
@@ -5,8 +5,8 @@ rescue Object => ex
5
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
- require 'vendor/mysql'
9
- require 'vendor/mysql411'
8
+ require 'og/vendor/mysql'
9
+ require 'og/vendor/mysql411'
10
10
  rescue Object => ex
11
11
  Logger.error ex
12
12
  end
@@ -43,30 +43,30 @@ class Mysql::Result
43
43
  yield(row, 0)
44
44
  end
45
45
  end
46
-
46
+
47
47
  def first_value
48
48
  val = fetch_row[0]
49
49
  free
50
50
  return val
51
51
  end
52
-
52
+
53
53
  alias_method :close, :free
54
54
 
55
55
  def fields
56
56
  fetch_fields.map { |f| f.name }
57
- end
57
+ end
58
58
  end
59
59
 
60
60
  module Og
61
61
 
62
62
  module MysqlUtils
63
63
  include SqlUtils
64
-
64
+
65
65
  def escape(str)
66
66
  return nil unless str
67
67
  return Mysql.quote(str)
68
68
  end
69
-
69
+
70
70
  def quote(val)
71
71
  case val
72
72
  when Fixnum, Integer, Float
@@ -79,11 +79,11 @@ module MysqlUtils
79
79
  val ? "'#{date(val)}'" : 'NULL'
80
80
  when TrueClass
81
81
  val ? "'1'" : 'NULL'
82
- else
82
+ else
83
83
  # gmosx: keep the '' for nil symbols.
84
84
  val ? escape(val.to_yaml) : ''
85
- end
86
- end
85
+ end
86
+ end
87
87
  end
88
88
 
89
89
  # A Store that persists objects into a MySQL database.
@@ -129,7 +129,7 @@ class MysqlStore < SqlStore
129
129
  # this is the location of mysql.sock. For Ubuntu/Debian
130
130
  # this is '/var/run/mysqld/mysqld.sock'. You can find
131
131
  # the location for your system in my.cnf
132
-
132
+
133
133
  def initialize(options)
134
134
  super
135
135
 
@@ -146,12 +146,12 @@ class MysqlStore < SqlStore
146
146
 
147
147
  # You should set recconect to true to avoid MySQL has
148
148
  # gone away errors.
149
-
149
+
150
150
  if @conn.respond_to? :reconnect
151
151
  options[:reconnect] = true unless options.has_key?(:reconnect)
152
152
  @conn.reconnect = options[:reconnect]
153
153
  end
154
-
154
+
155
155
  rescue => ex
156
156
  if ex.errno == 1049 # database does not exist.
157
157
  Logger.info "Database '#{options[:name]}' not found!"
@@ -166,15 +166,15 @@ class MysqlStore < SqlStore
166
166
  super
167
167
  end
168
168
 
169
- def enchant(klass, manager)
170
- if klass.ann.this.primary_key.symbol == :oid
169
+ def enchant(klass, manager)
170
+ if klass.ann.self.primary_key.symbol == :oid
171
171
  unless klass.properties.include? :oid
172
172
  klass.property :oid, Fixnum, :sql => 'integer AUTO_INCREMENT PRIMARY KEY'
173
173
  end
174
174
  end
175
175
  super
176
176
  end
177
-
177
+
178
178
  def query(sql)
179
179
  Logger.debug sql if $DBG
180
180
  @conn.query_with_result = true
@@ -191,20 +191,20 @@ class MysqlStore < SqlStore
191
191
  handle_sql_exception(ex, sql)
192
192
  end
193
193
 
194
- def start
194
+ def start
195
195
  # nop
196
196
  # FIXME: InnoDB supports transactions.
197
197
  end
198
-
198
+
199
199
  # Commit a transaction.
200
-
200
+
201
201
  def commit
202
202
  # nop, not supported?
203
203
  # FIXME: InnoDB supports transactions.
204
204
  end
205
-
205
+
206
206
  # Rollback a transaction.
207
-
207
+
208
208
  def rollback
209
209
  # nop, not supported?
210
210
  # FIXME: InnoDB supports transactions.
@@ -223,29 +223,29 @@ private
223
223
  # THINK, should a method more like this be
224
224
  # used instead of catching database exceptions
225
225
  # for 'table exists'?
226
-
226
+
227
227
  return if @conn.list_tables.include?(klass::OGTABLE)
228
-
229
-
228
+
229
+
230
230
  fields = fields_for_class(klass)
231
231
 
232
232
  sql = "CREATE TABLE #{klass::OGTABLE} (#{fields.join(', ')}"
233
-
233
+
234
234
  # Create table constraints.
235
-
236
- if constraints = klass.ann.this[:sql_constraint]
235
+
236
+ if constraints = klass.ann.self[:sql_constraint]
237
237
  sql << ", #{constraints.join(', ')}"
238
238
  end
239
-
239
+
240
240
  if table_type = @options[:table_type]
241
241
  sql << ") TYPE = #{table_type};"
242
242
  else
243
243
  sql << ");"
244
244
  end
245
-
245
+
246
246
  # Create indices.
247
-
248
- if indices = klass.ann.this[:index]
247
+
248
+ if indices = klass.ann.self[:index]
249
249
  for data in indices
250
250
  idx, options = *data
251
251
  idx = idx.to_s
@@ -254,7 +254,7 @@ private
254
254
  sql << " CREATE #{pre_sql} INDEX #{klass::OGTABLE}_#{idxname}_idx #{post_sql} ON #{klass::OGTABLE} (#{idx});"
255
255
  end
256
256
  end
257
-
257
+
258
258
  @conn.query_with_result = false
259
259
 
260
260
  begin
@@ -268,11 +268,11 @@ private
268
268
  raise
269
269
  end
270
270
  end
271
-
271
+
272
272
  # Create join tables if needed. Join tables are used in
273
273
  # 'many_to_many' relations.
274
-
275
- if join_tables = klass.ann.this[:join_tables]
274
+
275
+ if join_tables = klass.ann.self[:join_tables]
276
276
  for info in join_tables
277
277
  begin
278
278
  create_join_table_sql(info).each do |sql|
@@ -317,12 +317,12 @@ private
317
317
  return %|#\{@#{p} ? "'#\{#{self.class}.date(@#{p})\}'" : 'NULL'\}|
318
318
  elsif p.klass.ancestors.include?(TrueClass)
319
319
  return "#\{@#{p} ? \"'1'\" : 'NULL' \}"
320
- else
320
+ else
321
321
  # gmosx: keep the '' for nil symbols.
322
322
  return %|#\{@#{p} ? "'#\{#{self.class}.escape(@#{p}.to_yaml)\}'" : "''"\}|
323
323
  end
324
- end
325
-
324
+ end
325
+
326
326
  def read_prop(p, col)
327
327
  if p.klass.ancestors.include?(Integer)
328
328
  return "#{self.class}.parse_int(res[#{col} + offset])"
@@ -336,9 +336,9 @@ private
336
336
  return "#{self.class}.parse_date(res[#{col} + offset])"
337
337
  elsif p.klass.ancestors.include?(TrueClass)
338
338
  return "('1' == res[#{col} + offset])"
339
- else
339
+ else
340
340
  return "YAML.load(res[#{col} + offset])"
341
- end
341
+ end
342
342
  end
343
343
 
344
344
  def eval_og_insert(klass)
@@ -349,16 +349,16 @@ private
349
349
  props << Property.new(:symbol => :ogtype, :klass => String)
350
350
  values << ", '#{klass}'"
351
351
  end
352
-
352
+
353
353
  sql = "INSERT INTO #{klass::OGTABLE} (#{props.collect {|p| field_for_property(p)}.join(',')}) VALUES (#{values})"
354
354
 
355
355
  klass.class_eval %{
356
356
  def og_insert(store)
357
- #{Aspects.gen_advice_code(:og_insert, klass.advices, :pre) if klass.respond_to?(:advices)}
357
+ #{Glue::Aspects.gen_advice_code(:og_insert, klass.advices, :pre) if klass.respond_to?(:advices)}
358
358
  store.conn.query_with_result = false
359
359
  store.conn.query "#{sql}"
360
360
  @#{klass.pk_symbol} = store.conn.insert_id
361
- #{Aspects.gen_advice_code(:og_insert, klass.advices, :post) if klass.respond_to?(:advices)}
361
+ #{Glue::Aspects.gen_advice_code(:og_insert, klass.advices, :post) if klass.respond_to?(:advices)}
362
362
  end
363
363
  }
364
364
  end