nitro 0.9.3 → 0.9.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/ChangeLog +64 -0
  2. data/RELEASES +13 -0
  3. data/examples/blog/README +1 -1
  4. data/examples/blog/conf/app.conf.rb +2 -6
  5. data/examples/blog/conf/lhttpd.conf +1 -1
  6. data/examples/blog/lib/blog/controller.rb +2 -2
  7. data/examples/blog/root/style.css +0 -2
  8. data/examples/flash/conf/app.conf.rb +2 -4
  9. data/examples/no_xsl_blog/README +9 -0
  10. data/examples/no_xsl_blog/conf/app.conf.rb +5 -8
  11. data/examples/no_xsl_blog/conf/lhttpd.conf +1 -1
  12. data/examples/no_xsl_blog/lib/blog/controller.rb +2 -3
  13. data/examples/no_xsl_blog/lib/blog/template.rb +3 -3
  14. data/examples/no_xsl_blog/root/style.css +0 -2
  15. data/examples/og/mock_example.rb +1 -4
  16. data/examples/og/mysql_to_psql.rb +2 -4
  17. data/examples/tiny/README +1 -1
  18. data/examples/tiny/conf/app.conf.rb +2 -6
  19. data/examples/tiny/conf/lhttpd.conf +1 -1
  20. data/examples/wee_style/README +10 -0
  21. data/examples/wee_style/wee.rb +50 -0
  22. data/lib/glue/cache.rb +32 -34
  23. data/lib/glue/number.rb +3 -9
  24. data/lib/glue/time.rb +14 -22
  25. data/lib/glue/validation.rb +2 -4
  26. data/lib/nitro.rb +1 -3
  27. data/lib/nitro/adaptors/fastcgi.rb +10 -0
  28. data/lib/nitro/adaptors/webrick.rb +27 -11
  29. data/lib/nitro/builders/rss.rb +31 -11
  30. data/lib/nitro/builders/xhtml.rb +2 -8
  31. data/lib/nitro/context.rb +3 -1
  32. data/lib/nitro/dispatcher.rb +21 -4
  33. data/lib/nitro/filters.rb +12 -12
  34. data/lib/nitro/version.rb +1 -1
  35. data/lib/og/backend.rb +36 -40
  36. data/lib/og/backends/psql.rb +7 -7
  37. data/lib/og/backends/sqlite.rb +383 -0
  38. data/lib/og/connection.rb +34 -34
  39. data/lib/og/meta.rb +8 -0
  40. data/lib/og/version.rb +2 -4
  41. data/test/nitro/builders/tc_rss.rb +22 -0
  42. data/test/nitro/tc_dispatcher.rb +6 -1
  43. metadata +7 -2
@@ -10,6 +10,6 @@ module Nitro
10
10
 
11
11
  # The version of the server.
12
12
 
13
- Version = '0.9.3'
13
+ Version = '0.9.5'
14
14
 
15
15
  end
@@ -1,73 +1,69 @@
1
- # code:
2
1
  # * George Moschovitis <gm@navel.gr>
3
- #
4
- # (c) 2004 Navel, all rights reserved.
2
+ # (c) 2004-2005 Navel, all rights reserved.
5
3
  # $Id: backend.rb 202 2005-01-17 10:44:13Z gmosx $
6
4
 
7
- require "yaml"
5
+ require 'yaml'
8
6
 
9
- require "og/connection"
7
+ require 'og/connection'
10
8
 
11
9
  class Og
12
10
 
13
- # = Backend
14
- #
15
11
  # Abstract backend. A backend communicates with the RDBMS.
16
12
  # This is the base class for the various backend implementations.
17
- #
13
+
18
14
  class Backend
19
15
 
20
16
  # The actual connection to the database
21
17
  attr_accessor :conn
22
18
 
23
19
  # Intitialize the connection to the RDBMS.
24
- #
20
+
25
21
  def initialize(config)
26
22
  raise "Not implemented"
27
23
  end
28
24
 
29
25
  # Close the connection to the RDBMS.
30
- #
26
+
31
27
  def close()
32
28
  @conn.close()
33
29
  end
34
30
 
35
- # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
36
- # Utilities
31
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
32
+ # :section: Utilities
37
33
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
38
34
 
39
35
  # Encode the name of the klass as an sql safe string.
40
36
  # The Module separators are replaced with _ and NOT stripped
41
37
  # out so that we can convert back to the original notation if
42
38
  # needed. The leading module if available is removed.
43
- #
39
+
44
40
  def self.encode(klass)
