nitro 0.7.0 → 0.8.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.
- 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
|