spqr 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +12 -0
- data/VERSION +1 -1
- data/lib/rhubarb/rhubarb.rb +50 -32
- metadata +2 -2
data/CHANGES
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
* This is a release featuring only minor enhancements to Rhubarb. If
|
2
|
+
you don't use Rhubarb, there is no need to upgrade.
|
3
|
+
|
4
|
+
* Rhubarb classes now support a delete_all method.
|
5
|
+
|
6
|
+
* Rhubarb now supports multiple backend databases, via an extra
|
7
|
+
parameter to Persistence::open and to the create_table class methods.
|
8
|
+
Note that it is not currently possible to have one class backed by
|
9
|
+
multiple databases.
|
10
|
+
|
11
|
+
version 0.1.2 (2c45fd4d693a396e9206607a6f053b1eb4696272)
|
12
|
+
|
1
13
|
* Enhancements to SPQR/Rhubarb interoperability. (Rhubarb row_ids are
|
2
14
|
now used for half of the QMF object ID).
|
3
15
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.3
|
data/lib/rhubarb/rhubarb.rb
CHANGED
@@ -27,29 +27,39 @@ end
|
|
27
27
|
|
28
28
|
|
29
29
|
module Persistence
|
30
|
-
|
30
|
+
class DbCollection < Hash
|
31
|
+
alias orig_set []=
|
32
|
+
|
33
|
+
def []=(k,v)
|
34
|
+
v.results_as_hash = true if v
|
35
|
+
v.type_translation = true if v
|
36
|
+
orig_set(k,v)
|
37
|
+
end
|
38
|
+
end
|
31
39
|
|
32
|
-
|
33
|
-
|
40
|
+
@@dbs = DbCollection.new
|
41
|
+
|
42
|
+
def self.open(filename, which=:default)
|
43
|
+
dbs[which] = SQLite3::Database.new(filename)
|
34
44
|
end
|
35
45
|
|
36
|
-
def self.close
|
37
|
-
|
38
|
-
|
46
|
+
def self.close(which=:default)
|
47
|
+
if dbs[which]
|
48
|
+
dbs[which].close
|
49
|
+
dbs.delete(which)
|
50
|
+
end
|
39
51
|
end
|
40
52
|
|
41
53
|
def self.db
|
42
|
-
|
54
|
+
dbs[:default]
|
43
55
|
end
|
44
|
-
|
56
|
+
|
45
57
|
def self.db=(d)
|
46
|
-
|
47
|
-
self.db.results_as_hash = true if d != nil
|
48
|
-
self.db.type_translation = true if d != nil
|
58
|
+
dbs[:default] = d
|
49
59
|
end
|
50
|
-
|
51
|
-
def self.
|
52
|
-
|
60
|
+
|
61
|
+
def self.dbs
|
62
|
+
@@dbs
|
53
63
|
end
|
54
64
|
end
|
55
65
|
|
@@ -139,7 +149,7 @@ module PersistingClassMixins
|
|
139
149
|
select_criteria = valid_cols.map {|col| "#{col.to_s} = #{col.inspect}"}.join(" AND ")
|
140
150
|
arg_hash.each {|k,v| arg_hash[k] = v.row_id if v.respond_to? :row_id}
|
141
151
|
|
142
|
-
|
152
|
+
self.db.execute("select * from #{table_name} where #{select_criteria} order by row_id", arg_hash).map {|tup| self.new(tup) }
|
143
153
|
end
|
144
154
|
|
145
155
|
# args contains the following keys
|
@@ -186,12 +196,16 @@ SELECT __freshest.* FROM (
|
|
186
196
|
ORDER BY row_id
|
187
197
|
"
|
188
198
|
|
189
|
-
|
199
|
+
self.db.execute(query, query_params).map {|tup| self.new(tup) }
|
190
200
|
end
|
191
201
|
|
192
202
|
# Does what it says on the tin. Since this will allocate an object for each row, it isn't recomended for huge tables.
|
193
203
|
def find_all
|
194
|
-
|
204
|
+
self.db.execute("SELECT * from #{table_name}").map {|tup| self.new(tup)}
|
205
|
+
end
|
206
|
+
|
207
|
+
def delete_all
|
208
|
+
self.db.execute("DELETE from #{table_name}")
|
195
209
|
end
|
196
210
|
|
197
211
|
# Declares a query method named +name+ and adds it to this class. The query method returns a list of objects corresponding to the rows returned by executing "+SELECT * FROM+ _table_ +WHERE+ _query_" on the database.
|
@@ -202,7 +216,7 @@ SELECT __freshest.* FROM (
|
|
202
216
|
# handle reference parameters
|
203
217
|
args = args.map {|x| (x.row_id if x.class.ancestors.include? Persisting) or x}
|
204
218
|
|
205
|
-
res =
|
219
|
+
res = self.db.execute("select * from #{table_name} where #{query}", args)
|
206
220
|
res.map {|row| self.new(row)}
|
207
221
|
end
|
208
222
|
end
|
@@ -216,7 +230,7 @@ SELECT __freshest.* FROM (
|
|
216
230
|
# handle reference parameters
|
217
231
|
args = args.map {|x| (x.row_id if x.class.ancestors.include? Persisting) or x}
|
218
232
|
|
219
|
-
res =
|
233
|
+
res = self.db.execute(query.gsub("__TABLE__", "#{self.table_name}"), args)
|
220
234
|
# XXX: should freshen each row?
|
221
235
|
res.map {|row| self.new(row) }
|
222
236
|
end
|
@@ -227,7 +241,7 @@ SELECT __freshest.* FROM (
|
|
227
241
|
@creation_callbacks << Proc.new do
|
228
242
|
idx_name = "idx_#{self.table_name}__#{fields.join('__')}__#{@creation_callbacks.size}"
|
229
243
|
creation_cmd = "create index #{idx_name} on #{self.table_name} (#{fields.join(', ')})"
|
230
|
-
|
244
|
+
self.db.execute(creation_cmd)
|
231
245
|
end if fields.size > 0
|
232
246
|
end
|
233
247
|
|
@@ -254,12 +268,12 @@ SELECT __freshest.* FROM (
|
|
254
268
|
klass = (class << self; self end)
|
255
269
|
klass.class_eval do
|
256
270
|
define_method find_method_name do |arg|
|
257
|
-
res =
|
271
|
+
res = self.db.execute("select * from #{table_name} where #{cname} = ?", arg)
|
258
272
|
res.map {|row| self.new(row)}
|
259
273
|
end
|
260
274
|
|
261
275
|
define_method find_first_method_name do |arg|
|
262
|
-
res =
|
276
|
+
res = self.db.execute("select * from #{table_name} where #{cname} = ?", arg)
|
263
277
|
return self.new(res[0]) if res.size > 0
|
264
278
|
nil
|
265
279
|
end
|
@@ -311,9 +325,9 @@ SELECT __freshest.* FROM (
|
|
311
325
|
insert_trigger_name = "ri_insert_#{self.table_name}_#{@ccount}_#{rf.referent.table_name}"
|
312
326
|
delete_trigger_name = "ri_delete_#{self.table_name}_#{@ccount}_#{rf.referent.table_name}"
|
313
327
|
|
314
|
-
|
328
|
+
self.db.execute_batch("CREATE TRIGGER #{insert_trigger_name} BEFORE INSERT ON \"#{self.table_name}\" WHEN new.\"#{cname}\" IS NOT NULL AND NOT EXISTS (SELECT 1 FROM \"#{rf.referent.table_name}\" WHERE new.\"#{cname}\" == \"#{rf.column}\") BEGIN SELECT RAISE(ABORT, 'constraint #{insert_trigger_name} (#{rf.referent.table_name} missing foreign key row) failed'); END;")
|
315
329
|
|
316
|
-
|
330
|
+
self.db.execute_batch("CREATE TRIGGER #{delete_trigger_name} BEFORE DELETE ON \"#{rf.referent.table_name}\" WHEN EXISTS (SELECT 1 FROM \"#{self.table_name}\" WHERE old.\"#{rf.column}\" == \"#{cname}\") BEGIN DELETE FROM \"#{self.table_name}\" WHERE \"#{cname}\" = old.\"#{rf.column}\"; END;") if rf.options[:on_delete] == :cascade
|
317
331
|
|
318
332
|
@ccount = @ccount + 1
|
319
333
|
end
|
@@ -344,7 +358,7 @@ SELECT __freshest.* FROM (
|
|
344
358
|
new_row[k] = v.row_id if v.class.ancestors.include? Persisting
|
345
359
|
end
|
346
360
|
|
347
|
-
|
361
|
+
self.db.transaction do |db|
|
348
362
|
stmt = "insert into #{table_name} (#{colspec}) values (#{valspec})"
|
349
363
|
# p stmt
|
350
364
|
db.execute(stmt, new_row)
|
@@ -366,8 +380,9 @@ SELECT __freshest.* FROM (
|
|
366
380
|
end
|
367
381
|
|
368
382
|
# Creates a table in the database corresponding to this class.
|
369
|
-
def create_table
|
370
|
-
Persistence::
|
383
|
+
def create_table(dbkey=:default)
|
384
|
+
self.db = Persistence::dbs[dbkey]
|
385
|
+
self.db.execute(table_decl)
|
371
386
|
@creation_callbacks.each {|func| func.call}
|
372
387
|
end
|
373
388
|
|
@@ -382,7 +397,7 @@ SELECT __freshest.* FROM (
|
|
382
397
|
# The API purposefully does not expose the ability to create a
|
383
398
|
# row with a given id, and created and updated values are
|
384
399
|
# maintained automatically by the API.
|
385
|
-
attr_accessor :columns, :colnames, :constraints, :dirtied, :refs, :creation_callbacks
|
400
|
+
attr_accessor :columns, :colnames, :constraints, :dirtied, :refs, :creation_callbacks, :db
|
386
401
|
end
|
387
402
|
end
|
388
403
|
|
@@ -397,12 +412,12 @@ SELECT __freshest.* FROM (
|
|
397
412
|
|
398
413
|
# Returns the number of rows in the table backing this class
|
399
414
|
def count
|
400
|
-
result =
|
415
|
+
result = self.db.execute("select count(row_id) from #{table_name}")[0]
|
401
416
|
result[0].to_i
|
402
417
|
end
|
403
418
|
|
404
419
|
def find_tuple(id)
|
405
|
-
res =
|
420
|
+
res = self.db.execute("select * from #{table_name} where row_id = ?", id)
|
406
421
|
if res.size == 0
|
407
422
|
nil
|
408
423
|
else
|
@@ -424,6 +439,9 @@ module Persisting
|
|
424
439
|
end
|
425
440
|
end
|
426
441
|
|
442
|
+
def db
|
443
|
+
self.class.db
|
444
|
+
end
|
427
445
|
|
428
446
|
# Returns true if the row backing this object has been deleted from the database
|
429
447
|
def deleted?
|
@@ -448,7 +466,7 @@ module Persisting
|
|
448
466
|
# Deletes the row corresponding to this object from the database;
|
449
467
|
# invalidates =self= and any other objects backed by this row
|
450
468
|
def delete
|
451
|
-
|
469
|
+
self.db.execute("delete from #{self.class.table_name} where row_id = ?", @row_id)
|
452
470
|
mark_dirty
|
453
471
|
@tuple = nil
|
454
472
|
@row_id = nil
|
@@ -496,7 +514,7 @@ module Persisting
|
|
496
514
|
# Helper method to update the row in the database when one of our fields changes
|
497
515
|
def update(attr_name, value)
|
498
516
|
mark_dirty
|
499
|
-
|
517
|
+
self.db.execute("update #{self.class.table_name} set #{attr_name} = ?, updated = ? where row_id = ?", value, SQLBUtil::timestamp, @row_id)
|
500
518
|
end
|
501
519
|
|
502
520
|
# Resolve any fields that reference other tables, replacing row ids with referred objects
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spqr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- William Benton
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2010-01-26 00:00:00 -06:00
|
13
13
|
default_executable: spqr-gen.rb
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|