45
41
  "#{klass.name.gsub(/^.*::/, "")}".gsub(/::/, "_").downcase
46
42
  end
47
43
 
48
44
  # The name of the SQL table where objects of this class
49
45
  # are stored.
50
- #
46
+
51
47
  def self.table(klass)
52
48
  "_#{Og.table_prefix}#{encode(klass)}"
53
49
  end
54
50
 
55
51
  # The name of the join table for the two given classes.
56
- #
52
+
57
53
  def self.join_table(klass1, klass2)
58
54
  "_#{Og.table_prefix}j_#{encode(klass1)}_#{encode(klass2)}"
59
55
  end
60
56
 
61
57
  # Returns the props that will be included in the insert query.
62
58
  # For some backends the oid should be stripped.
63
- #
59
+
64
60
  def self.props_for_insert(klass)
65
61
  klass.__props
66
62
  end
67
63
 
68
64
  # Precompile the insert code for the given class.
69
65
  # The generated code sets the oid when inserting!
70
- #
66
+
71
67
  def self.eval_og_insert(klass)
72
68
  props = props_for_insert(klass)
73
69
 
@@ -104,7 +100,7 @@ class Backend
104
100
 
105
101
  # Precompile the update code for the given class.
106
102
  # Ignore the oid when updating!
107
- #
103
+
108
104
  def self.eval_og_update(klass)
109
105
  props = klass.__props.reject { |p| :oid == p.symbol }
110
106
 
@@ -146,7 +142,7 @@ class Backend
146
142
  # Precompile the code to read objects of the given class
147
143
  # from the backend. In order to allow for changing
148
144
  # field/attribute orders we have to use a field mapping hash.
149
- #
145
+
150
146
  def self.eval_og_deserialize(klass, og)
151
147
  calc_field_index(klass, og)
152
148
 
@@ -169,67 +165,67 @@ class Backend
169
165
  end
170
166
 
171
167
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
172
- # Connection methods.
168
+ # :section: Connection methods.
173
169
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
174
170
 
175
171
  # Create the database.
176
- #
172
+
177
173
  def self.create_db(database, user = nil, password = nil)
178
174
  Logger.info "Creating database '#{database}'."
179
175
  end
180
176
 
181
177
  # Drop the database.
182
- #
178
+
183
179
  def self.drop_db(database, user = nil, password = nil)
184
180
  Logger.info "Dropping database '#{database}'."
185
181
  end
186
182
 
187
183
  # Execute an SQL query and return the result.
188
- #
184
+
189
185
  def query(sql)
190
186
  raise "Not implemented"
191
187
  end
192
188
 
193
189
  # Execute an SQL query, no result returned.
194
- #
190
+
195
191
  def exec(sql)
196
192
  raise "Not implemented"
197
193
  end
198
194
 
199
195
  # Execute an SQL query and return the result. Wrapped in a
200
196
  # rescue block.
201
- #
197
+
202
198
  def safe_query(sql)
203
199
  raise "Not implemented"
204
200
  end
205
201
 
206
202
  # Execute an SQL query, no result returned. Wrapped in a
207
203
  # rescue block.
208
- #
204
+
209
205
  def safe_exec(sql)
210
206
  raise "Not implemented"
211
207
  end
212
208
 
213
209
  # Check if it is a valid resultset.
214
- #
210
+
215
211
  def valid?(res)
216
212
  raise "Not implemented"
217
213
  end
218
214
 
219
215
  # Start a new transaction.
220
- #
216
+
221
217
  def start
222
218
  exec "START TRANSACTION"
223
219
  end
224
220
 
225
221
  # Commit a transaction.
226
- #
222
+
227
223
  def commit
228
224
  exec "COMMIT"
229
225
  end
230
226
 
231
227
  # Rollback transaction.
232
- #
228
+
233
229
  def rollback
234
230
  exec "ROLLBACK"
235
231
  end
@@ -239,7 +235,7 @@ class Backend
239
235
  # If the property has an :sql metadata this overrides the
240
236
  # default mapping. If the property has an :extra_sql metadata
241
237
  # the extra sql is appended after the default mapping.
242
- #
238
+
243
239
  def create_fields(klass, typemap)
244
240
  fields = []
245
241
 
@@ -267,35 +263,35 @@ class Backend
267
263
  # Create the managed object table. The properties of the
268
264
  # object are mapped to the table columns. Additional sql relations
269
265
  # and constrains are created (indicices, sequences, etc).
270
- #
266
+
271
267
  def create_table(klass)
