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