spqr 0.1.2 → 0.1.3
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/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
|