272
268
  return if query("SELECT * FROM #{klass::DBTABLE} LIMIT 1")
273
269
  end
274
270
 
275
271
  # Drop the managed object table
276
- #
272
+
277
273
  def drop_table(klass)
278
274
  exec "DROP TABLE #{klass::DBTABLE}"
279
275
  end
280
276
 
281
277
  # Deserialize one row of the resultset.
282
- #
278
+
283
279
  def deserialize_one(res, klass)
284
- raise "Not implemented"
280
+ raise 'Not implemented'
285
281
  end
286
282
 
287
283
  # Deserialize all rows of the resultset.
288
- #
284
+
289
285
  def deserialize_all(res, klass)
290
- raise "Not implemented"
286
+ raise 'Not implemented'
291
287
  end
292
288
 
293
289
  # Return a single integer value from the resultset.
294
- #
290
+
295
291
  def get_int(res, idx = 0)
296
- raise "Not implemented"
292
+ raise 'Not implemented'
297
293
  end
298
294
 
299
295
  end
300
296
 
301
- end # namespace
297
+ end
@@ -12,7 +12,7 @@ class Og
12
12
  # This backend is compatible with Michael Neumann's postgres-pr
13
13
  # pure ruby driver.
14
14
 
15
- class PsqlBackend < Og::Backend
15
+ class PsqlBackend < Backend
16
16
 
17
17
  # A mapping between Ruby and SQL types.
18
18
 
@@ -101,15 +101,15 @@ class PsqlBackend < Og::Backend
101
101
  elsif p.klass.ancestors.include?(Float)
102
102
  return "#\{@#{p.symbol} || 'NULL'\}"
103
103
  elsif p.klass.ancestors.include?(String)
104
- return "'#\{Og::PsqlBackend.escape(@#{p.symbol})\}'"
104
+ return "'#\{PsqlBackend.escape(@#{p.symbol})\}'"
105
105
  elsif p.klass.ancestors.include?(Time)
106
- return %|#\{@#{p.symbol} ? "'#\{Og::PsqlBackend.timestamp(@#{p.symbol})\}'" : 'NULL'\}|
106
+ return %|#\{@#{p.symbol} ? "'#\{PsqlBackend.timestamp(@#{p.symbol})\}'" : 'NULL'\}|
107
107
  elsif p.klass.ancestors.include?(Date)
108
- return %|#\{@#{p.symbol} ? "'#\{Og::PsqlBackend.date(@#{p.symbol})\}'" : 'NULL'\}|
108
+ return %|#\{@#{p.symbol} ? "'#\{PsqlBackend.date(@#{p.symbol})\}'" : 'NULL'\}|
109
109
  elsif p.klass.ancestors.include?(TrueClass)
110
110
  return "#\{@#{p.symbol} ? \"'t'\" : 'NULL' \}"
111
111
  else
112
- return %|#\{@#{p.symbol} ? "'#\{Og::PsqlBackend.escape(@#{p.symbol}.to_yaml)\}'" : "''"\}|
112
+ return %|#\{@#{p.symbol} ? "'#\{PsqlBackend.escape(@#{p.symbol}.to_yaml)\}'" : "''"\}|
113
113
  end
114
114
  end
115
115
 
@@ -124,9 +124,9 @@ class PsqlBackend < Og::Backend
124
124
  elsif p.klass.ancestors.include?(String)
125
125
  return "res.getvalue(tuple, #{idx})"
126
126
  elsif p.klass.ancestors.include?(Time)
127
- return "Og::PsqlBackend.parse_timestamp(res.getvalue(tuple, #{idx}))"
127
+ return "PsqlBackend.parse_timestamp(res.getvalue(tuple, #{idx}))"
128
128
  elsif p.klass.ancestors.include?(Date)
129
- return "Og::PsqlBackend.parse_date(res.getvalue(tuple, #{idx}))"
129
+ return "PsqlBackend.parse_date(res.getvalue(tuple, #{idx}))"
130
130
  elsif p.klass.ancestors.include?(TrueClass)
