spqr 0.1.0 → 0.1.1
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 +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
|