nitro 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +14 -4
- data/ChangeLog +192 -1
- data/README +50 -6
- data/RELEASES +60 -0
- data/Rakefile +1 -1
- data/bin/cluster.rb +2 -2
- data/bin/new_form.rb +1 -1
- data/examples/blog/config.rb +5 -4
- data/examples/blog/lib/blog.rb +56 -36
- data/examples/blog/root/comments.xhtml +5 -2
- data/examples/blog/root/entry_form.xhtml +7 -2
- data/examples/blog/root/login.xhtml +1 -1
- data/examples/blog/root/style.xsl +7 -0
- data/examples/og/mock_example.rb +6 -9
- data/examples/og/mysql_to_psql.rb +100 -0
- data/examples/og/run.rb +8 -17
- data/lib/glue.rb +7 -8
- data/lib/glue/array.rb +1 -1
- data/lib/glue/attribute.rb +86 -0
- data/lib/glue/cache.rb +1 -1
- data/lib/glue/hash.rb +1 -1
- data/lib/glue/inflector.rb +1 -1
- data/lib/glue/logger.rb +118 -18
- data/lib/glue/mixins.rb +1 -1
- data/lib/glue/number.rb +1 -1
- data/lib/glue/pool.rb +1 -1
- data/lib/glue/property.rb +48 -31
- data/lib/glue/string.rb +1 -1
- data/lib/glue/time.rb +2 -2
- data/lib/glue/validation.rb +400 -0
- data/lib/nitro/application.rb +6 -6
- data/lib/nitro/builders/form.rb +5 -5
- data/lib/nitro/builders/rss.rb +1 -1
- data/lib/nitro/builders/xhtml.rb +119 -0
- data/lib/nitro/builders/xml.rb +111 -0
- data/lib/nitro/config.rb +6 -6
- data/lib/nitro/events.rb +1 -1
- data/lib/nitro/html.rb +1 -1
- data/lib/nitro/markup.rb +15 -20
- data/lib/nitro/scaffold.rb +2 -2
- data/lib/nitro/server/appserver.rb +3 -3
- data/lib/nitro/server/cluster.rb +2 -2
- data/lib/nitro/server/dispatcher.rb +2 -2
- data/lib/nitro/server/filters/autologin.rb +1 -1
- data/lib/nitro/server/fragment.rb +2 -2
- data/lib/nitro/server/handlers.rb +2 -2
- data/lib/nitro/server/render.rb +17 -15
- data/lib/nitro/server/request.rb +6 -6
- data/lib/nitro/server/script.rb +2 -2
- data/lib/nitro/server/server.rb +2 -2
- data/lib/nitro/server/session.rb +6 -6
- data/lib/nitro/server/shaders.rb +2 -2
- data/lib/nitro/server/webrick.rb +1 -1
- data/lib/nitro/sitemap.rb +2 -2
- data/lib/nitro/uri.rb +1 -1
- data/lib/nitro/version.rb +7 -5
- data/lib/og.rb +95 -129
- data/lib/og/backend.rb +47 -46
- data/lib/og/backends/mysql.rb +64 -63
- data/lib/og/backends/psql.rb +73 -72
- data/lib/og/connection.rb +7 -8
- data/lib/og/enchant.rb +80 -0
- data/lib/og/meta.rb +21 -21
- data/lib/og/mock.rb +31 -88
- data/lib/og/version.rb +6 -5
- data/lib/parts/README +9 -0
- data/lib/parts/content.rb +23 -9
- data/test/glue/tc_attribute.rb +22 -0
- data/test/glue/tc_cache.rb +4 -6
- data/test/glue/tc_hash.rb +2 -2
- data/test/glue/tc_logger.rb +36 -0
- data/test/glue/tc_numbers.rb +2 -2
- data/test/glue/tc_property_mixins.rb +35 -4
- data/test/glue/tc_strings.rb +32 -32
- data/test/glue/tc_validation.rb +186 -0
- data/test/nitro/builders/tc_xhtml.rb +38 -0
- data/test/nitro/builders/tc_xml.rb +47 -0
- data/test/nitro/server/tc_request.rb +2 -2
- data/test/nitro/server/tc_session.rb +1 -1
- data/test/nitro/tc_sitemap.rb +1 -1
- data/test/nitro/ui/tc_pager.rb +1 -10
- data/test/tc_og.rb +3 -3
- data/vendor/blankslate.rb +53 -0
- data/vendor/extensions/_base.rb +153 -0
- data/vendor/extensions/_template.rb +36 -0
- data/vendor/extensions/all.rb +21 -0
- data/vendor/extensions/array.rb +68 -0
- data/vendor/extensions/binding.rb +224 -0
- data/vendor/extensions/class.rb +50 -0
- data/vendor/extensions/continuation.rb +71 -0
- data/vendor/extensions/enumerable.rb +250 -0
- data/vendor/extensions/hash.rb +23 -0
- data/vendor/extensions/io.rb +58 -0
- data/vendor/extensions/kernel.rb +42 -0
- data/vendor/extensions/module.rb +114 -0
- data/vendor/extensions/numeric.rb +230 -0
- data/vendor/extensions/object.rb +164 -0
- data/vendor/extensions/ostruct.rb +41 -0
- data/vendor/extensions/string.rb +316 -0
- data/vendor/extensions/symbol.rb +28 -0
- metadata +35 -13
- data/lib/glue/property.rb.old +0 -307
data/lib/og/backend.rb
CHANGED
@@ -8,33 +8,54 @@ require "yaml"
|
|
8
8
|
|
9
9
|
require "og/connection"
|
10
10
|
|
11
|
-
|
11
|
+
class Og
|
12
12
|
|
13
|
-
# =
|
13
|
+
# = Backend
|
14
14
|
#
|
15
|
-
# A
|
15
|
+
# Abstract backend. A backend communicates with the RDBMS.
|
16
|
+
# This is the base class for the various backend implementations.
|
16
17
|
#
|
17
|
-
|
18
|
+
class Backend
|
19
|
+
|
20
|
+
# The actual connection to the database
|
21
|
+
attr_accessor :conn
|
22
|
+
|
23
|
+
# Intitialize the connection to the RDBMS.
|
24
|
+
#
|
25
|
+
def initialize(config)
|
26
|
+
raise "Not implemented"
|
27
|
+
end
|
28
|
+
|
29
|
+
# Close the connection to the RDBMS.
|
30
|
+
#
|
31
|
+
def close()
|
32
|
+
@conn.close()
|
33
|
+
end
|
34
|
+
|
35
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
36
|
+
# Utilities
|
37
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
18
38
|
|
19
39
|
# Encode the name of the klass as an sql safe string.
|
20
|
-
# The Module separators are replaced with _ and NOT stripped
|
21
|
-
# that we can convert back to the original notation if
|
22
|
-
# The leading module if available is removed.
|
40
|
+
# The Module separators are replaced with _ and NOT stripped
|
41
|
+
# out so that we can convert back to the original notation if
|
42
|
+
# needed. The leading module if available is removed.
|
23
43
|
#
|
24
44
|
def self.encode(klass)
|
25
45
|
"#{klass.name.gsub(/^.*::/, "")}".gsub(/::/, "_").downcase
|
26
46
|
end
|
27
47
|
|
28
|
-
# The name of the SQL table where objects of this class
|
48
|
+
# The name of the SQL table where objects of this class
|
49
|
+
# are stored.
|
29
50
|
#
|
30
51
|
def self.table(klass)
|
31
|
-
"_#{
|
52
|
+
"_#{Og.table_prefix}#{encode(klass)}"
|
32
53
|
end
|
33
54
|
|
34
55
|
# The name of the join table for the two given classes.
|
35
56
|
#
|
36
57
|
def self.join_table(klass1, klass2)
|
37
|
-
"_#{
|
58
|
+
"_#{Og.table_prefix}j_#{encode(klass1)}_#{encode(klass2)}"
|
38
59
|
end
|
39
60
|
|
40
61
|
# Returns the props that will be included in the insert query.
|
@@ -123,8 +144,8 @@ module Utils
|
|
123
144
|
end
|
124
145
|
|
125
146
|
# Precompile the code to read objects of the given class
|
126
|
-
# from the backend. In order to allow for changing
|
127
|
-
# orders we have to use a field mapping hash.
|
147
|
+
# from the backend. In order to allow for changing
|
148
|
+
# field/attribute orders we have to use a field mapping hash.
|
128
149
|
#
|
129
150
|
def self.eval_og_deserialize(klass, og)
|
130
151
|
calc_field_index(klass, og)
|
@@ -136,7 +157,7 @@ module Utils
|
|
136
157
|
if idx = og.managed_classes[klass].field_index[p.name]
|
137
158
|
# more fault tolerant if a new field is added and it
|
138
159
|
# doesnt exist in the database.
|
139
|
-
code << "@#{p.name} = #{
|
160
|
+
code << "@#{p.name} = #{read_prop(p, idx)}"
|
140
161
|
end
|
141
162
|
end
|
142
163
|
|
@@ -147,40 +168,20 @@ module Utils
|
|
147
168
|
}
|
148
169
|
end
|
149
170
|
|
150
|
-
|
151
|
-
|
152
|
-
#
|
153
|
-
#
|
154
|
-
# Abstract backend. A backend communicates with the RDBMS.
|
155
|
-
# This is the base class for the various backend implementations.
|
156
|
-
#
|
157
|
-
class Backend
|
158
|
-
|
159
|
-
# The actual connection to the database
|
160
|
-
attr_accessor :conn
|
161
|
-
|
162
|
-
# Intitialize the connection to the RDBMS.
|
163
|
-
#
|
164
|
-
def initialize(config)
|
165
|
-
raise "Not implemented"
|
166
|
-
end
|
167
|
-
|
168
|
-
# Close the connection to the RDBMS.
|
169
|
-
#
|
170
|
-
def close()
|
171
|
-
@conn.close()
|
172
|
-
end
|
171
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
172
|
+
# Connection methods.
|
173
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
173
174
|
|
174
175
|
# Create the database.
|
175
176
|
#
|
176
177
|
def self.create_db(database, user = nil, password = nil)
|
177
|
-
|
178
|
+
Logger.info "Creating database '#{database}'."
|
178
179
|
end
|
179
180
|
|
180
181
|
# Drop the database.
|
181
182
|
#
|
182
183
|
def self.drop_db(database, user = nil, password = nil)
|
183
|
-
|
184
|
+
Logger.info "Dropping database '#{database}'."
|
184
185
|
end
|
185
186
|
|
186
187
|
# Execute an SQL query and return the result.
|
@@ -195,15 +196,15 @@ class Backend
|
|
195
196
|
raise "Not implemented"
|
196
197
|
end
|
197
198
|
|
198
|
-
# Execute an SQL query and return the result. Wrapped in a
|
199
|
-
# block.
|
199
|
+
# Execute an SQL query and return the result. Wrapped in a
|
200
|
+
# rescue block.
|
200
201
|
#
|
201
202
|
def safe_query(sql)
|
202
203
|
raise "Not implemented"
|
203
204
|
end
|
204
205
|
|
205
|
-
# Execute an SQL query, no result returned. Wrapped in a
|
206
|
-
# block.
|
206
|
+
# Execute an SQL query, no result returned. Wrapped in a
|
207
|
+
# rescue block.
|
207
208
|
#
|
208
209
|
def safe_exec(sql)
|
209
210
|
raise "Not implemented"
|
@@ -235,9 +236,9 @@ class Backend
|
|
235
236
|
|
236
237
|
# Create the fields that correpsond to the klass properties.
|
237
238
|
# The generated fields array is used in create_table.
|
238
|
-
# If the property has an :sql metadata this overrides the
|
239
|
-
# If the property has an :extra_sql metadata
|
240
|
-
# after the default mapping.
|
239
|
+
# If the property has an :sql metadata this overrides the
|
240
|
+
# default mapping. If the property has an :extra_sql metadata
|
241
|
+
# the extra sql is appended after the default mapping.
|
241
242
|
#
|
242
243
|
def create_fields(klass, typemap)
|
243
244
|
fields = []
|
@@ -297,4 +298,4 @@ class Backend
|
|
297
298
|
|
298
299
|
end
|
299
300
|
|
300
|
-
end #
|
301
|
+
end # namespace
|
data/lib/og/backends/mysql.rb
CHANGED
@@ -9,13 +9,48 @@ require "mysql"
|
|
9
9
|
|
10
10
|
require "og/backend"
|
11
11
|
|
12
|
-
|
12
|
+
class Og
|
13
13
|
|
14
|
-
# =
|
14
|
+
# = MysqlBackend
|
15
15
|
#
|
16
|
-
#
|
16
|
+
# Implements a MySQL powered backend.
|
17
17
|
#
|
18
|
-
|
18
|
+
class MysqlBackend < Og::Backend
|
19
|
+
|
20
|
+
# A mapping between Ruby and SQL types.
|
21
|
+
#
|
22
|
+
TYPEMAP = {
|
23
|
+
Integer => "integer",
|
24
|
+
Fixnum => "integer",
|
25
|
+
Float => "float",
|
26
|
+
String => "text",
|
27
|
+
Time => "timestamp",
|
28
|
+
Date => "date",
|
29
|
+
TrueClass => "boolean",
|
30
|
+
Object => "text",
|
31
|
+
Array => "text",
|
32
|
+
Hash => "text"
|
33
|
+
}
|
34
|
+
|
35
|
+
# Intitialize the connection to the RDBMS.
|
36
|
+
#
|
37
|
+
def initialize(config)
|
38
|
+
begin
|
39
|
+
@conn = Mysql.connect(config[:address], config[:user],
|
40
|
+
config[:password], config[:database])
|
41
|
+
rescue => ex
|
42
|
+
if ex.errno == 1049 # database does not exist.
|
43
|
+
Logger.info "Database '#{config[:database]}' not found!"
|
44
|
+
MysqlBackend.create_db(config[:database], config[:user], config[:password])
|
45
|
+
retry
|
46
|
+
end
|
47
|
+
raise
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
52
|
+
# Utilities
|
53
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
19
54
|
|
20
55
|
# Escape an SQL string
|
21
56
|
#
|
@@ -68,15 +103,15 @@ module Utils
|
|
68
103
|
elsif p.klass.ancestors.include?(Float)
|
69
104
|
return "#\{@#{p.symbol} || 'NULL'\}"
|
70
105
|
elsif p.klass.ancestors.include?(String)
|
71
|
-
return "'#\{Og::
|
106
|
+
return "'#\{Og::MysqlBackend.escape(@#{p.symbol})\}'"
|
72
107
|
elsif p.klass.ancestors.include?(Time)
|
73
|
-
return %|#\{@#{p.symbol} ? "'#\{Og::
|
108
|
+
return %|#\{@#{p.symbol} ? "'#\{Og::MysqlBackend.timestamp(@#{p.symbol})\}'" : 'NULL'\}|
|
74
109
|
elsif p.klass.ancestors.include?(Date)
|
75
|
-
return %|#\{@#{p.symbol} ? "'#\{Og::
|
110
|
+
return %|#\{@#{p.symbol} ? "'#\{Og::MysqlBackend.date(@#{p.symbol})\}'" : 'NULL'\}|
|
76
111
|
elsif p.klass.ancestors.include?(TrueClass)
|
77
112
|
return "#\{@#{p.symbol} || 'NULL'\}"
|
78
113
|
else
|
79
|
-
return %|#\{@#{p.symbol} ? "'#\{Og::
|
114
|
+
return %|#\{@#{p.symbol} ? "'#\{Og::MysqlBackend.escape(@#{p.symbol}.to_yaml)\}'" : "''"\}|
|
80
115
|
end
|
81
116
|
end
|
82
117
|
|
@@ -91,9 +126,9 @@ module Utils
|
|
91
126
|
elsif p.klass.ancestors.include?(String)
|
92
127
|
return "res[#{idx}]"
|
93
128
|
elsif p.klass.ancestors.include?(Time)
|
94
|
-
return "Og::
|
129
|
+
return "Og::MysqlBackend.parse_timestamp(res[#{idx}])"
|
95
130
|
elsif p.klass.ancestors.include?(Date)
|
96
|
-
return "Og::
|
131
|
+
return "Og::MysqlBackend.parse_date(res[#{idx}])"
|
97
132
|
elsif p.klass.ancestors.include?(TrueClass)
|
98
133
|
return "('true' == res[#{idx}])"
|
99
134
|
else
|
@@ -140,70 +175,36 @@ module Utils
|
|
140
175
|
prop_accessor :oid, Fixnum, :sql => "integer AUTO_INCREMENT PRIMARY KEY"
|
141
176
|
}
|
142
177
|
end
|
143
|
-
end
|
144
178
|
|
145
|
-
#
|
146
|
-
#
|
147
|
-
#
|
148
|
-
#
|
149
|
-
class MysqlBackend < Og::Backend
|
150
|
-
|
151
|
-
# A mapping between Ruby and SQL types.
|
152
|
-
#
|
153
|
-
TYPEMAP = {
|
154
|
-
Integer => "integer",
|
155
|
-
Fixnum => "integer",
|
156
|
-
Float => "float",
|
157
|
-
String => "text",
|
158
|
-
Time => "timestamp",
|
159
|
-
Date => "date",
|
160
|
-
TrueClass => "boolean",
|
161
|
-
Object => "text",
|
162
|
-
Array => "text",
|
163
|
-
Hash => "text"
|
164
|
-
}
|
165
|
-
|
166
|
-
# Intitialize the connection to the RDBMS.
|
167
|
-
#
|
168
|
-
def initialize(config)
|
169
|
-
begin
|
170
|
-
@conn = Mysql.connect(config[:address], config[:user],
|
171
|
-
config[:password], config[:database])
|
172
|
-
rescue => ex
|
173
|
-
if ex.errno == 1049 # database does not exist.
|
174
|
-
$log.info "Database '#{config[:database]}' not found!"
|
175
|
-
MysqlBackend.create_db(config[:database], config[:user], config[:password])
|
176
|
-
retry
|
177
|
-
end
|
178
|
-
raise
|
179
|
-
end
|
180
|
-
end
|
179
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
180
|
+
# Connection methods.
|
181
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
181
182
|
|
182
183
|
# Create the database.
|
183
184
|
#
|
184
185
|
def self.create_db(database, user = nil, password = nil)
|
185
|
-
|
186
|
+
Logger.info "Creating database '#{database}'."
|
186
187
|
`mysqladmin -f --user=#{user} --password=#{password} create #{database}`
|
187
188
|
end
|
188
189
|
|
189
190
|
# Drop the database.
|
190
191
|
#
|
191
192
|
def self.drop_db(database, user = nil, password = nil)
|
192
|
-
|
193
|
+
Logger.info "Dropping database '#{database}'."
|
193
194
|
`mysqladmin -f --user=#{user} --password=#{password} drop #{database}`
|
194
195
|
end
|
195
196
|
|
196
197
|
# Execute an SQL query and return the result
|
197
198
|
#
|
198
199
|
def query(sql)
|
199
|
-
|
200
|
+
Logger.debug sql if $DBG
|
200
201
|
return @conn.query(sql)
|
201
202
|
end
|
202
203
|
|
203
204
|
# Execute an SQL query, no result returned.
|
204
205
|
#
|
205
206
|
def exec(sql)
|
206
|
-
|
207
|
+
Logger.debug sql if $DBG
|
207
208
|
@conn.query(sql)
|
208
209
|
end
|
209
210
|
|
@@ -211,12 +212,12 @@ class MysqlBackend < Og::Backend
|
|
211
212
|
# block.
|
212
213
|
#
|
213
214
|
def safe_query(sql)
|
214
|
-
|
215
|
+
Logger.debug sql if $DBG
|
215
216
|
begin
|
216
217
|
return @conn.query(sql)
|
217
218
|
rescue => ex
|
218
|
-
|
219
|
-
|
219
|
+
Logger.error "DB error #{ex}, [#{sql}]"
|
220
|
+
Logger.error ex.backtrace
|
220
221
|
return nil
|
221
222
|
end
|
222
223
|
end
|
@@ -225,12 +226,12 @@ class MysqlBackend < Og::Backend
|
|
225
226
|
# block.
|
226
227
|
#
|
227
228
|
def safe_exec(sql)
|
228
|
-
|
229
|
+
Logger.debug sql if $DBG
|
229
230
|
begin
|
230
231
|
@conn.query(sql)
|
231
232
|
rescue => ex
|
232
|
-
|
233
|
-
|
233
|
+
Logger.error "DB error #{ex}, [#{sql}]"
|
234
|
+
Logger.error ex.backtrace
|
234
235
|
end
|
235
236
|
end
|
236
237
|
|
@@ -277,10 +278,10 @@ class MysqlBackend < Og::Backend
|
|
277
278
|
|
278
279
|
begin
|
279
280
|
exec(sql)
|
280
|
-
|
281
|
+
Logger.info "Created table '#{klass::DBTABLE}'."
|
281
282
|
rescue => ex
|
282
283
|
if ex.errno == 1050 # table already exists.
|
283
|
-
|
284
|
+
Logger.debug "Table already exists" if $DBG
|
284
285
|
else
|
285
286
|
raise
|
286
287
|
end
|
@@ -308,16 +309,16 @@ class MysqlBackend < Og::Backend
|
|
308
309
|
|
309
310
|
# gmosx: dont use DBTABLE here, perhaps the join class
|
310
311
|
# is not managed yet.
|
311
|
-
join_table = "#{
|
312
|
-
join_src = "#{
|
313
|
-
join_dst = "#{
|
312
|
+
join_table = "#{self.class.join_table(klass, join_class)}"
|
313
|
+
join_src = "#{self.class.encode(klass)}_oid"
|
314
|
+
join_dst = "#{self.class.encode(join_class)}_oid"
|
314
315
|
begin
|
315
316
|
exec "CREATE TABLE #{join_table} ( key1 integer NOT NULL, key2 integer NOT NULL )"
|
316
317
|
exec "CREATE INDEX #{join_table}_key1_idx ON #{join_table} (key1)"
|
317
318
|
exec "CREATE INDEX #{join_table}_key2_idx ON #{join_table} (key2)"
|
318
319
|
rescue => ex
|
319
320
|
if ex.errno == 1050 # table already exists.
|
320
|
-
|
321
|
+
Logger.debug "Join table already exists" if $DBG
|
321
322
|
else
|
322
323
|
raise
|
323
324
|
end
|
data/lib/og/backends/psql.rb
CHANGED
@@ -4,17 +4,55 @@
|
|
4
4
|
# (c) 2004 Navel, all rights reserved.
|
5
5
|
# $Id: psql.rb 194 2004-12-20 20:23:57Z gmosx $
|
6
6
|
|
7
|
-
require
|
7
|
+
require 'postgres'
|
8
8
|
|
9
|
-
require
|
9
|
+
require 'og/backend'
|
10
10
|
|
11
|
-
|
11
|
+
class Og
|
12
12
|
|
13
|
-
# =
|
13
|
+
# = PsqlBackend
|
14
14
|
#
|
15
|
-
#
|
15
|
+
# Implements a PostgreSQL powered backend.
|
16
|
+
# This backend is compatible with Michael Neumann's postgres-pr
|
17
|
+
# pure ruby driver.
|
16
18
|
#
|
17
|
-
|
19
|
+
class PsqlBackend < Og::Backend
|
20
|
+
|
21
|
+
# A mapping between Ruby and SQL types.
|
22
|
+
#
|
23
|
+
TYPEMAP = {
|
24
|
+
Integer => 'integer',
|
25
|
+
Fixnum => 'integer',
|
26
|
+
Float => 'float',
|
27
|
+
String => 'text',
|
28
|
+
Time => 'timestamp',
|
29
|
+
Date => 'date',
|
30
|
+
TrueClass => 'boolean',
|
31
|
+
Object => 'text',
|
32
|
+
Array => 'text',
|
33
|
+
Hash => 'text'
|
34
|
+
}
|
35
|
+
|
36
|
+
# Intitialize the connection to the RDBMS.
|
37
|
+
#
|
38
|
+
def initialize(config)
|
39
|
+
begin
|
40
|
+
@conn = PGconn.connect(nil, nil, nil, nil, config[:database],
|
41
|
+
config[:user], config[:password])
|
42
|
+
rescue => ex
|
43
|
+
# gmosx: any idea how to better test this?
|
44
|
+
if ex.to_s =~ /database .* does not exist/i
|
45
|
+
Logger.info "Database '#{config[:database]}' not found!"
|
46
|
+
PsqlBackend.create_db(config[:database], config[:user])
|
47
|
+
retry
|
48
|
+
end
|
49
|
+
raise
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
54
|
+
# Utilities
|
55
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
18
56
|
|
19
57
|
# Escape an SQL string
|
20
58
|
#
|
@@ -67,15 +105,15 @@ module Utils
|
|
67
105
|
elsif p.klass.ancestors.include?(Float)
|
68
106
|
return "#\{@#{p.symbol} || 'NULL'\}"
|
69
107
|
elsif p.klass.ancestors.include?(String)
|
70
|
-
return "'#\{Og::
|
108
|
+
return "'#\{Og::PsqlBackend.escape(@#{p.symbol})\}'"
|
71
109
|
elsif p.klass.ancestors.include?(Time)
|
72
|
-
return %|#\{@#{p.symbol} ? "'#\{Og::
|
110
|
+
return %|#\{@#{p.symbol} ? "'#\{Og::PsqlBackend.timestamp(@#{p.symbol})\}'" : 'NULL'\}|
|
73
111
|
elsif p.klass.ancestors.include?(Date)
|
74
|
-
return %|#\{@#{p.symbol} ? "'#\{Og::
|
112
|
+
return %|#\{@#{p.symbol} ? "'#\{Og::PsqlBackend.date(@#{p.symbol})\}'" : 'NULL'\}|
|
75
113
|
elsif p.klass.ancestors.include?(TrueClass)
|
76
114
|
return "#\{@#{p.symbol} || 'NULL'\}"
|
77
115
|
else
|
78
|
-
return %|#\{@#{p.symbol} ? "'#\{Og::
|
116
|
+
return %|#\{@#{p.symbol} ? "'#\{Og::PsqlBackend.escape(@#{p.symbol}.to_yaml)\}'" : "''"\}|
|
79
117
|
end
|
80
118
|
end
|
81
119
|
|
@@ -90,9 +128,9 @@ module Utils
|
|
90
128
|
elsif p.klass.ancestors.include?(String)
|
91
129
|
return "res.getvalue(tuple, #{idx})"
|
92
130
|
elsif p.klass.ancestors.include?(Time)
|
93
|
-
return "Og::
|
131
|
+
return "Og::PsqlBackend.parse_timestamp(res.getvalue(tuple, #{idx}))"
|
94
132
|
elsif p.klass.ancestors.include?(Date)
|
95
|
-
return "Og::
|
133
|
+
return "Og::PsqlBackend.parse_date(res.getvalue(tuple, #{idx}))"
|
96
134
|
elsif p.klass.ancestors.include?(TrueClass)
|
97
135
|
return "('true' == res.getvalue(tuple, #{idx}))"
|
98
136
|
else
|
@@ -132,73 +170,36 @@ module Utils
|
|
132
170
|
prop_accessor :oid, Fixnum, :sql => "integer PRIMARY KEY"
|
133
171
|
}
|
134
172
|
end
|
135
|
-
end
|
136
173
|
|
137
|
-
#
|
138
|
-
#
|
139
|
-
#
|
140
|
-
# This backend is compatible with Michael Neumann's postgres-pr
|
141
|
-
# pure ruby driver.
|
142
|
-
#
|
143
|
-
class PsqlBackend < Og::Backend
|
144
|
-
|
145
|
-
# A mapping between Ruby and SQL types.
|
146
|
-
#
|
147
|
-
TYPEMAP = {
|
148
|
-
Integer => "integer",
|
149
|
-
Fixnum => "integer",
|
150
|
-
Float => "float",
|
151
|
-
String => "text",
|
152
|
-
Time => "timestamp",
|
153
|
-
Date => "date",
|
154
|
-
TrueClass => "boolean",
|
155
|
-
Object => "text",
|
156
|
-
Array => "text",
|
157
|
-
Hash => "text"
|
158
|
-
}
|
159
|
-
|
160
|
-
# Intitialize the connection to the RDBMS.
|
161
|
-
#
|
162
|
-
def initialize(config)
|
163
|
-
begin
|
164
|
-
@conn = PGconn.connect(nil, nil, nil, nil, config[:database],
|
165
|
-
config[:user], config[:password])
|
166
|
-
rescue => ex
|
167
|
-
# gmosx: any idea how to better test this?
|
168
|
-
if ex.to_s =~ /database .* does not exist/i
|
169
|
-
$log.info "Database '#{config[:database]}' not found!"
|
170
|
-
PsqlBackend.create_db(config[:database], config[:user])
|
171
|
-
retry
|
172
|
-
end
|
173
|
-
raise
|
174
|
-
end
|
175
|
-
end
|
174
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
175
|
+
# Connection methods.
|
176
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
176
177
|
|
177
178
|
# Create the database.
|
178
179
|
#
|
179
180
|
def self.create_db(database, user = nil, password = nil)
|
180
|
-
|
181
|
+
Logger.info "Creating database '#{database}'."
|
181
182
|
`createdb #{database} -U #{user}`
|
182
183
|
end
|
183
184
|
|
184
185
|
# Drop the database.
|
185
186
|
#
|
186
187
|
def self.drop_db(database, user = nil, password = nil)
|
187
|
-
|
188
|
+
Logger.info "Dropping database '#{database}'."
|
188
189
|
`dropdb #{database} -U #{user}`
|
189
190
|
end
|
190
191
|
|
191
192
|
# Execute an SQL query and return the result
|
192
193
|
#
|
193
194
|
def query(sql)
|
194
|
-
|
195
|
+
Logger.debug sql if $DBG
|
195
196
|
return @conn.exec(sql)
|
196
197
|
end
|
197
198
|
|
198
199
|
# Execute an SQL query, no result returned.
|
199
200
|
#
|
200
201
|
def exec(sql)
|
201
|
-
|
202
|
+
Logger.debug sql if $DBG
|
202
203
|
res = @conn.exec(sql)
|
203
204
|
res.clear()
|
204
205
|
end
|
@@ -207,12 +208,12 @@ class PsqlBackend < Og::Backend
|
|
207
208
|
# block.
|
208
209
|
#
|
209
210
|
def safe_query(sql)
|
210
|
-
|
211
|
+
Logger.debug sql if $DBG
|
211
212
|
begin
|
212
213
|
return @conn.exec(sql)
|
213
214
|
rescue => ex
|
214
|
-
|
215
|
-
|
215
|
+
Logger.error "DB error #{ex}, [#{sql}]"
|
216
|
+
Logger.error ex.backtrace
|
216
217
|
return nil
|
217
218
|
end
|
218
219
|
end
|
@@ -221,13 +222,13 @@ class PsqlBackend < Og::Backend
|
|
221
222
|
# block.
|
222
223
|
#
|
223
224
|
def safe_exec(sql)
|
224
|
-
|
225
|
+
Logger.debug sql if $DBG
|
225
226
|
begin
|
226
227
|
res = @conn.exec(sql)
|
227
228
|
res.clear()
|
228
229
|
rescue => ex
|
229
|
-
|
230
|
-
|
230
|
+
Logger.error "DB error #{ex}, [#{sql}]"
|
231
|
+
Logger.error ex.backtrace
|
231
232
|
end
|
232
233
|
end
|
233
234
|
|
@@ -268,11 +269,11 @@ class PsqlBackend < Og::Backend
|
|
268
269
|
|
269
270
|
begin
|
270
271
|
exec(sql)
|
271
|
-
|
272
|
+
Logger.info "Created table '#{klass::DBTABLE}'."
|
272
273
|
rescue => ex
|
273
274
|
# gmosx: any idea how to better test this?
|
274
275
|
if ex.to_s =~ /relation .* already exists/i
|
275
|
-
|
276
|
+
Logger.debug "Table already exists" if $DBG
|
276
277
|
else
|
277
278
|
raise
|
278
279
|
end
|
@@ -283,11 +284,11 @@ class PsqlBackend < Og::Backend
|
|
283
284
|
# the system more fault tolerant.
|
284
285
|
begin
|
285
286
|
exec "CREATE SEQUENCE #{klass::DBSEQ}"
|
286
|
-
|
287
|
+
Logger.info "Created sequence '#{klass::DBSEQ}'."
|
287
288
|
rescue => ex
|
288
289
|
# gmosx: any idea how to better test this?
|
289
290
|
if ex.to_s =~ /relation .* already exists/i
|
290
|
-
|
291
|
+
Logger.debug "Sequence already exists" if $DBG
|
291
292
|
else
|
292
293
|
raise
|
293
294
|
end
|
@@ -303,9 +304,9 @@ class PsqlBackend < Og::Backend
|
|
303
304
|
|
304
305
|
# gmosx: dont use DBTABLE here, perhaps the join class
|
305
306
|
# is not managed yet.
|
306
|
-
join_table = "#{
|
307
|
-
join_src = "#{
|
308
|
-
join_dst = "#{
|
307
|
+
join_table = "#{self.class.join_table(klass, join_class)}"
|
308
|
+
join_src = "#{self.class.encode(klass)}_oid"
|
309
|
+
join_dst = "#{self.class.encode(join_class)}_oid"
|
309
310
|
begin
|
310
311
|
exec "CREATE TABLE #{join_table} ( key1 integer NOT NULL, key2 integer NOT NULL )"
|
311
312
|
exec "CREATE INDEX #{join_table}_key1_idx ON #{join_table} (key1)"
|
@@ -313,7 +314,7 @@ class PsqlBackend < Og::Backend
|
|
313
314
|
rescue => ex
|
314
315
|
# gmosx: any idea how to better test this?
|
315
316
|
if ex.to_s =~ /relation .* already exists/i
|
316
|
-
|
317
|
+
Logger.debug "Join table already exists" if $DBG
|
317
318
|
else
|
318
319
|
raise
|
319
320
|
end
|
@@ -323,11 +324,11 @@ class PsqlBackend < Og::Backend
|
|
323
324
|
|
324
325
|
begin
|
325
326
|
exec(sql)
|
326
|
-
|
327
|
+
Logger.info "Created join table '#{join_table}'."
|
327
328
|
rescue => ex
|
328
329
|
# gmosx: any idea how to better test this?
|
329
330
|
if ex.to_s =~ /relation .* already exists/i
|
330
|
-
|
331
|
+
Logger.debug "Join table already exists" if $DBG
|
331
332
|
else
|
332
333
|
raise
|
333
334
|
end
|