og 0.31.0 → 0.40.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 (97) hide show
  1. data/doc/{AUTHORS → CONTRIBUTORS} +26 -10
  2. data/doc/LICENSE +2 -3
  3. data/doc/RELEASES +56 -7
  4. data/doc/tutorial.txt +15 -15
  5. data/lib/glue/cacheable.rb +2 -5
  6. data/lib/glue/hierarchical.rb +1 -4
  7. data/lib/glue/optimistic_locking.rb +0 -2
  8. data/lib/glue/orderable.rb +79 -75
  9. data/lib/glue/revisable.rb +19 -24
  10. data/lib/glue/searchable.rb +0 -2
  11. data/lib/glue/taggable.rb +31 -29
  12. data/lib/glue/timestamped.rb +4 -2
  13. data/lib/og.rb +50 -29
  14. data/lib/og/adapter.rb +19 -0
  15. data/lib/og/adapter/mysql.rb +212 -0
  16. data/lib/og/adapter/mysql/override.rb +34 -0
  17. data/lib/og/adapter/mysql/script.rb +15 -0
  18. data/lib/og/adapter/mysql/utils.rb +40 -0
  19. data/lib/og/adapter/postgresql.rb +231 -0
  20. data/lib/og/adapter/postgresql/override.rb +117 -0
  21. data/lib/og/adapter/postgresql/script.rb +15 -0
  22. data/lib/og/adapter/postgresql/utils.rb +35 -0
  23. data/lib/og/adapter/sqlite.rb +132 -0
  24. data/lib/og/adapter/sqlite/override.rb +33 -0
  25. data/lib/og/adapter/sqlite/script.rb +15 -0
  26. data/lib/og/collection.rb +35 -7
  27. data/lib/og/{evolution.rb → dump.rb} +4 -5
  28. data/lib/og/entity.rb +102 -173
  29. data/lib/og/entity/clone.rb +119 -0
  30. data/lib/og/errors.rb +0 -2
  31. data/lib/og/manager.rb +85 -37
  32. data/lib/og/relation.rb +52 -34
  33. data/lib/og/relation/belongs_to.rb +0 -2
  34. data/lib/og/relation/has_many.rb +27 -4
  35. data/lib/og/relation/joins_many.rb +41 -14
  36. data/lib/og/relation/many_to_many.rb +10 -0
  37. data/lib/og/relation/refers_to.rb +22 -5
  38. data/lib/og/store.rb +80 -86
  39. data/lib/og/store/sql.rb +710 -713
  40. data/lib/og/store/sql/evolution.rb +119 -0
  41. data/lib/og/store/sql/join.rb +155 -0
  42. data/lib/og/store/sql/utils.rb +149 -0
  43. data/lib/og/test/assertions.rb +1 -3
  44. data/lib/og/test/testcase.rb +0 -2
  45. data/lib/og/types.rb +2 -5
  46. data/lib/og/validation.rb +6 -9
  47. data/test/{og/mixin → glue}/tc_hierarchical.rb +3 -13
  48. data/test/glue/tc_og_paginate.rb +47 -0
  49. data/test/{og/mixin → glue}/tc_optimistic_locking.rb +2 -12
  50. data/test/{og/mixin → glue}/tc_orderable.rb +15 -23
  51. data/test/glue/tc_orderable2.rb +47 -0
  52. data/test/glue/tc_revisable.rb +3 -3
  53. data/test/{og/mixin → glue}/tc_taggable.rb +20 -10
  54. data/test/{og/mixin → glue}/tc_timestamped.rb +2 -12
  55. data/test/glue/tc_webfile.rb +36 -0
  56. data/test/og/CONFIG.rb +8 -11
  57. data/test/og/multi_validations_model.rb +14 -0
  58. data/test/og/store/tc_filesys.rb +3 -1
  59. data/test/og/store/tc_kirby.rb +16 -13
  60. data/test/og/store/tc_sti.rb +11 -11
  61. data/test/og/store/tc_sti2.rb +79 -0
  62. data/test/og/tc_build.rb +41 -0
  63. data/test/og/tc_cacheable.rb +3 -2
  64. data/test/og/tc_has_many.rb +96 -0
  65. data/test/og/tc_inheritance.rb +6 -4
  66. data/test/og/tc_joins_many.rb +93 -0
  67. data/test/og/tc_multi_validations.rb +5 -7
  68. data/test/og/tc_multiple.rb +7 -6
  69. data/test/og/tc_override.rb +13 -7
  70. data/test/og/tc_primary_key.rb +30 -0
  71. data/test/og/tc_relation.rb +8 -14
  72. data/test/og/tc_reldelete.rb +163 -0
  73. data/test/og/tc_reverse.rb +17 -14
  74. data/test/og/tc_scoped.rb +3 -11
  75. data/test/og/tc_setup.rb +13 -11
  76. data/test/og/tc_store.rb +21 -28
  77. data/test/og/tc_validation2.rb +2 -2
  78. data/test/og/tc_validation_loop.rb +17 -15
  79. metadata +109 -103
  80. data/INSTALL +0 -91
  81. data/ProjectInfo +0 -51
  82. data/README +0 -177
  83. data/doc/config.txt +0 -28
  84. data/examples/README +0 -23
  85. data/examples/mysql_to_psql.rb +0 -71
  86. data/examples/run.rb +0 -271
  87. data/lib/glue/tree.rb +0 -218
  88. data/lib/og/store/alpha/filesys.rb +0 -110
  89. data/lib/og/store/alpha/memory.rb +0 -295
  90. data/lib/og/store/alpha/sqlserver.rb +0 -256
  91. data/lib/og/store/kirby.rb +0 -490
  92. data/lib/og/store/mysql.rb +0 -415
  93. data/lib/og/store/psql.rb +0 -875
  94. data/lib/og/store/sqlite.rb +0 -348
  95. data/lib/og/store/sqlite2.rb +0 -241
  96. data/setup.rb +0 -1585
  97. data/test/og/tc_sti_find.rb +0 -35
