og 0.24.0 → 0.25.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 (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