nitro 0.1.2

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 (119) hide show
  1. data/AUTHORS +8 -0
  2. data/ChangeLog +1546 -0
  3. data/LICENCE +32 -0
  4. data/README +278 -0
  5. data/RELEASES +7 -0
  6. data/Rakefile +79 -0
  7. data/bin/cluster.rb +219 -0
  8. data/doc/architecture.txt +28 -0
  9. data/doc/bugs.txt +7 -0
  10. data/doc/css.txt +20 -0
  11. data/doc/ideas.txt +120 -0
  12. data/doc/pg.txt +47 -0
  13. data/doc/svn.txt +82 -0
  14. data/doc/todo.txt +30 -0
  15. data/etc/new-project.rb +18 -0
  16. data/examples/simple/README +15 -0
  17. data/examples/simple/app.rb +31 -0
  18. data/examples/simple/conf/apache.conf +100 -0
  19. data/examples/simple/conf/config.rb +89 -0
  20. data/examples/simple/conf/debug-config.rb +53 -0
  21. data/examples/simple/conf/live-config.rb +48 -0
  22. data/examples/simple/conf/overrides.rb +9 -0
  23. data/examples/simple/conf/requires.rb +51 -0
  24. data/examples/simple/ctl +32 -0
  25. data/examples/simple/env.rb +33 -0
  26. data/examples/simple/install.rb +12 -0
  27. data/examples/simple/lib/articles/entities.rb +35 -0
  28. data/examples/simple/lib/articles/lc-en.rb +36 -0
  29. data/examples/simple/lib/articles/methods.rb +55 -0
  30. data/examples/simple/lib/articles/part.rb +58 -0
  31. data/examples/simple/logs/access_log +2 -0
  32. data/examples/simple/logs/apache.log +3 -0
  33. data/examples/simple/logs/app.log +1 -0
  34. data/examples/simple/logs/events.log +1 -0
  35. data/examples/simple/root/add-article.sx +15 -0
  36. data/examples/simple/root/article-form.ss +20 -0
  37. data/examples/simple/root/comments-form.ss +16 -0
  38. data/examples/simple/root/comments.si +30 -0
  39. data/examples/simple/root/index.sx +44 -0
  40. data/examples/simple/root/shader/shader.xsl +100 -0
  41. data/examples/simple/root/shader/style.css +9 -0
  42. data/examples/simple/root/view-article.sx +30 -0
  43. data/examples/tiny/app.rb +30 -0
  44. data/examples/tiny/conf/apache.conf +100 -0
  45. data/examples/tiny/conf/config.rb +67 -0
  46. data/examples/tiny/conf/requires.rb +40 -0
  47. data/examples/tiny/ctl +31 -0
  48. data/examples/tiny/logs/access_log +9 -0
  49. data/examples/tiny/logs/apache.log +9 -0
  50. data/examples/tiny/root/index.sx +35 -0
  51. data/lib/n/app/cluster.rb +219 -0
  52. data/lib/n/app/cookie.rb +86 -0
  53. data/lib/n/app/filters/autologin.rb +50 -0
  54. data/lib/n/app/fragment.rb +67 -0
  55. data/lib/n/app/handlers.rb +120 -0
  56. data/lib/n/app/handlers/code-handler.rb +184 -0
  57. data/lib/n/app/handlers/page-handler.rb +612 -0
  58. data/lib/n/app/request-part.rb +59 -0
  59. data/lib/n/app/request.rb +653 -0
  60. data/lib/n/app/script.rb +398 -0
  61. data/lib/n/app/server.rb +53 -0
  62. data/lib/n/app/session.rb +224 -0
  63. data/lib/n/app/user.rb +47 -0
  64. data/lib/n/app/webrick-servlet.rb +213 -0
  65. data/lib/n/app/webrick.rb +70 -0
  66. data/lib/n/application.rb +187 -0
  67. data/lib/n/config.rb +31 -0
  68. data/lib/n/db.rb +217 -0
  69. data/lib/n/db/README +232 -0
  70. data/lib/n/db/connection.rb +369 -0
  71. data/lib/n/db/make-release.sh +26 -0
  72. data/lib/n/db/managed.rb +235 -0
  73. data/lib/n/db/mixins.rb +282 -0
  74. data/lib/n/db/mysql.rb +342 -0
  75. data/lib/n/db/psql.rb +378 -0
  76. data/lib/n/db/tools.rb +110 -0
  77. data/lib/n/db/utils.rb +99 -0
  78. data/lib/n/events.rb +118 -0
  79. data/lib/n/l10n.rb +22 -0
  80. data/lib/n/logger.rb +33 -0
  81. data/lib/n/macros.rb +53 -0
  82. data/lib/n/mixins.rb +46 -0
  83. data/lib/n/parts.rb +154 -0
  84. data/lib/n/properties.rb +194 -0
  85. data/lib/n/server.rb +61 -0
  86. data/lib/n/server/PLAYBACK.txt +8 -0
  87. data/lib/n/server/RESEARCH.txt +13 -0
  88. data/lib/n/server/filter.rb +77 -0
  89. data/lib/n/shaders.rb +167 -0
  90. data/lib/n/sitemap.rb +188 -0
  91. data/lib/n/std.rb +69 -0
  92. data/lib/n/sync/clc.rb +108 -0
  93. data/lib/n/sync/handler.rb +221 -0
  94. data/lib/n/sync/server.rb +170 -0
  95. data/lib/n/tools/README +11 -0
  96. data/lib/n/ui/date-select.rb +74 -0
  97. data/lib/n/ui/pager.rb +187 -0
  98. data/lib/n/ui/popup.rb +45 -0
  99. data/lib/n/ui/select.rb +41 -0
  100. data/lib/n/ui/tabs.rb +34 -0
  101. data/lib/n/utils/array.rb +92 -0
  102. data/lib/n/utils/cache.rb +144 -0
  103. data/lib/n/utils/gfx.rb +108 -0
  104. data/lib/n/utils/hash.rb +148 -0
  105. data/lib/n/utils/html.rb +147 -0
  106. data/lib/n/utils/http.rb +98 -0
  107. data/lib/n/utils/mail.rb +28 -0
  108. data/lib/n/utils/number.rb +31 -0
  109. data/lib/n/utils/pool.rb +66 -0
  110. data/lib/n/utils/string.rb +297 -0
  111. data/lib/n/utils/template.rb +38 -0
  112. data/lib/n/utils/time.rb +91 -0
  113. data/lib/n/utils/uri.rb +193 -0
  114. data/lib/xsl/base.xsl +205 -0
  115. data/lib/xsl/ce.xsl +30 -0
  116. data/lib/xsl/localization.xsl +23 -0
  117. data/lib/xsl/xforms.xsl +26 -0
  118. data/test/run.rb +95 -0
  119. metadata +187 -0
