og 0.31.0 → 0.40.0

Sign up to get free protection for your applications and to get access to all the features.
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>