@@ -1,256 +0,0 @@
1
- # WARNING:
2
- # This store is not converted to the latest Og codebase.
3
- # DO NOT USE YET!
4
-
5
- begin
6
- require 'dbi'
7
- rescue Object => ex
8
- Logger.error 'Ruby-DBI bindings not present or ADO support not available.'
9
- Logger.error ex
10
- end
11
-
12
- require 'og/store/sql'
13
-
14
- # Customize the standard SqlServer resultset to make
15
- # more compatible with Og.
16
- =begin
17
- class Sqlserver::Result
18
- def blank?
19
- 0 == num_rows
20
- end
21
-
22
- alias_method :next, :fetch_row
23
-
24
- def each_row
25
- each do |row|
26
- yield(row, 0)
27
- end
28
- end
29
-
30
- def first_value
31
- val = fetch_row[0]
32
- free
33
- return val
34
- end
35
-
36
- alias_method :close, :free
37
- end
38
- =end
39
-
40
- module Og
41
-
42
- module SqlserverUtils
43
- include SqlUtils
44
-
45
- def escape(str)
46
- return nil unless str
47
- return Sqlserver.quote(str)
48
- end
49
-
50
- def quote(val)
51
- case val
52
- when Fixnum, Integer, Float
53
- val ? val.to_s : 'NULL'
54
- when String
55
- val ? "'#{escape(val)}'" : 'NULL'
56
- when Time
57
- val ? "'#{timestamp(val)}'" : 'NULL'
58
- when Date
59
- val ? "'#{date(val)}'" : 'NULL'
60
- when TrueClass
61
- val ? "'1'" : 'NULL'
62
- else
63
- # gmosx: keep the '' for nil symbols.
64
- val ? escape(val.to_yaml) : ''
65
- end
66
- end
67
- end
68
-
69
- # A Store that persists objects into a Sqlserver database.
70
- # To read documentation about the methods, consult the documentation
71
- # for SqlStore and Store.
72
-
73
- class SqlserverStore < SqlStore
74
- extend SqlserverUtils
75
- include SqlserverUtils
76
-
77
- def self.create(options)
78
- raise 'Not implemented'
79
- end
80
-
81
- def self.destroy(options)
82
- raise 'Not implemented'
83
- end
84
-
85
- def initialize(options)
86
- super
87
-
88
- begin
89
- @conn = DBI.connect("DBI:ADO:Provider=SQLOLEDB;Data Source=#{options[:address]};Initial Catalog=#{options[:name]};User Id=#{options[:user]};Password=#{options[:password]};")
90
- rescue => ex
91
- # gmosx, FIXME: drak, fix this!
92
- if ex.to_s =~ /database .* does not exist/i
93
- Logger.info "Database '#{options[:name]}' not found!"
94
- self.class.create(options)
95
- retry
96
- end
97
- raise
98
- end
99
- end
100
-
101
- def close
102
- @conn.disconnect
103
- super
104
- end
105
-
106
- def enchant(klass, manager)
107
- klass.property :oid, Fixnum, :sql => 'integer AUTO_INCREMENT PRIMARY KEY'
108
- super
109
- end
110
-
111
- def query(sql)
112
- # Logger.debug sql if $DBG
113
- return @conn.select_all(sql)
114
- rescue => ex
115
- handle_sql_exception(ex, sql)
116
- end
117
-
118
- def exec(sql)
119
- # Logger.debug sql if $DBG
120
- @conn.execute(sql).finish
121
- rescue => ex
122
- handle_sql_exception(ex, sql)
123
- end
124
-
125
- private
126
-
127
- def create_table(klass)
128
- fields = fields_for_class(klass)
129
-
130
- sql = "CREATE TABLE #{klass::OGTABLE} (#{fields.join(', ')}"
131
-
132
- # Create table constraints.
133
-
134
- if klass.__meta and constraints = klass.__meta[:sql_constraint]
135
- sql << ", #{constraints.join(', ')}"
136
- end
137
-
138
- sql << ");"
139
-
140
- # Create indices.
141
-
142
- if klass.__meta and indices = klass.__meta[:index]
143
- for data in indices
144
- idx, options = *data
145
- idx = idx.to_s
146
- pre_sql, post_sql = options[:pre], options[:post]
147
- idxname = idx.gsub(/ /, "").gsub(/,/, "_").gsub(/\(.*\)/, "")
148
- sql << " CREATE #{pre_sql} INDEX #{klass::OGTABLE}_#{idxname}_idx #{post_sql} ON #{klass::OGTABLE} (#{idx});"
149
- end
150
- end
151
-
152
- conn.query_with_result = false
153
-
154
- begin
155
- conn.query(sql)
156
- Logger.info "Created table '#{klass::OGTABLE}'."
157
- rescue => ex
158
- # gmosx: any idea how to better test this?
159
- if ex.errno == 1050 # table already exists.
160
- Logger.debug 'Table already exists' if $DBG
161
- return
162
- else
163
- raise
164
- end
165
- end
166
-
167
- # Create join tables if needed. Join tables are used in
168
- # 'many_to_many' relations.
169
-
170
- if klass.__meta and join_tables = klass.__meta[:join_tables]
171
- for join_table in join_tables
172
- begin
173
- conn.query(create_join_table_sql(join_table))
174
- rescue => ex
175
- # gmosx: any idea how to better test this?
176
- if ex.errno == 1050 # table already exists.
177
- Logger.debug 'Join table already exists' if $DBG
178
- else
179
- raise
180
- end
181
- end
182
- end
183
- end
184
- end
185
-
186
- def create_field_map(klass)
187
- conn.query_with_result = true
188
- res = @conn.query "SELECT * FROM #{klass::OGTABLE} LIMIT 1"
189
- map = {}
190
-
191
- res.num_fields.times do |i|
192
- map[res.fetch_field.name.intern] = i
193
- end
194
-
195
- return map
196
- ensure
197
- res.close if res
198
- end
199
-
200
- def write_prop(p)
201
- if p.klass.ancestors.include?(Integer)
202
- return "#\{@#{p.symbol} || 'NULL'\}"
203
- elsif p.klass.ancestors.include?(Float)
204
- return "#\{@#{p.symbol} || 'NULL'\}"
205
- elsif p.klass.ancestors.include?(String)
206
- return %|#\{@#{p.symbol} ? "'#\{#{self.class}.escape(@#{p.symbol})\}'" : 'NULL'\}|
207
- elsif p.klass.ancestors.include?(Time)
208
- return %|#\{@#{p.symbol} ? "'#\{#{self.class}.timestamp(@#{p.symbol})\}'" : 'NULL'\}|
209
- elsif p.klass.ancestors.include?(Date)
210
- return %|#\{@#{p.symbol} ? "'#\{#{self.class}.date(@#{p.symbol})\}'" : 'NULL'\}|
211
- elsif p.klass.ancestors.include?(TrueClass)
212
- return "#\{@#{p.symbol} ? \"'1'\" : 'NULL' \}"
213
- else
214
- # gmosx: keep the '' for nil symbols.
215
- return %|#\{@#{p.symbol} ? "'#\{#{self.class}.escape(@#{p.symbol}.to_yaml)\}'" : "''"\}|
216
- end
217
- end
218
-
219
- def read_prop(p, col)
220
- if p.klass.ancestors.include?(Integer)
221
- return "res[#{col} + offset].to_i"
222
- elsif p.klass.ancestors.include?(Float)
223
- return "res[#{col} + offset].to_f"
224
- elsif p.klass.ancestors.include?(String)
225
- return "res[#{col} + offset]"
226
- elsif p.klass.ancestors.include?(Time)
227
- return "#{self.class}.parse_timestamp(res[#{col} + offset])"
228
- elsif p.klass.ancestors.include?(Date)
229
- return "#{self.class}.parse_date(res[#{col} + offset])"
230
- elsif p.klass.ancestors.include?(TrueClass)
231
- return "('0' != res[#{col} + offset])"
232
- else
233
- return "YAML.load(res[#{col} + offset])"
234
- end
235
- end
236
-
237
- def eval_og_insert(klass)
238
- props = klass.properties
239
- values = props.collect { |p| write_prop(p) }.join(',')
240
-
241
- sql = "INSERT INTO #{klass::OGTABLE} (#{props.collect {|p| p.symbol.to_s}.join(',')}) VALUES (#{values})"
242
-
243
- klass.class_eval %{
244
- def og_insert(store)
245
- #{::Aspects.gen_advice_code(:og_insert, klass.advices, :pre) if klass.respond_to?(:advices)}
246
- store.conn.query_with_result = false
247
- store.conn.query "#{sql}"
248
- @#{klass.pk_symbol} = store.conn.insert_id
249
- #{::Aspects.gen_advice_code(:og_insert, klass.advices, :post) if klass.respond_to?(:advices)}
250
- end
251
- }
252
- end
253
-
254
- end
255
-
256
- end
@@ -1,490 +0,0 @@
1
- begin
2
- require 'kirbybase'
3
- rescue Object => ex
4
- Logger.error "KirbyBase is not installed. Please run 'gem install KirbyBase'"
5
- Logger.error ex
6
- end
7
-
8
- require 'fileutils'
9
-
10
- require 'og/store/sql'
11
-
12
- module Og
13
-
14
- #Fix for schema inheritance. Instead of inferring the property on each
15
- #access of the database we add it explicitly when the marker module
16
- #is imported. Currently this fix is applied for this store only. The
17
- #other stores should be similarly altered.
18
- module SchemaInheritanceBase
19
- property :ogtype, String
20
- end
21
-
22
- # A Store that persists objects into a KirbyBase database.
23
- # To read documentation about the methods, consult the documentation
24
- # for SqlStore and Store.
25
-
26
- class KirbyStore < SqlStore
27
-
28
- Text = :Text
29
-
30
- # Override if needed.
31
-
32
- def self.base_dir(options)
33
- options[:base_dir] || './kirbydb'
34
- end
35
-
36
- def self.destroy(options)
37
- begin
38
- FileUtils.rm_rf(base_dir(options))
39
- super
40
- rescue Object
41
- Logger.info 'Cannot drop database!'
42
- end
43
- end
44
-
45
- def initialize(options)
46
- super
47
-
48
- @typemap = {
49
- Integer => :Integer,
50
- Fixnum => :Integer,
51
- Float => :Float,
52
- String => :String,
53
- Time => :Time,
54
- Date => :Date,
55
- TrueClass => :Boolean,
56
- FalseClass => :Boolean,
57
- Object => :Object,
58
- Array => :String,
59
- Hash => :String
60
- }
61
-
62
- mode = options[:mode] || :local
63
-
64
- if mode == :client
65
- # Use a client/server configuration.
66
- @conn = KirbyBase.new(:client, options[:address], options[:port])
67
- else
68
- # Use an in-process configuration.
69
- base_dir = self.class.base_dir(options)
70
- FileUtils.mkdir_p(base_dir) unless File.exist?(base_dir)
71
- @conn = KirbyBase.new(
72
- :local,
73
- nil, nil,
74
- base_dir,
75
- options[:ext] || '.tbl'
76
- )
77
- end
78
- end
79
-
80
- def close
81
- # Nothing to do.
82
- super
83
- end
84
-
85
- def enchant(klass, manager)
86
- klass.send(:attr_accessor, :oid)
87
- klass.send(:alias_method, :recno, :oid)
88
- klass.send(:alias_method, :recno=, :oid=)
89
-
90
- super
91
- end
92
-
93
- def get_table(klass)
94
- @conn.get_table(klass.table.to_sym)
95
- end
96
-
97
- # :section: Lifecycle methods.
98
-
99
- def load(pk, klass)
100
- convert_to_instance(klass, get_table(klass)[pk.to_i])
101
- end
102
-
103
- #convert the given record into an instance of the given class.
104
- #Supports schema_inheritance.
105
- def convert_to_instance(klass, record)
106
- return nil unless record
107
-
108
- if(klass.schema_inheritance?)
109
- obj = eval("#{record.ogtype}.allocate")
110
- elsif
111
- obj = klass.allocate
112
- end
113
-
114
- record.each_pair do |k, v|
115
- assign_sym = (k.to_s + '=').to_sym
116
- if (v && obj.respond_to?(assign_sym))
117
- obj.method(assign_sym).call(v)
118
- end
119
- end
120
- obj
121
- end
122
-
123
- alias_method :exist?, :load
124
-
125
- def reload(obj, pk)
126
- raise 'Cannot reload unmanaged object' unless obj.saved?
127
- new_obj = load(pk, obj.class)
128
- #TODO: replace obj with new_obj
129
- end
130
-
131
- def find(options)
132
- #FIXME: this currently overrides options with scope; this should
133
- #work the other way around
134
- if scope = options[:class].get_scope
135
- scope = scope.dup
136
- options.update(scope)
137
- end
138
- query(options)
139
- end
140
-
141
- def find_one(options)
142
- query(options).first
143
- end
144
-
145
- #--
146
- # FIXME: optimize me!
147
- #++
148
-
149
- def count(options)
150
- find(options).size
151
- end
152
-
153
- #--
154
- # FIXME: optimize me!
155
- #
156
- #++
157
-
158
- def aggregate(term = 'COUNT(*)', options = {})
159
- case term.upcase
160
- when /COUNT/
161
- count(options)
162
- when /MIN/
163
- term =~ /MIN\((\w*)\)/
164
- find_column_values($1, options){|result| result.min}
165
- when /MAX/
166
- term =~ /MAX\((\w*)\)/
167
- find_column_values($1, options) {|result| result.max}
168
- when /AVG/
169
- term =~ /AVG\((\w*)\)/
170
- values = find_column_values($1, options) do |results|
171
- results.inject(0.0){|sum, result| sum + result} / results.size
172
- end
173
- when /SUM/
174
- term =~ /SUM\((\w*)\)/
175
- values = find_column_values($1, options) do |results|
176
- results.inject(0.0){|sum, result| sum + result}
177
- end
178
- end
179
- end
180
- alias_method :calculate, :aggregate
181
-
182
- #find an array of values for the given column; or a hash of arrays
183
- #if there is a group by expression. Takes an optional block
184
- #that operates on each value in the array.
185
-
186
- def find_column_values(column_name, options)
187
- options[:column] = column_name
188
- results = find(options)
189
- if (block_given?)
190
- if (options[:group]) #hash of arrays
191
- results.values.collect { |group| yield group}
192
- else
193
- yield results
194
- end
195
- end
196
- end
197
-
198
- #needs refactoring, badly
199
- def query(options)
200
- Logger.debug "Querying with #{options.inspect}." if $DBG
201
-
202
- klass = options[:class]
203
-
204
- table = get_table(klass)
205
- result_set = []
206
- if (klass.schema_inheritance_child?)
207
- type_condition = 'ogtype = \'' + klass.name + '\''
208
- condition = options[:condition]
209
-
210
- if (condition)
211
- #if there is already an ogtype condition do nothing
212
- if (condition !~ /ogtype/)
213
- options[:condition] = condition + " and #{type_condition}"
214
- end
215
- else
216
- options[:condition] = type_condition
217
- end
218
- end
219
-
220
- #FIXME: add support for select statements with mathematics (tc_select)
221
- #as well as 'from table' statements
222
- if condition = options[:condition] || options[:where]
223
- condition = condition.is_a?(Array) ? prepare_statement(condition) : condition
224
- condition.gsub!(/ogtc_resolveoptions\w*\./, '')
225
- condition.gsub!(/([^><])=/, '\1==')
226
- condition.gsub!(/ OR /, ' or ')
227
- condition.gsub!(/ AND /, ' and ')
228
- condition.gsub!(/LIKE '(.*?)'/, '=~ /\1/')
229
- condition.gsub!(/\%/, '')
230
- condition.gsub!(/(\w*?)\s?([=><])(.)/, 'o.\1 \2\3')
231
- condition.gsub!(/o.oid/, 'o.recno')
232
- if(condition =~ /LIMIT (\d+)/)
233
- condition.gsub!(/LIMIT (\d+)/, '')
234
- options[:limit] = $1.to_i
235
- end
236
- result_set = eval("table.select do |o| #{condition} end")
237
- else
238
- result_set = table.select
239
- end
240
-
241
- if (order = options[:order])
242
- order = order.to_s
243
- desc = (order =~ /DESC/)
244
- order = order.gsub(/DESC/, '').gsub(/ASC/, '')
245
- eval("result_set.sort { |x, y| x.#{order} <=> y.#{order} }")
246
- result_set.reverse! if desc
247
- end
248
-
249
- if (options[:limit])
250
- limit = options[:limit]
251
- if (result_set.size > limit)
252
- result_set = result_set[0, limit]
253
- end
254
- end
255
-
256
- if (column = options[:column])
257
- if (!result_set.methods.include?(column))
258
- return []
259
- end
260
- begin
261
- #if there is a group by expression on a valid column
262
- #gather arrays of values
263
- if ((group = options[:group]) && result_set.method(options[:group]))
264
- #store an empty array under the key if there is no value
265
- groups = Hash.new {|groups, key| groups[key] = []}
266
- result_set.each do |object|
267
- groups[object.method(group).call] << object.method(column).call
268
- end
269
- return groups
270
- end
271
- rescue NameError => error
272
- Logger.warn 'Attempt to group by missing column:'
273
- Logger.warn 'Column \'' + options[:group].to_s + '\' does not exist on table \'' + klass.name + '\''
274
- raise error
275
- end
276
-
277
- #when called on a resultset a column method returns an array of that
278
- #column's values
279
- return result_set.method(column).call()
280
- end
281
-
282
- result_set.map { |object| convert_to_instance(klass, object) }
283
- end
284
-
285
- def start
286
- # nop
287
- end
288
-
289
- # Commit a transaction.
290
-
291
- def commit
292
- # nop, not supported?
293
- end
294
-
295
- # Rollback a transaction.
296
-
297
- def rollback
298
- # nop, not supported?
299
- end
300
-
301
- def sql_update(sql)
302
- # nop, not supported.
303
- end
304
-
305
- #FIXME: this method relies on naming conventions and does not account
306
- #for more than one set of '::' marks.
307
-
308
- def join(obj1, obj2, table, options = nil)
309
- first, second = join_object_ordering(obj1, obj2)
310
-
311
- options[get_key(first)] = first.pk
312
- options[get_key(second)] = second.pk
313
-
314
- @conn.get_table(table.to_sym).insert(options)
315
- end
316
-
317
- #retrieve the join table key name for the object
318
-
319
- def get_key(obj)
320
- obj.class.name =~ /::(\w*)/
321
- ($1.downcase + '_oid').to_sym
322
- end
323
-
324
- def unjoin(obj1, obj2, table)
325
- first, second = join_object_ordering(obj1, obj2)
326
-
327
- @conn.get_table(table.to_sym).delete do |r|
328
- r.send(get_key(first)) == first.pk and
329
- r.send(get_key(second)) == second.pk
330
- end
331
- end
332
-
333
- private
334
-
335
- def typemap(key)
336
- @typemap[key]
337
- end
338
-
339
- def create_table(klass)
340
- if @conn.table_exists?(klass.table.to_sym)
341
- get_table(klass).pack # Kirby specific method of database cleanup.
342
-
343
- field_names = field_names_for_class(klass)
344
-
345
- actual_fields = get_table(klass).field_names
346
-
347
- field_names.each do |needed_field|
348
- next if actual_fields.include?(needed_field)
349
-
350
- if @options[:evolve_schema] == true
351
- Logger.debug "Adding field '#{needed_field}' to '#{klass.table}'" if $DBG
352
- field_type = typemap(klass.properties[needed_field].klass)
353
- if get_table(klass).respond_to?(:add_column)
354
- get_table(klass).add_column(needed_field, field_type)
355
- else
356
- @conn.add_table_column(klass.table, needed_field, field_type)
357
- end
358
- else
359
- Logger.warn "Table '#{klass.table}' is missing field '#{needed_field}' and :evolve_schema is not set to true!"
360
- end
361
- end
362
-
363
- actual_fields.each do |obsolete_field|
364
- next if field_names.include?(obsolete_field) || obsolete_field == :recno
365
- if @options[:evolve_schema] == true and @options[:evolve_schema_cautious] == false
366
- Logger.debug "Removing obsolete field '#{obsolete_field}' from '#{klass.table}'" if $DBG
367
- if get_table(klass).respond_to?(:drop_column)
368
- get_table(klass).drop_column(obsolete_field)
369
- else
370
- @conn.drop_table_column(klass.table, obsolete_field)
371
- end
372
- else
373
- Logger.warn "You have an obsolete field '#{obsolete_field}' on table '#{klass.table}' and :evolve_schema is not set or is in cautious mode!"
374
- end
375
- end
376
- else
377
- Logger.debug "Creating table '#{klass.table}'" if $DBG
378
- fields = fields_for_class(klass)
379
- table = @conn.create_table(klass.table.to_sym, *fields)
380
- end
381
-
382
- =begin
383
- # Create join tables if needed. Join tables are used in
384
- # 'many_to_many' relations.
385
-
386
- if join_tables = klass.ann.self[:join_tables]
387
- for info in join_tables
388
- unless @conn.table_exists?(info[:table].to_sym)
389
- @conn.create_table(info[:table].to_sym, *create_join_table_sql(info))
390
- Logger.debug "Created jointable '#{info[:table]}'." if $DBG
391
- end
392
- end
393
- end
394
- =end
395
- end
396
-
397
- def create_join_table_sql(join_info)
398
- [join_info[:first_key].to_sym, :Integer, join_info[:second_key].to_sym, :Integer]
399
- end
400
-
401
- def drop_table(klass)
402
- @conn.drop_table(klass.table) if @conn.table_exists?(klass.table)
403
- end
404
-
405
- def field_names_for_class(klass)
406
- properties = get_properties_for_class(klass)
407
- properties.values.map {|p| p.symbol }
408
- end
409
-
410
- def fields_for_class(klass)
411
- fields = []
412
-
413
- get_properties_for_class(klass).values.each do |p|
414
- klass.index(p.symbol) if p.index
415
-
416
- fields << p.symbol
417
-
418
- type = typemap(p.klass)
419
-
420
- fields << type
421
- end
422
-
423
- fields
424
- end
425
-
426
- def eval_og_insert(klass)
427
- pk = klass.primary_key.symbol
428
-
429
- fields = get_properties_for_class(klass).keys
430
- fields = fields.map { |f| ":#{f} => (self.respond_to?(:#{f}) ? self.#{f} : nil)"}
431
- field_string = fields.join(', ')
432
- klass.class_eval %{
433
- def og_insert(store)
434
- @ogtype = #{klass}
435
- #{::Aspects.gen_advice_code(:og_insert, klass.advices, :pre) if klass.respond_to?(:advices)}
436
- Logger.debug "Inserting \#{self}." if $DBG
437
- @#{pk} = store.get_table(#{klass}).insert({#{field_string}})
438
- #{::Aspects.gen_advice_code(:og_insert, klass.advices, :post) if klass.respond_to?(:advices)}
439
- end
440
- }
441
- end
442
-
443
- # Compile the og_update method for the class.
444
-
445
- def eval_og_update(klass)
446
- pk = klass.pk_symbol
447
- updates = klass.properties.keys.collect { |p| ":#{p} => @#{p}" }
448
-
449
- klass.module_eval %{
450
- def og_update(store, options = nil)
451
- #{::Aspects.gen_advice_code(:og_update, klass.advices, :pre) if klass.respond_to?(:advices)}
452
- store.get_table(#{klass}).update { |r| r.recno == #{pk} }.set(#{updates.join(', ')})
453
- #{::Aspects.gen_advice_code(:og_update, klass.advices, :post) if klass.respond_to?(:advices)}
454
- end
455
- }
456
- end
457
-
458
- def eval_og_read(klass)
459
- klass.module_eval %{
460
- def og_read(res, row = 0, offset = 0)
461
- #{::Aspects.gen_advice_code(:og_read, klass.advices, :pre) if klass.respond_to?(:advices)}
462
- #{::Aspects.gen_advice_code(:og_read, klass.advices, :post) if klass.respond_to?(:advices)}
463
- end
464
- }
465
- end
466
-
467
- def eval_og_delete(klass)
468
- klass.module_eval %{
469
- def og_delete(store, pk, cascade = true)
470
- pk ||= self.pk
471
- pk = pk.to_i
472
- #{::Aspects.gen_advice_code(:og_delete, klass.advices, :pre) if klass.respond_to?(:advices)}
473
- table = store.get_table(self.class)
474
- transaction do |tx|
475
- table.delete { |r| r.recno == pk }
476
- if cascade and #{klass}.ann.self[:descendants]
477
- #{klass}.ann.self.descendants.each do |dclass, foreign_key|
478
- dtable = store.get_table(dclass)
479
- dtable.delete { |r| foreign_key == pk }
480
- end
481
- end
482
- end
483
- #{::Aspects.gen_advice_code(:og_delete, klass.advices, :post) if klass.respond_to?(:advices)}
484
- end
485
- }
486
- end
487
- end
488
- end
489
-
490
- # * George Moschovitis <gm@navel.gr>