@@ -0,0 +1,232 @@
1
+ = n/db README
2
+
3
+ == Table of Contents
4
+
5
+ 1. Introduction
6
+ 2. Features
7
+ 3. Sample Code
8
+ 4. Technical Information
9
+ 5. Links
10
+ 6. Current Version and Status
11
+ 7. Feedback
12
+ 8. Licence
13
+
14
+
15
+ == Introduction
16
+
17
+ The aim of this project is to deliver an efficient, yet simple,
18
+ Object-Relational Mapper Library. The library manages the
19
+ lifecycle of standard Ruby objects. Unlike similar projects, our
20
+ approach requires neither xml configuration files (i.e. J2EE), nor
21
+ SQL definitions (i.e. ActiveRecord).
22
+
23
+ By defining the Managed Objects (or Entities) using Ruby, we
24
+ leverage Ruby's OOP features to allow for OO definitions of DataBase
25
+ schemas. A simple meta-language is provided to allow fine-tuning
26
+ of the generated SQL code.
27
+
28
+ This library is designed to be integrated in a Web Application
29
+ Framework. This Framework will be released on a later day.
30
+
31
+
32
+ == Features
33
+
34
+ The library provides the following features:
35
+
36
+ + Object-Relational mapping
37
+ + Deserialize to Ruby Objects or ResultSets
38
+ + Deserialize sql join queries to Ruby Objects
39
+ + Serialize arbitrary ruby object graphs through Marshal
40
+ + Connection pooling
41
+ + Thread safety
42
+ + SQL transactions
43
+ + Callbacks
44
+ + Simple support for cascading deletes
45
+ + Hierarchical structures (preorder traversal, materialized paths)
46
+ + Works safely as part of distributed application.
47
+ + Simple implementation < 10k lines of code.
48
+
49
+ == Sample Code
50
+
51
+ require "n/db"
52
+
53
+ # initialize the DB interface
54
+ $db = N::Db.new(
55
+ :address => "localhost",
56
+ :database => "test",
57
+ :user => "postgres",
58
+ :password => "mypassword",
59
+ :connection_count => 20
60
+ )
61
+
62
+ # create a managed object
63
+
64
+ class MyObject
65
+ include N::Entity
66
+ manage {
67
+ prop String, :name
68
+ prop [Fixnum, "smallint default 1"], :level; sql_index :level
69
+ }
70
+ # non serialized attributes
71
+ attr_accessor value
72
+ end
73
+
74
+ class AnotherObject < MyObject
75
+ manage {
76
+ prop Time, :create_time
77
+ }
78
+
79
+ def initialize
80
+ @create_time = Time.now
81
+ super
82
+ end
83
+ end
84
+
85
+ obj = AnotherObject.new
86
+ obj.name = "gmosx"
87
+
88
+ # insert into db
89
+ $db << obj
90
+
91
+ obj.name = "drak"
92
+
93
+ # update
94
+ $db << obj
95
+
96
+ # get object id allocated when inserting
97
+ oid = obj.oid
98
+
99
+ # lookup
100
+ obj = $db.get(oid, AnotherObject)
101
+
102
+ # multiple commands
103
+
104
+ $db.open { |db|
105
+ obj = db.get(oid, AnotherObject)
106
+ db.delete(obj)
107
+ db.select("SELECT * FROM #{AnotherObject::DBTABLE} WHERE oid=1")
108
+ }
109
+
110
+ # graph like objects
111
+
112
+ class Child
113
+ include N::Entity
114
+ include N::Child
115
+ manage {
116
+ prop Time, :create_time
117
+ }
118
+ end
119
+
120
+ child = Child.new
121
+ child.set_parent(obj)
122
+
123
+ $db << child
124
+
125
+ # get children of class Child
126
+
127
+ $db.children(obj, Child, "ORDER BY create_time")
128
+
129
+ # sql transactions
130
+
131
+ $db.transaction {
132
+ ...
133
+ }
134
+
135
+ # Lets say you have changed the definition of a managed object
136
+ # (for example you have added a new prop_accessor, and want to
137
+ # automatically update the database schema:
138
+ #
139
+
140
+ $db.alter_table(MyObject)
141
+
142
+
143
+ == Technical Information
144
+
145
+ === Installation
146
+
147
+ Just uncompress the distribution and add the 'n' directory to
148
+ the Ruby path.
149
+
150
+ tar xvfz ndb-x.x.tar.gz
151
+ cd ndb
152
+ ruby n/.tc-db.rb
153
+
154
+ === How the package is organized:
155
+
156
+ n/db.rb:
157
+ The main n/db file.
158
+
159
+ n/db/*:
160
+ The db related files.
161
+
162
+ n/utils/*:
163
+ Various utility files.
164
+
165
+ === Testing:
166
+
167
+ Unit Test files start with the .tc- prefix, i.e. they are hidden
168
+ by default (Unix). You can run the db test unit as follows:
169
+
170
+ ruby n/.tc-db.rb
171
+
172
+ Before running this test unit, make sure tha the postgreSQL server
173
+ is running, and the 'test' database is created.
174
+
175
+
176
+ == Links
177
+
178
+ * http://www.navel.gr, Navel
179
+ * http://www.navel.gr/open-source, Project Home Page
180
+
181
+
182
+ == Current Version and Status
183
+
184
+ Version 0.3 was released on 15-10-2004. The source is cleaned up and
185
+ the API refined. Added preliminary support for multiple backends (the
186
+ included MySQL backend is not completed). Added support for
187
+ marshaled fields. Available as a RubyGem too.
188
+
189
+ Version 0.2 was released on 07-10-2004. The sofware is actually usable
190
+ but not tested in a production environment. Comments from the Ruby
191
+ community are critical in order to fix possible bugs and improve the
192
+ API. Suggestions for missing features are also welcome. This version
193
+ only supports the Postgres Database.
194
+
195
+ === Contributors
196
+
197
+ * George Moschovitis <gm@navel.gr>: design, lead developer.
198
+
199
+ === Future
200
+
201
+ * Support multiple DataBase backends (Postgres, MySQL, ...)
202
+ * Support prepared statements (pgsql)
203
+ * Support stored procedures (pgsql)
204
+ * Support caching
205
+ * Deserialize to OpenStruct
206
+ * Better documentation
207
+ * Code cleanup, refactoring
208
+ * Release as Gem, RPAbase
209
+
210
+
211
+ == Feedback
212
+
213
+ If you would like to report a bug, suggest a method for inclusion, or make
214
+ some other suggestion (documentation, package layout, etc.), please send
215
+ an email to ndb-feedback@navel.gr
216
+
217
+
218
+ == Licence
219
+
220
+ Copyright (c) 2004 Navel, all rights reserved. http://www.navel.gr
221
+
222
+ n/db (http://www.navel.gr/open-source) is copyrighted free software
223
+ created and maintained by George Moschovitis (mailto:gm@navel.gr)
224
+ and released under the same license as Ruby.
225
+
226
+ Standard disclaimer:
227
+
228
+ THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
229
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
230
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
231
+ PURPOSE.
232
+
@@ -0,0 +1,369 @@
1
+ # = Database Connection
2
+ #
3
+ # Encapsulates a database connection.
4
+ #
5
+ # === Design
6
+ #
7
+ # Try to make the methods to work with oids.
8
+ # Do NOT implement descendants, use a root id (rid).
9
+ #
10
+ # === Todo
11
+ #
12
+ # - support caching
13
+ # - support prepared statements
14
+ # - foreign keys (delete cascade)
15
+ # - keep attribudes as marshaled!!
16
+ #
17
+ # === Investigate
18
+ #
19
+ # - should we use retry_query ?
20
+ #
21
+ # code:
22
+ # George Moschovitis <gm@navel.gr>
23
+ #
24
+ # (c) 2004 Navel, all rights reserved.
25
+ # $Id: connection.rb 79 2004-10-18 16:38:19Z gmosx $
26
+
27
+ module N;
28
+
29
+ require "n/properties"
30
+ require "n/utils/array"
31
+ require "n/utils/time"
32
+
33
+ # = DbConnection
34
+ #
35
+ # A Connection to the Database.
36
+ #
37
+ class DbConnection
38
+ include N::DbUtils
39
+
40
+ # the actual connection to the database.
41
+ attr_reader :rdb
42
+
43
+ # If set to true, the select methods deserialize the rows to
44
+ # create entities.
45
+ attr_accessor :deserialize
46
+
47
+ # Initializate a connection to the database.
48
+ #
49
+ def initialize(config)
50
+ super
51
+ @deserialize = true
52
+ $log.debug "Created connection."
53
+ end
54
+
55
+ # Close the connection to the database.
56
+ #
57
+ def close()
58
+ super
59
+ $log.debug "Closed connection."
60
+ end
61
+
62
+ # --------------------------------------------------------------------
63
+ # Basic methods
64
+
65
+ # Put an entity to the database. Insert if this is a new entity or
66
+ # update if this is an existing entity.
67
+ #
68
+ def put(entity)
69
+ if entity.oid
70
+ # entity allready inserted, update!
71
+ entity.__db_update(self)
72
+ else
73
+ # this is a new entity, insert!
74
+ entity.__db_insert(self)
75
+ end
76
+ end
77
+ alias_method :<<, :put
78
+
79
+ # Force insertion of managed object.
80
+ # Typically used for NON-entities.
81
+ #
82
+ def insert(obj)
83
+ obj.__db_insert(self)
84
+ end
85
+
86
+ # Force update of managed object.
87
+ # Typically used for relations.
88
+ #
89
+ def update(obj)
90
+ obj.__db_update(self)
91
+ end
92
+
93
+ # Update only specific fields of the entity
94
+ #
95
+ # Input:
96
+ # sql = the sql code to updated the properties.
97
+ #
98
+ def update_properties(update_sql, ent_or_oid, klass = nil)
99
+ oid = ent_or_oid.to_i()
100
+ klass = ent_or_oid.class unless klass
101
+
102
+ sql = "UPDATE #{klass::DBTABLE} SET #{update_sql} WHERE oid=#{oid}"
103
+ retry_query(sql, klass)
104
+ end
105
+ alias_method :pupdate, :update_properties
106
+
107
+ # Get an entity from the database.
108
+ #
109
+ # COOL: klass can be the class to fetch or the DBTABLE of the class.
110
+ # thanks to duck typing it works correctly!
111
+ #
112
+ # Input:
113
+ # oid = the entity oid, OR the entity name.
114
+ #
115
+ def get(oid, klass)
116
+ if oid.to_i > 0 # a valid Fixnum ?
117
+ get_by_oid(oid, klass)
118
+ else
119
+ get_by_name(oid, klass)
120
+ end
121
+ end
122
+
123
+ # Get an entity by oid.
124
+ #
125
+ # COOL: klass can be the class to fetch or the DBTABLE of the class.
126
+ # thanks to duck typing it works correctly!
127
+ #
128
+ def get_by_oid(oid, klass)
129
+ rows = safe_query("SELECT * FROM #{DbUtils.sql_table(klass)} WHERE oid=#{oid}")
130
+ return deserialize_one(rows, klass)
131
+ end
132
+
133
+ # Get an entity by name.
134
+ #
135
+ # COOL: klass can be the class to fetch or the DBTABLE of the class.
136
+ # thanks to duck typing it works correctly!
137
+ #
138
+ def get_by_name(name, klass)
139
+ rows = safe_query("SELECT * FROM #{DbUtils.sql_table(klass)} WHERE name='#{name}'")
140
+ return deserialize_one(rows, klass)
141
+ end
142
+
143
+ # Get all entities of the given klass.
144
+ #
145
+ # COOL: klass can be the class to fetch or the DBTABLE of the class.
146
+ # thanks to duck typing it works correctly!
147
+ #
148
+ # Used to be called 'collect' in an earlier version.
149
+ #
150
+ def get_all(klass, extrasql = nil)
151
+ rows = safe_query("SELECT * FROM #{DbUtils.sql_table(klass)} #{extrasql}")
152
+ return deserialize_all(rows, klass)
153
+ end
154
+
155
+ # Delete an entity from the database. Allways perform a deep delete.
156
+ #
157
+ # DONT use ::DBTABLE (allow classes as table names).
158
+ #
159
+ # Input:
160
+ #
161
+ # entity = Entity or oid to delete.
162
+ # klass = klass of entity (can be nil if an entity is passed)
163
+ #
164
+ def delete(entity, klass = nil, cascade = true)
165
+ if entity.is_a?(Fixnum)
166
+ oid = entity
167
+ entity = klass.new
168
+ entity.oid = oid
169
+ end
170
+
171
+ transaction {
172
+ entity.__db_pre_delete(self)
173
+ exec_clear("DELETE FROM #{DbUtils.sql_table(entity.class)} WHERE oid=#{entity.oid}")
174
+ }
175
+ end
176
+ alias_method :delete!, :delete
177
+
178
+ # Recursively delete all descendants of this entity.
179
+ # Operates in a transaction.
180
+ #
181
+ def delete_descendants(pid, pclass)
182
+ return unless pclass.respond_to?(:__descendants_classes)
183
+
184
+ for dclass in pclass.__descendants_classes
185
+ if dclass.include?(N::ParentClass)
186
+ rs = exec("SELECT oid FROM #{dclass::DBTABLE} WHERE pid=#{pid} AND pclass='#{pclass}'")
187
+ exec_clear("DELETE FROM #{dclass::DBTABLE} where pid=#{pid} AND pclass='#{pclass}'")
188
+ else
189
+ rs = exec("SELECT oid FROM #{dclass::DBTABLE} WHERE pid=#{pid}")
190
+ exec_clear("DELETE FROM #{dclass::DBTABLE} where pid=#{pid}")
191
+ end
192
+
193
+ for tuple in (0...rs.num_tuples)
194
+ delete_descendants(rs.getvalue(tuple, 0), dclass)
195
+ end
196
+
197
+ rs.clear()
198
+ end
199
+ end
200
+ alias_method :delete_descendants!, :delete_descendants
201
+
202
+ # --------------------------------------------------------------------
203
+ # Graph methods
204
+
205
+ # = child
206
+ #
207
+ # Return one children of the parent
208
+ #
209
+ def child(entity, klass, extrasql = nil)
210
+ pid = entity.to_i()
211
+
212
+ sql = "SELECT * FROM #{klass::DBTABLE}"
213
+ sql << " WHERE pid=#{pid}" if pid > 0
214
+ sql << " #{extrasql}" if extrasql
215
+
216
+ rows = safe_query(sql)
217
+
218
+ return deserialize_one(rows, klass)
219
+ end
220
+
221
+ # = children
222
+ #
223
+ # entity = entity or oid.
224
+ # klass = the class of the children to return.
225
+ # extrasql = extra sql for limit/order etc.
226
+ #
227
+ # === Design:
228
+ #
229
+ # If the calculated pid is 0 returns all entities
230
+ # of the given class. It works like the older collect method.
231
+ # This make sense, the root of all objects has an oid = 0.
232
+ # INVESTIGATE: is this a security problem?
233
+ #
234
+ # only_oids is not needed use the limit modifiers instead.
235
+ # if you need oids, use specialized sql. You can use the
236
+ # extrasql parameter to change the where clause too.
237
+ #
238
+ def children(entity, klass, extrasql = nil)
239
+ pid = entity.to_i()
240
+
241
+ sql = "SELECT * FROM #{klass::DBTABLE}"
242
+ sql << " WHERE pid=#{pid}" if pid > 0
243
+ sql << " #{extrasql}" if extrasql
244
+
245
+ rows = safe_query(sql)
246
+
247
+ return deserialize_all(rows, klass)
248
+ end
249
+
250
+ # = count_children
251
+ #
252
+ # Use extrasql to change the where clause.
253
+ #
254
+ def count_children(entity, klass, extasql = nil)
255
+ pid = entity.to_i()
256
+
257
+ sql = "SELECT COUNT(pid) FROM #{klass::DBTABLE}"
258
+ sql << " WHERE pid=#{pid}" if pid > 0
259
+
260
+ if rows = safe_query(sql)
261
+ return rows[0][0].to_i()
262
+ end
263
+
264
+ return 0
265
+ end
266
+
267
+ # --------------------------------------------------------------------
268
+ # Transaction methods.
269
+
270
+ def start
271
+ exec_clear("START TRANSACTION")
272
+ end
273
+
274
+ def commit
275
+ exec_clear("COMMIT")
276
+ end
277
+
278
+ def rollback
279
+ exec_clear("ROLLBACK")
280
+ end
281
+
282
+ # Transaction helper
283
+ #
284
+ def transaction(&block)
285
+ begin
286
+ exec_clear("START TRANSACTION")
287
+ yield
288
+ exec_clear("COMMIT")
289
+ rescue => ex
290
+ $log.error "DB Error: ERROR IN TRANSACTION"
291
+ $log.error "DB Error: #{ex}, #{caller[0]} : #{caller[1]} : #{caller[2]}"
292
+ exec_clear("ROLLBACK")
293
+ end
294
+ end
295
+
296
+ # Useful in transactions
297
+ #
298
+ def exec(sql)
299
+ @rdb.exec(sql)
300
+ end
301
+
302
+ # Useful in transactions
303
+ # Exec an sql command and clear the resultset.
304
+ #
305
+ def exec_and_clear(sql)
306
+ rows = @rdb.exec(sql)
307
+ rows.clear()
308
+ end
309
+ alias_method :exec_clear, :exec_and_clear
310
+
311
+ # --------------------------------------------------------------------
312
+ # Standard SQL methods.
313
+
314
+ # Perform a standard SQL query to the database. Deserializes the
315
+ # results.
316
+ #
317
+ def select(sql, klass, join_fields = nil)
318
+ rows = safe_query(sql)
319
+ return deserialize_all(rows, klass, join_fields)
320
+ end
321
+
322
+ # Optimized for one result.
323
+ #
324
+ def select_one(sql, klass, join_fields = nil)
325
+ rows = safe_query(sql)
326
+ return deserialize_one(rows, klass, join_fields)
327
+ end
328
+
329
+ # Perform a standard SQL query to the database. Returns the result
330
+ # rows.
331
+ #
332
+ def query(sql)
333
+ return safe_query(sql)
334
+ end
335
+ alias_method :sql, :query
336
+
337
+ # Perform a standard SQL query to the database. Used for queries that
338
+ # return one row (typically with values) ie COUNT, etc.
339
+ #
340
+ def query_one(sql)
341
+ if rows = safe_query(sql)
342
+ return rows[0]
343
+ else
344
+ return nil
345
+ end
346
+ end
347
+
348
+ # Count the entities returned by the sql statement.
349
+ #
350
+ def count(sql)
351
+ if rows = safe_query(sql)
352
+ return rows[0][0].to_i()
353
+ end
354
+
355
+ return 0
356
+ end
357
+
358
+ # Match a query, return true if a resultset was found
359
+ #
360
+ def match(sql)
361
+ res = safe_query(sql)
362
+ return true if res and (res.num_tuples > 0)
363
+ end
364
+ alias_method :match?, :match
365
+
366
+ end
367
+
368
+ end # module
369
+