spqr 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +16 -0
- data/VERSION +1 -1
- data/bin/spqr-gen.rb +5 -1
- data/lib/rhubarb/rhubarb.rb +18 -5
- data/lib/spqr/app.rb +6 -4
- data/lib/spqr/codegen.rb +65 -58
- data/lib/spqr/manageable.rb +3 -1
- data/test/helper.rb +1 -1
- data/test/test_rhubarb.rb +25 -1
- data/test/test_spqr_integerprop.rb +0 -1
- metadata +2 -2
data/CHANGES
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
version 0.1.1
|
2
|
+
|
3
|
+
* Other minor Rhubarb fixes.
|
4
|
+
|
5
|
+
* Rhubarb now properly handles persisting classes declared in
|
6
|
+
different modules and supports defining custom table names.
|
7
|
+
|
8
|
+
* spqr-gen now has preliminary support for generating classes that
|
9
|
+
persist via rhubarb.
|
10
|
+
|
11
|
+
* Fixes and cleanups to class- and package-name metadata. Previously,
|
12
|
+
the app skeleton would fail to find some manageable classes
|
13
|
+
declared in separate Ruby modules. In addition, the manageable
|
14
|
+
mixin now sets sensible default names for the QMF class and package
|
15
|
+
(when one is not provided within the class declaration).
|
16
|
+
|
1
17
|
version 0.1.0 (3a3ca52c4f086d1f20fdf5ed89dda262622c171d)
|
2
18
|
|
3
19
|
* Note that this version breaks backwards compatibility, for reasons
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.1
|
data/bin/spqr-gen.rb
CHANGED
@@ -18,8 +18,8 @@ $: << File.expand_path(File.dirname(__FILE__))
|
|
18
18
|
require 'spqr/spqr'
|
19
19
|
|
20
20
|
def main
|
21
|
-
$WRITEMODE
|
22
21
|
$OUTDIR = "."
|
22
|
+
$DO_RHUBARB = false
|
23
23
|
|
24
24
|
op = OptionParser.new do |opts|
|
25
25
|
opts.banner = "Usage spqr.rb [options] schema-file"
|
@@ -31,6 +31,10 @@ def main
|
|
31
31
|
opts.on("-d", "--output-dir DIR", "directory in which to place generated app and controllers") do |dir|
|
32
32
|
$OUTDIR = dir
|
33
33
|
end
|
34
|
+
|
35
|
+
opts.on("-r", "--rhubarb", "enable support for rhubarb in generated code") do
|
36
|
+
$DO_RHUBARB = true
|
37
|
+
end
|
34
38
|
end
|
35
39
|
|
36
40
|
begin
|
data/lib/rhubarb/rhubarb.rb
CHANGED
@@ -102,10 +102,16 @@ end
|
|
102
102
|
# Methods mixed in to the class object of a persisting class
|
103
103
|
module PersistingClassMixins
|
104
104
|
# Returns the name of the database table modeled by this class.
|
105
|
+
# Defaults to the name of the class (sans module names)
|
105
106
|
def table_name
|
106
|
-
self.name.downcase
|
107
|
+
@table_name ||= self.name.split("::").pop.downcase
|
107
108
|
end
|
108
109
|
|
110
|
+
# Enables setting the table name to a custom name
|
111
|
+
def declare_table_name(nm)
|
112
|
+
@table_name = nm
|
113
|
+
end
|
114
|
+
|
109
115
|
# Models a foreign-key relationship. +options+ is a hash of options, which include
|
110
116
|
# +:column => + _name_:: specifies the name of the column to reference in +klass+ (defaults to row id)
|
111
117
|
# +:on_delete => :cascade+:: specifies that deleting the referenced row in +klass+ will delete all rows referencing that row through this reference
|
@@ -131,7 +137,7 @@ module PersistingClassMixins
|
|
131
137
|
select_criteria = valid_cols.map {|col| "#{col.to_s} = #{col.inspect}"}.join(" AND ")
|
132
138
|
arg_hash.each {|k,v| arg_hash[k] = v.row_id if v.respond_to? :row_id}
|
133
139
|
|
134
|
-
Persistence::execute("select * from #{table_name} where #{select_criteria}", arg_hash).map {|tup| self.new(tup) }
|
140
|
+
Persistence::execute("select * from #{table_name} where #{select_criteria} order by row_id", arg_hash).map {|tup| self.new(tup) }
|
135
141
|
end
|
136
142
|
|
137
143
|
# args contains the following keys
|
@@ -297,10 +303,17 @@ SELECT __freshest.* FROM (
|
|
297
303
|
# Note that we do not support update triggers, since the API does
|
298
304
|
# not expose the capacity to change row IDs.
|
299
305
|
|
300
|
-
self.creation_callbacks << Proc.new do
|
301
|
-
|
306
|
+
self.creation_callbacks << Proc.new do
|
307
|
+
@ccount ||= 0
|
308
|
+
|
309
|
+
insert_trigger_name = "ri_insert_#{self.table_name}_#{@ccount}_#{rf.referent.table_name}"
|
310
|
+
delete_trigger_name = "ri_delete_#{self.table_name}_#{@ccount}_#{rf.referent.table_name}"
|
302
311
|
|
303
|
-
Persistence::db.execute_batch("CREATE TRIGGER
|
312
|
+
Persistence::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;")
|
313
|
+
|
314
|
+
Persistence::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
|
315
|
+
|
316
|
+
@ccount = @ccount + 1
|
304
317
|
end
|
305
318
|
end
|
306
319
|
end
|
data/lib/spqr/app.rb
CHANGED
@@ -52,14 +52,14 @@ module SPQR
|
|
52
52
|
manageable_ks = ks.select {|kl| manageable? kl}
|
53
53
|
unmanageable_ks = ks.select {|kl| not manageable? kl}
|
54
54
|
manageable_ks.each do |klass|
|
55
|
-
@log.info("SPQR will manage registered class #{klass} (#{klass.
|
55
|
+
@log.info("SPQR will manage registered class #{klass} (#{klass.spqr_meta.classname})...")
|
56
56
|
|
57
57
|
schemaclass = schematize(klass)
|
58
58
|
|
59
59
|
klass.log = @log
|
60
60
|
|
61
61
|
@classes_by_id[klass.class_id] = klass
|
62
|
-
@classes_by_name[klass.
|
62
|
+
@classes_by_name[klass.spqr_meta.classname.to_s] = ClassMeta.new(klass, schemaclass)
|
63
63
|
end
|
64
64
|
|
65
65
|
unmanageable_ks.each do |klass|
|
@@ -125,6 +125,8 @@ module SPQR
|
|
125
125
|
def get_query(context, query, user_id)
|
126
126
|
@log.debug "query: user=#{user_id} context=#{context} class=#{query.class_name} object_num=#{query.object_id.object_num_low if query.object_id} details=#{query} haveSelect=#{query.impl and query.impl.haveSelect} getSelect=#{query.impl and query.impl.getSelect} (#{query.impl and query.impl.getSelect and query.impl.getSelect.methods.inspect})"
|
127
127
|
|
128
|
+
@log.debug "classes_by_name is #{@classes_by_name.inspect}"
|
129
|
+
|
128
130
|
cmeta = @classes_by_name[query.class_name]
|
129
131
|
objs = []
|
130
132
|
|
@@ -204,7 +206,7 @@ module SPQR
|
|
204
206
|
end
|
205
207
|
|
206
208
|
def schematize(klass)
|
207
|
-
@log.info("Making a QMF schema for #{klass}")
|
209
|
+
@log.info("Making a QMF schema for #{klass.spqr_meta.classname}")
|
208
210
|
|
209
211
|
meta = klass.spqr_meta
|
210
212
|
package = meta.package.to_s
|
@@ -290,7 +292,7 @@ module SPQR
|
|
290
292
|
# turns an instance of a managed object into a QmfObject
|
291
293
|
def qmfify(obj)
|
292
294
|
@log.debug("trying to qmfify #{obj}: qmf_oid is #{obj.qmf_oid} and class_id is #{obj.class.class_id}")
|
293
|
-
cm = @classes_by_name[obj.class.
|
295
|
+
cm = @classes_by_name[obj.class.spqr_meta.classname.to_s]
|
294
296
|
return nil unless cm
|
295
297
|
|
296
298
|
qmfobj = Qmf::AgentObject.new(cm.schema_class)
|
data/lib/spqr/codegen.rb
CHANGED
@@ -168,6 +168,7 @@ module SPQR
|
|
168
168
|
private
|
169
169
|
def gen_class
|
170
170
|
pp "require 'spqr/spqr'"
|
171
|
+
pp "require 'rhubarb/rhubarb'" if $DO_RHUBARB
|
171
172
|
pp ""
|
172
173
|
|
173
174
|
@package_list.map {|pkg| pkg.capitalize}.each do |modname|
|
@@ -179,22 +180,25 @@ module SPQR
|
|
179
180
|
pkgname = (@package_list.map {|pkg| pkg.capitalize}).join("::")
|
180
181
|
fqcn = ("#{pkgname}::#{@sc.name}" if pkgname) or @sc.name
|
181
182
|
|
182
|
-
pp "include ::
|
183
|
-
pp ""
|
184
|
-
|
183
|
+
pp "include ::Rhubarb::Persisting" if $DO_RHUBARB
|
184
|
+
pp "include ::SPQR::Manageable\n"
|
185
|
+
|
185
186
|
pp "qmf_package_name '#{@package_list.join(".")}'"
|
186
187
|
pp "qmf_class_name '#{@sc.name.split("::")[-1]}'"
|
187
188
|
|
188
|
-
|
189
|
-
|
190
|
-
pp
|
191
|
-
|
192
|
-
|
193
|
-
|
189
|
+
unless $DO_RHUBARB
|
190
|
+
|
191
|
+
pp '# Find method (NB: you must implement this)'
|
192
|
+
pp_decl :def, "#{@sc.name}.find_by_id", "(objid)" do
|
193
|
+
pp "#{@sc.name}.new"
|
194
|
+
end
|
194
195
|
|
195
|
-
|
196
|
-
|
197
|
-
pp
|
196
|
+
pp ""
|
197
|
+
|
198
|
+
pp '# Find-all method (NB: you must implement this)'
|
199
|
+
pp_decl :def, "#{@sc.name}.find_all" do
|
200
|
+
pp "[#{@sc.name}.new]"
|
201
|
+
end
|
198
202
|
end
|
199
203
|
|
200
204
|
ModelClassGenerator.id_registry[fqcn.hash] = fqcn
|
@@ -223,19 +227,28 @@ module SPQR
|
|
223
227
|
end
|
224
228
|
|
225
229
|
def gen_property(property)
|
226
|
-
pp ""
|
227
|
-
|
228
|
-
|
229
|
-
pp "log.debug 'Requested property #{property.name}'"
|
230
|
-
pp "nil"
|
231
|
-
end
|
230
|
+
pp "\# property #{property.name} #{property.kind} #{property.desc}\n"
|
231
|
+
|
232
|
+
rhubarb_kind = qmf_to_rhubarb(property.kind)
|
232
233
|
|
233
|
-
|
234
|
-
|
235
|
-
pp "
|
236
|
-
pp "
|
234
|
+
if $DO_RHUBARB and rhubarb_kind
|
235
|
+
notnull = (", :not_null" if property.options[:index])
|
236
|
+
pp "declare_column :#{property.name}, #{rhubarb_kind}#{notnull}"
|
237
|
+
pp "declare_index_on :#{property.name}" if notnull
|
238
|
+
else
|
239
|
+
pp ""
|
240
|
+
pp_decl :def, "#{property.name}" do
|
241
|
+
pp "log.debug 'Requested property #{property.name}'"
|
242
|
+
pp "nil"
|
243
|
+
end
|
244
|
+
|
245
|
+
pp ""
|
246
|
+
pp_decl :def, "#{property.name}=", "(val)" do
|
247
|
+
pp "log.debug 'Set property #{property.name} to \#\{val\}'"
|
248
|
+
pp "nil"
|
249
|
+
end
|
237
250
|
end
|
238
|
-
|
251
|
+
|
239
252
|
property.options[:desc] = property.desc if property.desc
|
240
253
|
|
241
254
|
pp ""
|
@@ -261,14 +274,10 @@ module SPQR
|
|
261
274
|
pp "\# #{method.name} #{method.desc}"
|
262
275
|
method.args.each do |arg|
|
263
276
|
pp "\# * #{arg.name} (#{arg.kind}/#{arg.dir})"
|
264
|
-
pp "\#
|
277
|
+
pp "\# #{arg.desc}" if arg.desc
|
265
278
|
end
|
266
279
|
|
267
|
-
|
268
|
-
out_params = method.args.select {|arg| ['out', 'o', 'qmf::dir_out'].include? arg.dir.to_s.downcase }
|
269
|
-
inout_params = method.args.select {|arg| ['inout', 'io', 'qmf::dir_inout'].include? arg.dir.to_s.downcase }
|
270
|
-
|
271
|
-
formals_in = method.args.select {|arg| ['in','i','qmf::dir_in','io','inout','qmf::dir_inout'].include? arg.dir.to_s.downcase}.map{|arg| arg.name}.join(",")
|
280
|
+
formals_in = method.args.select {|arg| ['in','i','qmf::dir_in','io','inout','qmf::dir_inout'].include? arg.dir.to_s.downcase}.map{|arg| arg.name}
|
272
281
|
formals_out = method.args.select {|arg| ['out','o','qmf::dir_out','io','inout','qmf::dir_inout'].include? arg.dir.to_s.downcase}.map{|arg| arg.name}
|
273
282
|
|
274
283
|
actuals_out = case formals_out.size
|
@@ -282,40 +291,24 @@ module SPQR
|
|
282
291
|
acc
|
283
292
|
end
|
284
293
|
|
285
|
-
pp_decl :def, method.name, "(#{formals_in})" do
|
294
|
+
pp_decl :def, method.name, "(#{formals_in.join(",")})" do
|
286
295
|
|
287
|
-
if
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
elsif inout_params.size > 0
|
293
|
-
what << "/out"
|
294
|
-
end
|
295
|
-
|
296
|
-
pp "\# Print values of #{what} parameters"
|
297
|
-
(in_params + inout_params).each do |arg|
|
298
|
-
argdisplay = arg.name.to_s
|
299
|
-
pp('log.debug "' + "#{method.name}: #{arg.name} => " + '#{' + "#{argdisplay}" + '}"')
|
296
|
+
if formals_in.size > 0
|
297
|
+
|
298
|
+
pp "\# Print values of input parameters"
|
299
|
+
formals_in.each do |arg|
|
300
|
+
pp('log.debug "' + "#{method.name}: #{arg} => " + '#{' + "#{arg}" + '}"')
|
300
301
|
end
|
301
302
|
end
|
302
303
|
|
303
|
-
if
|
304
|
-
what = "out"
|
305
|
-
|
306
|
-
if out_params.size > 0 and inout_params.size > 0
|
307
|
-
what << " and in/out"
|
308
|
-
elsif inout_params.size > 0
|
309
|
-
what = "in/out"
|
310
|
-
end
|
304
|
+
if formals_out.size > 0
|
311
305
|
|
312
|
-
pp "\# Assign values to
|
306
|
+
pp "\# Assign values to output parameters"
|
313
307
|
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
pp "#{argdisplay} ||= #{default_val(argtype)}"
|
308
|
+
formals_out.each do |arg|
|
309
|
+
argtype = param_types[arg]
|
310
|
+
raise RuntimeError.new("Can't find type for #{arg} in #{param_types.inspect}") unless argtype
|
311
|
+
pp "#{arg} ||= #{default_val(argtype)}"
|
319
312
|
end
|
320
313
|
|
321
314
|
pp "\# Return value"
|
@@ -324,9 +317,12 @@ module SPQR
|
|
324
317
|
end
|
325
318
|
end
|
326
319
|
|
327
|
-
|
328
320
|
pp ""
|
329
321
|
pp_decl :expose, "#{method.name.to_sym.inspect} do |args|" do
|
322
|
+
in_params = method.args.select {|arg| ['in', 'i', 'qmf::dir_in'].include? arg.dir.to_s.downcase }
|
323
|
+
out_params = method.args.select {|arg| ['out', 'o', 'qmf::dir_out'].include? arg.dir.to_s.downcase }
|
324
|
+
inout_params = method.args.select {|arg| ['inout', 'io', 'qmf::dir_inout'].include? arg.dir.to_s.downcase }
|
325
|
+
|
330
326
|
{:in => in_params, :inout => inout_params, :out => out_params}.each do |dir,coll|
|
331
327
|
coll.each do |arg|
|
332
328
|
arg_nm = arg.name
|
@@ -350,6 +346,17 @@ module SPQR
|
|
350
346
|
else raise RuntimeError.new("Unknown type #{ty.inspect}")
|
351
347
|
end
|
352
348
|
end
|
349
|
+
|
350
|
+
def qmf_to_rhubarb(ty)
|
351
|
+
case ty.downcase
|
352
|
+
when 'bool' then ':boolean'
|
353
|
+
when 'double', 'float' then ':double'
|
354
|
+
when 'absTime', 'deltaTime' then ':timestamp'
|
355
|
+
when 'int16', 'int32', 'int64', 'int', 'int8', 'uint16', 'uint32', 'uint64', 'uint', 'uint8' then ':integer'
|
356
|
+
when 'lstr', 'string', 'sstr' then ':string'
|
357
|
+
else false
|
358
|
+
end
|
359
|
+
end
|
353
360
|
end
|
354
361
|
|
355
362
|
class AppBoilerplateGenerator
|
data/lib/spqr/manageable.rb
CHANGED
@@ -247,7 +247,9 @@ module SPQR
|
|
247
247
|
end
|
248
248
|
end
|
249
249
|
|
250
|
-
|
250
|
+
name_components = other.name.to_s.split("::")
|
251
|
+
other.qmf_class_name name_components.pop
|
252
|
+
other.qmf_package_name name_components.join(".").downcase
|
251
253
|
end
|
252
254
|
end
|
253
255
|
end
|
data/test/helper.rb
CHANGED
data/test/test_rhubarb.rb
CHANGED
@@ -11,7 +11,6 @@
|
|
11
11
|
#
|
12
12
|
# http://www.apache.org/licenses/LICENSE-2.0
|
13
13
|
|
14
|
-
require 'rubygems'
|
15
14
|
require 'rhubarb/rhubarb'
|
16
15
|
require 'test/unit'
|
17
16
|
|
@@ -21,6 +20,21 @@ class TestClass
|
|
21
20
|
declare_column :bar, :string
|
22
21
|
end
|
23
22
|
|
23
|
+
module RhubarbNamespace
|
24
|
+
class NMTC
|
25
|
+
include Rhubarb::Persisting
|
26
|
+
declare_column :foo, :integer
|
27
|
+
declare_column :bar, :string
|
28
|
+
end
|
29
|
+
|
30
|
+
class NMTC2
|
31
|
+
include Rhubarb::Persisting
|
32
|
+
declare_column :foo, :integer
|
33
|
+
declare_column :bar, :string
|
34
|
+
declare_table_name('namespacetestcase')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
24
38
|
class TestClass2
|
25
39
|
include Rhubarb::Persisting
|
26
40
|
declare_column :fred, :integer
|
@@ -87,6 +101,8 @@ class BackendBasicTests < Test::Unit::TestCase
|
|
87
101
|
klasses << ToRef
|
88
102
|
klasses << FromRef
|
89
103
|
klasses << FreshTestTable
|
104
|
+
klasses << RhubarbNamespace::NMTC
|
105
|
+
klasses << RhubarbNamespace::NMTC2
|
90
106
|
|
91
107
|
klasses.each { |klass| klass.create_table }
|
92
108
|
|
@@ -110,6 +126,14 @@ class BackendBasicTests < Test::Unit::TestCase
|
|
110
126
|
assert(r.managed_ref?, "managed reference should return true for managed_ref?")
|
111
127
|
end
|
112
128
|
|
129
|
+
def test_namespace_table_name
|
130
|
+
assert_equal "nmtc", RhubarbNamespace::NMTC.table_name
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_set_table_name
|
134
|
+
assert_equal "namespacetestcase", RhubarbNamespace::NMTC2.table_name
|
135
|
+
end
|
136
|
+
|
113
137
|
def test_reference_ctor_string
|
114
138
|
r = Rhubarb::Reference.new("TestClass")
|
115
139
|
assert(r.referent == "TestClass", "Referent of string-backed reference instance incorrect")
|
@@ -18,7 +18,6 @@ class TestSpqrIntegerProp < Test::Unit::TestCase
|
|
18
18
|
expected = objs[(x + 1) % QmfIntegerProp::SIZE]
|
19
19
|
o = objs[x]
|
20
20
|
next_o = o.next.result
|
21
|
-
puts ("next_o: #{next_o}")
|
22
21
|
actual = $console.object(:object_id=>next_o)
|
23
22
|
assert_equal expected.int_id, actual.int_id
|
24
23
|
end
|
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.1
|
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: 2009-12-
|
12
|
+
date: 2009-12-16 00:00:00 -06:00
|
13
13
|
default_executable: spqr-gen.rb
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|