nitro 0.9.3 → 0.9.5

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 (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