131
131
  return %|('t' == res.getvalue(tuple, #{idx}))|
132
132
  else
@@ -0,0 +1,383 @@
1
+ # * George Moschovitis <gm@navel.gr>
2
+ # (c) 2004-2005 Navel, all rights reserved.
3
+ # $Id$
4
+
5
+ require 'sqlite'
6
+
7
+ require 'og/backend'
8
+
9
+ class Og
10
+
11
+ # Implements an SQLite powered backend.
12
+
13
+ class SqliteBackend < Backend
14
+
15
+ # A mapping between Ruby and SQL types.
16
+
17
+ TYPEMAP = {
18
+ Integer => 'integer',
19
+ Fixnum => 'integer',
20
+ Float => 'float',
21
+ String => 'text',
22
+ Time => 'timestamp',
23
+ Date => 'date',
24
+ TrueClass => 'boolean',
25
+ Object => 'text',
26
+ Array => 'text',
27
+ Hash => 'text'
28
+ }
29
+
30
+ # Intitialize the connection to the RDBMS.
31
+
32
+ def initialize(config)
33
+ begin
34
+ @conn = SQLite::Database.new(config[:database])
35
+ rescue => ex
36
+ # gmosx: any idea how to better test this?
37
+ if ex.to_s =~ /database .* does not exist/i
38
+ Logger.info "Database '#{config[:database]}' not found!"
39
+ SqliteBackend.create_db(config[:database], config[:user])
40
+ retry
41
+ end
42
+ raise
43
+ end
44
+ end
45
+
46
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
47
+ # Utilities
48
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
49
+
50
+ # Escape an SQL string
51
+
52
+ def self.escape(str)
53
+ return nil unless str
54
+ return str.gsub( /'/, "''" )
55
+ end
56
+
57
+ # Convert a ruby time to an sql timestamp.
58
+ # TODO: Optimize this
59
+
60
+ def self.timestamp(time = Time.now)
61
+ return nil unless time
62
+ return time.strftime("%Y-%m-%d %H:%M:%S")
63
+ end
64
+
65
+ # Output YYY-mm-dd
66
+ # TODO: Optimize this
67
+
68
+ def self.date(date)
69
+ return nil unless date
70
+ return "#{date.year}-#{date.month}-#{date.mday}"
71
+ end
72
+
73
+ # Parse sql datetime
74
+ # TODO: Optimize this
75
+
76
+ def self.parse_timestamp(str)
77
+ return Time.parse(str)
78
+ end
79
+
80
+ # Input YYYY-mm-dd
81
+ # TODO: Optimize this
82
+
83
+ def self.parse_date(str)
84
+ return nil unless str
85
+ return Date.strptime(str)
86
+ end
87
+
88
+ # Return an sql string evaluator for the property.
89
+ # No need to optimize this, used only to precalculate code.
90
+ # YAML is used to store general Ruby objects to be more
91
+ # portable.
92
+ #
93
+ # FIXME: add extra handling for float.
94
+
95
+ def self.write_prop(p)
96
+ if p.klass.ancestors.include?(Integer)
97
+ return "#\{@#{p.symbol} || 'NULL'\}"
98
+ elsif p.klass.ancestors.include?(Float)
99
+ return "#\{@#{p.symbol} || 'NULL'\}"
100
+ elsif p.klass.ancestors.include?(String)
101
+ return "'#\{SqliteBackend.escape(@#{p.symbol})\}'"
102
+ elsif p.klass.ancestors.include?(Time)
103
+ return %|#\{@#{p.symbol} ? "'#\{SqliteBackend.timestamp(@#{p.symbol})\}'" : 'NULL'\}|
104
+ elsif p.klass.ancestors.include?(Date)
105
+ return %|#\{@#{p.symbol} ? "'#\{SqliteBackend.date(@#{p.symbol})\}'" : 'NULL'\}|
106
+ elsif p.klass.ancestors.include?(TrueClass)
107
+ return "#\{@#{p.symbol} ? \"'t'\" : 'NULL' \}"
108
+ else
109
+ return %|#\{@#{p.symbol} ? "'#\{SqliteBackend.escape(@#{p.symbol}.to_yaml)\}'" : "''"\}|
110
+ end
111
+ end
112
+
113
+ # Return an evaluator for reading the property.
114
+ # No need to optimize this, used only to precalculate code.
115
+
116
+ def self.read_prop(p, idx)
117
+ if p.klass.ancestors.include?(Integer)
118
+ return "res.getvalue(tuple, #{idx}).to_i()"
119
+ elsif p.klass.ancestors.include?(Float)
120
+ return "res.getvalue(tuple, #{idx}).to_f()"
121
+ elsif p.klass.ancestors.include?(String)
122
+ return "res.getvalue(tuple, #{idx})"
123
+ elsif p.klass.ancestors.include?(Time)
124
+ return "PsqlBackend.parse_timestamp(res.getvalue(tuple, #{idx}))"
125
+ elsif p.klass.ancestors.include?(Date)
126
+ return "PsqlBackend.parse_date(res.getvalue(tuple, #{idx}))"
127
+ elsif p.klass.ancestors.include?(TrueClass)
128
+ return %|('t' == res.getvalue(tuple, #{idx}))|
129
+ else
130
+ return "YAML::load(res.getvalue(tuple, #{idx}))"
131
+ end
132
+ end
133
+
134
+ # Returns the code that actually inserts the object into the
135
+ # database. Returns the code as String.
136
+
137
+ def self.insert_code(klass, sql, pre_cb, post_cb)
138
+ %{
139
+ #{pre_cb}
140
+ res = conn.db.query("SELECT nextval('#{klass::DBSEQ}')")
141
+ @oid = res.getvalue(0, 0).to_i
142
+ conn.exec "#{sql}"
143
+ #{post_cb}
144
+ }
145
+ end
146
+
147
+ # generate the mapping of the database fields to the
148
+ # object properties.
149
+
150
+ def self.calc_field_index(klass, og)
151
+ res = og.query "SELECT * FROM #{klass::DBTABLE} LIMIT 1"
152
+ meta = og.managed_classes[klass]
153
+
154
+ for field in res.fields
155
+ meta.field_index[field] = res.fieldnum(field)
156
+ end
157
+ end
158
+
159
+ # Generate the property for oid
160
+
161
+ def self.eval_og_oid(klass)
162
+ klass.class_eval %{
163
+ prop_accessor :oid, Fixnum, :sql => "integer PRIMARY KEY"
164
+ }
165
+ end
166
+
167
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
168
+ # Connection methods.
169
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
170
+
171
+ # Create the database.
172
+
173
+ def self.create_db(database, user = nil, password = nil)
174
+ Logger.info "Creating database '#{database}'."
175
+ `createdb #{database} -U #{user}`
176
+ end
177
+
178
+ # Drop the database.
179
+
180
+ def self.drop_db(database, user = nil, password = nil)
181
+ Logger.info "Dropping database '#{database}'."
182
+ `dropdb #{database} -U #{user}`
183
+ end
184
+
185
+ # Execute an SQL query and return the result.
186
+
187
+ def query(sql)
188
+ Logger.debug sql if $DBG
189
+ return @conn.exec(sql)
190
+ end
191
+
192
+ # Execute an SQL query, no result returned.
193
+
194
+ def exec(sql)
195
+ Logger.debug sql if $DBG
196
+ res = @conn.exec(sql)
197
+ res.clear()
198
+ end
199
+
200
+ # Execute an SQL query and return the result. Wrapped in a rescue
201
+ # block.
202
+
203
+ def safe_query(sql)
204
+ Logger.debug sql if $DBG
205
+ begin
206
+ return @conn.exec(sql)
207
+ rescue => ex
208
+ Logger.error "DB error #{ex}, [#{sql}]"
209
+ Logger.error ex.backtrace
210
+ return nil
211
+ end
212
+ end
213
+
214
+ # Execute an SQL query, no result returned. Wrapped in a rescue
215
+ # block.
216
+
217
+ def safe_exec(sql)
218
+ Logger.debug sql if $DBG
219
+ begin
220
+ res = @conn.exec(sql)
221
+ res.clear()
222
+ rescue => ex
223
+ Logger.error "DB error #{ex}, [#{sql}]"
224
+ Logger.error ex.backtrace
225
+ end
226
+ end
227
+
228
+ # Check if it is a valid resultset.
229
+
230
+ def valid?(res)
231
+ return !(res.nil? or 0 == res.num_tuples)
232
+ end
233
+
234
+ # Create the managed object table. The properties of the
235
+ # object are mapped to the table columns. Additional sql relations
236
+ # and constrains are created (indicices, sequences, etc).
237
+
238
+ def create_table(klass)
239
+ fields = create_fields(klass, TYPEMAP)
240
+
241
+ sql = "CREATE TABLE #{klass::DBTABLE} (#{fields.join(', ')}"
242
+
243
+ # Create table constrains
244
+
245
+ if klass.__meta and constrains = klass.__meta[:sql_constrain]
246
+ sql << ", #{constrains.join(', ')}"
247
+ end
248
+
249
+ sql << ") WITHOUT OIDS;"
250
+
251
+ # Create indices
252
+
253
+ if klass.__meta and indices = klass.__meta[:sql_index]
254
+ for data in indices
255
+ idx, options = *data
256
+ idx = idx.to_s
257
+ pre_sql, post_sql = options[:pre], options[:post]
258
+ idxname = idx.gsub(/ /, "").gsub(/,/, "_").gsub(/\(.*\)/, "")
259
+ sql << " CREATE #{pre_sql} INDEX #{klass::DBTABLE}_#{idxname}_idx #{post_sql} ON #{klass::DBTABLE} (#{idx});"
260
+ end
261
+ end
262
+
263
+ begin
264
+ exec(sql)
265
+ Logger.info "Created table '#{klass::DBTABLE}'."
266
+ rescue => ex
267
+ # gmosx: any idea how to better test this?
268
+ if ex.to_s =~ /relation .* already exists/i
269
+ Logger.debug "Table already exists" if $DBG
270
+ else
271
+ raise
272
+ end
273
+ end
274
+
275
+ # create the sequence for this table. Even if the table
276
+ # uses the oids_seq, attempt to create it. This makes
277
+ # the system more fault tolerant.
278
+
279
+ begin
280
+ exec "CREATE SEQUENCE #{klass::DBSEQ}"
281
+ Logger.info "Created sequence '#{klass::DBSEQ}'."
282
+ rescue => ex
283
+ # gmosx: any idea how to better test this?
284
+ if ex.to_s =~ /relation .* already exists/i
285
+ Logger.debug "Sequence already exists" if $DBG
286
+ else
287
+ raise
288
+ end
289
+ end
290
+
291
+ # Create join tables if needed. Join tables are used in
292
+ # 'many_to_many' relations.
293
+
294
+ if klass.__meta and joins = klass.__meta[:sql_join]
295
+ for data in joins
296
+ # the class to join to and some options.
297
+ join_class, options = *data
298
+
299
+ # gmosx: dont use DBTABLE here, perhaps the join class
300
+ # is not managed yet.
301
+ join_table = "#{self.class.join_table(klass, join_class)}"
302
+ join_src = "#{self.class.encode(klass)}_oid"
303
+ join_dst = "#{self.class.encode(join_class)}_oid"
304
+ begin
305
+ exec "CREATE TABLE #{join_table} ( key1 integer NOT NULL, key2 integer NOT NULL )"
306
+ exec "CREATE INDEX #{join_table}_key1_idx ON #{join_table} (key1)"
307
+ exec "CREATE INDEX #{join_table}_key2_idx ON #{join_table} (key2)"
308
+ rescue => ex
309
+ # gmosx: any idea how to better test this?
310
+ if ex.to_s =~ /relation .* already exists/i
311
+ Logger.debug "Join table already exists" if $DBG
312
+ else
313
+ raise
314
+ end
315
+ end
316
+ end
317
+ end
318
+
319
+ begin
320
+ exec(sql)
321
+ Logger.info "Created join table '#{join_table}'."
322
+ rescue => ex
323
+ # gmosx: any idea how to better test this?
324
+ if ex.to_s =~ /relation .* already exists/i
325
+ Logger.debug "Join table already exists" if $DBG
326
+ else
327
+ raise
328
+ end
329
+ end
330
+
331
+ end
332
+
333
+ # Drop the managed object table.
334
+
335
+ def drop_table(klass)
336
+ super
337
+ exec "DROP SEQUENCE #{klass::DBSEQ}"
338
+ end
339
+
340
+ # Deserialize one row of the resultset.
341
+
342
+ def deserialize_one(res, klass)
343
+ return nil unless valid?(res)
344
+
345
+ # gmosx: Managed objects should have no params constructor.
346
+ entity = klass.new()
347
+ entity.og_deserialize(res, 0)
348
+
349
+ # get_join_fields(res, 0, entity, join_fields) if join_fields
350
+
351
+ res.clear()
352
+ return entity
353
+ end
354
+
355
+ # Deserialize all rows of the resultset.
356
+
357
+ def deserialize_all(res, klass)
358
+ return [] unless valid?(res)
359
+
360
+ entities = []
361
+
362
+ for tuple in (0...res.num_tuples)
363
+ entity = klass.new()
364
+ entity.og_deserialize(res, tuple)
365
+
366
+ # get_join_fields(res, tuple, entity, join_fields) if join_fields
367
+
368
+ entities << entity
369
+ end
370
+
371
+ res.clear()
372
+ return entities
373
+ end
374
+
375
+ # Return a single integer value from the resultset.
376
+
377
+ def get_int(res, idx = 0)
378
+ return res.getvalue(0, idx).to_i
379
+ end
380
+
381
+ end
382
+
383
+ end