spqr 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +202 -0
- data/README.rdoc +28 -0
- data/Rakefile +105 -0
- data/TODO +33 -0
- data/VERSION +1 -0
- data/bin/spqr-gen.rb +60 -0
- data/examples/codegen-schema.xml +7 -0
- data/examples/codegen/EchoAgent.rb +33 -0
- data/examples/hello.rb +44 -0
- data/examples/logservice.rb +90 -0
- data/lib/rhubarb/rhubarb.rb +504 -0
- data/lib/spqr/app.rb +299 -0
- data/lib/spqr/codegen.rb +434 -0
- data/lib/spqr/constants.rb +64 -0
- data/lib/spqr/manageable.rb +222 -0
- data/lib/spqr/spqr.rb +16 -0
- data/lib/spqr/utils.rb +88 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/spqr_spec.rb +5 -0
- data/spqr.spec.in +95 -0
- data/test/helper.rb +11 -0
- data/test/test_rhubarb.rb +608 -0
- data/test/test_spqr.rb +7 -0
- metadata +97 -0
data/lib/spqr/app.rb
ADDED
@@ -0,0 +1,299 @@
|
|
1
|
+
# SPQR: Schema Processor for QMF/Ruby agents
|
2
|
+
#
|
3
|
+
# Application skeleton class
|
4
|
+
#
|
5
|
+
# Copyright (c) 2009 Red Hat, Inc.
|
6
|
+
#
|
7
|
+
# Author: William Benton (willb@redhat.com)
|
8
|
+
#
|
9
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
10
|
+
# you may not use this file except in compliance with the License.
|
11
|
+
# You may obtain a copy of the License at
|
12
|
+
#
|
13
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
14
|
+
|
15
|
+
require 'spqr/spqr'
|
16
|
+
require 'qmf'
|
17
|
+
require 'logger'
|
18
|
+
|
19
|
+
module SPQR
|
20
|
+
class App < Qmf::AgentHandler
|
21
|
+
class ClassMeta < Struct.new(:object_class, :schema_class) ; end
|
22
|
+
|
23
|
+
def initialize(options=nil)
|
24
|
+
defaults = {:logfile=>STDERR, :loglevel=>Logger::WARN}
|
25
|
+
|
26
|
+
# convenient shorthands for log levels
|
27
|
+
loglevels = {:debug => Logger::DEBUG, :info => Logger::INFO, :warn => Logger::WARN, :error => Logger::ERROR, :fatal => Logger::FATAL}
|
28
|
+
|
29
|
+
options = defaults unless options
|
30
|
+
|
31
|
+
# set unsupplied options to defaults
|
32
|
+
defaults.each do |k,v|
|
33
|
+
options[k] = v unless options[k]
|
34
|
+
end
|
35
|
+
|
36
|
+
# fix up shorthands
|
37
|
+
options[:loglevel] = loglevels[options[:loglevel]] if loglevels[options[:loglevel]]
|
38
|
+
|
39
|
+
@log = Logger.new(options[:logfile])
|
40
|
+
@log.level = options[:loglevel]
|
41
|
+
|
42
|
+
@log.info("initializing SPQR app....")
|
43
|
+
|
44
|
+
@classes_by_name = {}
|
45
|
+
@classes_by_id = {}
|
46
|
+
end
|
47
|
+
|
48
|
+
def register(*ks)
|
49
|
+
manageable_ks = ks.select {|kl| manageable? kl}
|
50
|
+
unmanageable_ks = ks.select {|kl| not manageable? kl}
|
51
|
+
manageable_ks.each do |klass|
|
52
|
+
@log.info("SPQR will manage registered class #{klass} (#{klass.name})...")
|
53
|
+
|
54
|
+
schemaclass = schematize(klass)
|
55
|
+
|
56
|
+
klass.spqr_logger = @log
|
57
|
+
|
58
|
+
@classes_by_id[klass.class_id] = klass
|
59
|
+
@classes_by_name[klass.name] = ClassMeta.new(klass, schemaclass)
|
60
|
+
end
|
61
|
+
|
62
|
+
unmanageable_ks.each do |klass|
|
63
|
+
@log.warn("SPQR can't manage #{klass}, which was registered")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
def method_call(context, name, obj_id, args, user_id)
|
69
|
+
begin
|
70
|
+
class_id = obj_id.object_num_high
|
71
|
+
obj_id = obj_id.object_num_low
|
72
|
+
|
73
|
+
@log.debug "calling method: context=#{context} method=#{name} object_id=#{obj_id}, args=#{args}, user=#{user_id}"
|
74
|
+
|
75
|
+
# Turn the Qmf::Arguments structure into a proper ruby hash
|
76
|
+
|
77
|
+
# XXX: consider adding appropriate impl method to Manageable
|
78
|
+
# to avoid this little dance
|
79
|
+
hash_args = qmf_arguments_to_hash(args)
|
80
|
+
|
81
|
+
managed_object = find_object(context, class_id, obj_id)
|
82
|
+
@log.debug("managed object is #{managed_object}")
|
83
|
+
|
84
|
+
@log.debug("managed_object.respond_to? #{name.to_sym} ==> #{managed_object.respond_to? name.to_sym}")
|
85
|
+
managed_object.send(name.to_sym, hash_args)
|
86
|
+
|
87
|
+
# Copy any out parameters from hash_args from the
|
88
|
+
# Qmf::Arguments structure; see XXX above
|
89
|
+
hash_args.each do |k,v|
|
90
|
+
encoded_val = encode_object(v)
|
91
|
+
args[k] = encoded_val
|
92
|
+
end
|
93
|
+
|
94
|
+
@agent.method_response(context, 0, "OK", args)
|
95
|
+
rescue Exception => ex
|
96
|
+
@log.error "Error calling #{name}: #{ex}"
|
97
|
+
@log.error " " + ex.backtrace.join("\n ")
|
98
|
+
@agent.method_response(context, 1, "ERROR: #{ex}", args)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def get_query(context, query, user_id)
|
103
|
+
@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}"
|
104
|
+
|
105
|
+
cmeta = @classes_by_name[query.class_name]
|
106
|
+
objs = []
|
107
|
+
|
108
|
+
# XXX: are these cases mutually exclusive?
|
109
|
+
|
110
|
+
# handle queries for a certain class
|
111
|
+
if cmeta
|
112
|
+
objs = objs + cmeta.object_class.find_all.collect {|obj| qmfify(obj)}
|
113
|
+
end
|
114
|
+
|
115
|
+
# handle queries for a specific object
|
116
|
+
o = find_object(context, query.object_id.object_num_high, query.object_id.object_num_low) rescue nil
|
117
|
+
if o
|
118
|
+
objs << qmfify(o)
|
119
|
+
end
|
120
|
+
|
121
|
+
objs.each do |obj|
|
122
|
+
@log.debug("query_response of: #{obj.inspect}")
|
123
|
+
@agent.query_response(context, obj) rescue @log.error($!.inspect)
|
124
|
+
end
|
125
|
+
|
126
|
+
@log.debug("completing query....")
|
127
|
+
@agent.query_complete(context)
|
128
|
+
end
|
129
|
+
|
130
|
+
def main
|
131
|
+
# XXX: fix and parameterize as necessary
|
132
|
+
@log.debug("starting SPQR::App.main...")
|
133
|
+
|
134
|
+
settings = Qmf::ConnectionSettings.new
|
135
|
+
settings.host = 'localhost'
|
136
|
+
|
137
|
+
@connection = Qmf::Connection.new(settings)
|
138
|
+
@log.debug(" +-- @connection created: #{@connection}")
|
139
|
+
|
140
|
+
@agent = Qmf::Agent.new(self)
|
141
|
+
@log.debug(" +-- @agent created: #{@agent}")
|
142
|
+
|
143
|
+
@agent.set_connection(@connection)
|
144
|
+
@log.debug(" +-- @agent.set_connection called")
|
145
|
+
|
146
|
+
@log.debug(" +-- registering classes...")
|
147
|
+
@classes_by_name.values.each do |km|
|
148
|
+
@agent.register_class(km.schema_class)
|
149
|
+
@log.debug(" +--+-- #{km.schema_class.package_name} #{km.schema_class.class_name} registered")
|
150
|
+
end
|
151
|
+
|
152
|
+
@log.debug("entering orbit....")
|
153
|
+
sleep
|
154
|
+
end
|
155
|
+
|
156
|
+
private
|
157
|
+
|
158
|
+
def qmf_arguments_to_hash(args)
|
159
|
+
result = {}
|
160
|
+
args.each do |k,v|
|
161
|
+
result[k] = v
|
162
|
+
end
|
163
|
+
result
|
164
|
+
end
|
165
|
+
|
166
|
+
def encode_object(o)
|
167
|
+
return o unless o.kind_of? ::SPQR::Manageable
|
168
|
+
@agent.alloc_object_id(*(o.qmf_id))
|
169
|
+
end
|
170
|
+
|
171
|
+
def find_object(ctx, c_id, obj_id)
|
172
|
+
# XXX: context is currently ignored
|
173
|
+
klass = @classes_by_id[c_id]
|
174
|
+
klass.find_by_id(obj_id) if klass
|
175
|
+
end
|
176
|
+
|
177
|
+
def schematize(klass)
|
178
|
+
@log.info("Making a QMF schema for #{klass}")
|
179
|
+
|
180
|
+
meta = klass.spqr_meta
|
181
|
+
package = meta.package.to_s
|
182
|
+
classname = meta.classname.to_s
|
183
|
+
@log.info("+-- class #{classname} is in package #{package}")
|
184
|
+
|
185
|
+
sc = Qmf::SchemaObjectClass.new(package, classname)
|
186
|
+
|
187
|
+
meta.mmethods.each do |mm|
|
188
|
+
@log.info("+-- creating a QMF schema for method #{mm}")
|
189
|
+
m_opts = mm.options
|
190
|
+
m_opts[:desc] ||= mm.description if mm.description
|
191
|
+
|
192
|
+
method = Qmf::SchemaMethod.new(mm.name.to_s, m_opts)
|
193
|
+
|
194
|
+
mm.args.each do |arg|
|
195
|
+
@log.info("| +-- creating a QMF schema for arg #{arg}")
|
196
|
+
|
197
|
+
arg_opts = arg.options
|
198
|
+
arg_opts[:desc] ||= arg.description if (arg.description and arg.description.is_a? String)
|
199
|
+
arg_opts[:dir] ||= get_xml_constant(arg.direction.to_s, ::SPQR::XmlConstants::Direction)
|
200
|
+
arg_name = arg.name.to_s
|
201
|
+
arg_type = get_xml_constant(arg.kind.to_s, ::SPQR::XmlConstants::Type)
|
202
|
+
|
203
|
+
if @log.level <= Logger::DEBUG
|
204
|
+
local_variables.grep(/^arg_/).each do |local|
|
205
|
+
@log.debug(" #{local} --> #{(eval local).inspect}")
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
method.add_argument(Qmf::SchemaArgument.new(arg_name, arg_type, arg_opts))
|
210
|
+
end
|
211
|
+
|
212
|
+
sc.add_method(method)
|
213
|
+
end
|
214
|
+
|
215
|
+
add_attributes(sc, meta.properties, :add_property, Qmf::SchemaProperty)
|
216
|
+
add_attributes(sc, meta.statistics, :add_statistic, Qmf::SchemaStatistic)
|
217
|
+
|
218
|
+
sc
|
219
|
+
end
|
220
|
+
|
221
|
+
def add_attributes(sc, collection, msg, klass, what=nil)
|
222
|
+
what ||= (msg.to_s.split("_").pop rescue "property or statistic")
|
223
|
+
collection.each do |basic|
|
224
|
+
basic_name = basic.name.to_s
|
225
|
+
basic_type = get_xml_constant(basic.kind.to_s, ::SPQR::XmlConstants::Type)
|
226
|
+
@log.debug("+-- creating a QMF schema for #{what} #{basic_name} (#{basic_type}) with options #{basic.options.inspect}")
|
227
|
+
sc.send(msg, klass.new(basic_name, basic_type, basic.options))
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def manageable?(k)
|
232
|
+
# FIXME: move out of App, into Manageable or a related utils module?
|
233
|
+
k.is_a? Class and k.included_modules.include? ::SPQR::Manageable
|
234
|
+
end
|
235
|
+
|
236
|
+
def get_xml_constant(xml_key, dictionary)
|
237
|
+
# FIXME: move out of App, into a utils module?
|
238
|
+
string_val = dictionary[xml_key]
|
239
|
+
return xml_key unless string_val
|
240
|
+
|
241
|
+
actual_val = const_lookup(string_val)
|
242
|
+
return string_val unless actual_val
|
243
|
+
|
244
|
+
return actual_val
|
245
|
+
end
|
246
|
+
|
247
|
+
# turns a string name of a constant into the value of that
|
248
|
+
# constant; returns that value, or nil if fqcn doesn't correspond
|
249
|
+
# to a valid constant
|
250
|
+
def const_lookup(fqcn)
|
251
|
+
# FIXME: move out of App, into a utils module?
|
252
|
+
hierarchy = fqcn.split("::")
|
253
|
+
const = hierarchy.pop
|
254
|
+
mod = Kernel
|
255
|
+
hierarchy.each do |m|
|
256
|
+
mod = mod.const_get(m)
|
257
|
+
end
|
258
|
+
mod.const_get(const) rescue nil
|
259
|
+
end
|
260
|
+
|
261
|
+
# turns an instance of a managed object into a QmfObject
|
262
|
+
def qmfify(obj)
|
263
|
+
@log.debug("trying to qmfify #{obj}: qmf_oid is #{obj.qmf_oid} and class_id is #{obj.class.class_id}")
|
264
|
+
cm = @classes_by_name[obj.class.name]
|
265
|
+
return nil unless cm
|
266
|
+
|
267
|
+
qmfobj = Qmf::AgentObject.new(cm.schema_class)
|
268
|
+
|
269
|
+
set_attrs(qmfobj, obj)
|
270
|
+
|
271
|
+
@log.debug("calling alloc_object_id(#{obj.qmf_oid}, #{obj.class.class_id})")
|
272
|
+
oid = @agent.alloc_object_id(obj.qmf_oid, obj.class.class_id)
|
273
|
+
|
274
|
+
@log.debug("calling qmfobj.set_object_id(#{oid})")
|
275
|
+
qmfobj.set_object_id(oid)
|
276
|
+
|
277
|
+
@log.debug("returning from qmfify")
|
278
|
+
qmfobj
|
279
|
+
end
|
280
|
+
|
281
|
+
def set_attrs(qo, o)
|
282
|
+
return unless o.class.respond_to? :spqr_meta
|
283
|
+
|
284
|
+
attrs = o.class.spqr_meta.properties + o.class.spqr_meta.statistics
|
285
|
+
|
286
|
+
attrs.each do |a|
|
287
|
+
getter = a.name.to_s
|
288
|
+
@log.debug("setting property/statistic #{getter} to its value from #{o}: #{o.send(getter) if o.respond_to?(getter)}")
|
289
|
+
value = o.send(getter) if o.respond_to?(getter)
|
290
|
+
if value
|
291
|
+
# XXX: remove this line when/if Manageable includes an
|
292
|
+
# appropriate impl method
|
293
|
+
value = encode_object(value) if value.kind_of?(::SPQR::Manageable)
|
294
|
+
qo[getter] = value
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
data/lib/spqr/codegen.rb
ADDED
@@ -0,0 +1,434 @@
|
|
1
|
+
# Code generation for SPQR
|
2
|
+
#
|
3
|
+
# Copyright (c) 2009 Red Hat, Inc.
|
4
|
+
#
|
5
|
+
# Author: William Benton (willb@redhat.com)
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
|
13
|
+
require 'spqr/spqr'
|
14
|
+
|
15
|
+
require 'rexml/document'
|
16
|
+
require 'fileutils'
|
17
|
+
require 'optparse'
|
18
|
+
|
19
|
+
module SPQR
|
20
|
+
|
21
|
+
class SchemaClass
|
22
|
+
attr_accessor :name, :package
|
23
|
+
|
24
|
+
def self.declare(name, package)
|
25
|
+
kl = SchemaClass.new(name, package)
|
26
|
+
yield kl if block_given?
|
27
|
+
kl
|
28
|
+
end
|
29
|
+
|
30
|
+
def declare_property(name, kind, options)
|
31
|
+
declare_basic(:property, name, kind, options)
|
32
|
+
end
|
33
|
+
|
34
|
+
def declare_statistic(name, kind, options)
|
35
|
+
declare_basic(:statistic, name, kind, options)
|
36
|
+
end
|
37
|
+
|
38
|
+
def declare_method(name, desc, options)
|
39
|
+
result = SchemaMethod.new name, desc, options.dup
|
40
|
+
|
41
|
+
yield result.args if block_given?
|
42
|
+
@methods << result
|
43
|
+
@methods[-1]
|
44
|
+
end
|
45
|
+
|
46
|
+
def initialize(nm=nil, pkg=nil)
|
47
|
+
@name, @package = nm, pkg
|
48
|
+
@properties = []
|
49
|
+
@statistics = []
|
50
|
+
@methods = []
|
51
|
+
end
|
52
|
+
|
53
|
+
def with_each(what)
|
54
|
+
source = self.instance_variable_get("@#{what}".to_sym) || []
|
55
|
+
source.each do |x|
|
56
|
+
yield x if block_given?
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def member_count(what)
|
61
|
+
source = self.instance_variable_get("@#{what}".to_sym) || []
|
62
|
+
source.size
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
def declare_basic(what, name, kind, options)
|
67
|
+
@wherelocs ||= {:property=>@properties,:statistic=>@statistics}
|
68
|
+
where = @wherelocs[what]
|
69
|
+
|
70
|
+
opts = options.dup
|
71
|
+
opts[:index] = true if opts[:index]
|
72
|
+
desc = opts.delete(:desc)
|
73
|
+
|
74
|
+
where << SchemaBasic.new(what, name, desc, kind, opts)
|
75
|
+
where[-1]
|
76
|
+
end
|
77
|
+
|
78
|
+
class SchemaBasic
|
79
|
+
attr_accessor :name, :kind, :desc, :options
|
80
|
+
def initialize(what,nm,desc,knd,opts)
|
81
|
+
@what = what
|
82
|
+
@name = nm
|
83
|
+
@kind = knd
|
84
|
+
@options = opts
|
85
|
+
@desc = desc.gsub(/\s+/, " ") if desc
|
86
|
+
end
|
87
|
+
|
88
|
+
def property?
|
89
|
+
@what == :property
|
90
|
+
end
|
91
|
+
|
92
|
+
def statistic?
|
93
|
+
@what == :statistic
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class SchemaMethod
|
98
|
+
attr_accessor :name, :desc, :options, :args
|
99
|
+
def initialize(nm,desc,opts=nil)
|
100
|
+
@options = (opts && opts.dup) || {}
|
101
|
+
@name = nm
|
102
|
+
@desc = desc.gsub(/\s+/, " ") if desc
|
103
|
+
@args = arg_struct
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
def arg_struct
|
108
|
+
@@prototype_args ||= gen_prototype_args
|
109
|
+
@@prototype_args.clone
|
110
|
+
end
|
111
|
+
|
112
|
+
def gen_prototype_args
|
113
|
+
prototype_args = []
|
114
|
+
|
115
|
+
def prototype_args.declare(name, kind, dir, desc, options)
|
116
|
+
opts = options.dup
|
117
|
+
|
118
|
+
self << ::SPQR::SchemaClass::SchemaArg.new(name, kind, dir, desc, opts)
|
119
|
+
end
|
120
|
+
|
121
|
+
prototype_args
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
class SchemaArg
|
126
|
+
attr_accessor :name, :desc, :kind, :dir, :options
|
127
|
+
def initialize(nm, knd, dr, dsc, opts)
|
128
|
+
@name = nm
|
129
|
+
@desc = dsc.gsub(/\s+/, " ") if dsc
|
130
|
+
@kind = knd
|
131
|
+
@dir = dr
|
132
|
+
@options = opts
|
133
|
+
end
|
134
|
+
|
135
|
+
def inspect
|
136
|
+
[:name, :desc, :kind, :dir, :options].map { |sel|"[#{sel}:#{self.send(sel)}]" }.join(" ")
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
class ModelClassGenerator
|
143
|
+
include ::SPQR::PrettyPrinter
|
144
|
+
|
145
|
+
def ModelClassGenerator.id_registry
|
146
|
+
@id_registry ||= {}
|
147
|
+
end
|
148
|
+
|
149
|
+
def ModelClassGenerator.class_registry
|
150
|
+
@class_registry ||= {}
|
151
|
+
end
|
152
|
+
|
153
|
+
def initialize(sc)
|
154
|
+
@sc = sc
|
155
|
+
end
|
156
|
+
|
157
|
+
def gen
|
158
|
+
@package_list = @sc.package.split(".")
|
159
|
+
package_dir = "./#{@package_list.join('/')}"
|
160
|
+
FileUtils.mkdir_p package_dir
|
161
|
+
|
162
|
+
filename = "#{$OUTDIR}/#{package_dir}/#{@sc.name}.rb"
|
163
|
+
with_output_to filename do
|
164
|
+
gen_class
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
private
|
169
|
+
def gen_class
|
170
|
+
pp "require 'spqr/spqr'"
|
171
|
+
pp ""
|
172
|
+
|
173
|
+
@package_list.map {|pkg| pkg.capitalize}.each do |modname|
|
174
|
+
pp "module #{modname}"
|
175
|
+
inc_indent
|
176
|
+
end
|
177
|
+
|
178
|
+
pp_decl :class, @sc.name do
|
179
|
+
pkgname = (@package_list.map {|pkg| pkg.capitalize}).join("::")
|
180
|
+
fqcn = ("#{pkgname}::#{@sc.name}" if pkgname) or @sc.name
|
181
|
+
|
182
|
+
pp "include ::SPQR::Manageable"
|
183
|
+
pp ""
|
184
|
+
|
185
|
+
pp "spqr_package '#{@package_list.join(".")}'"
|
186
|
+
pp "spqr_class '#{@sc.name.split("::")[-1]}'"
|
187
|
+
|
188
|
+
pp '# Find method (NB: you must implement this)'
|
189
|
+
pp_decl :def, "#{@sc.name}.find_by_id", "(objid)" do
|
190
|
+
pp "#{@sc.name}.new"
|
191
|
+
end
|
192
|
+
|
193
|
+
pp "\n# Find-all method (NB: you must implement this)"
|
194
|
+
pp_decl :def, "#{@sc.name}.find_all" do
|
195
|
+
pp "[#{@sc.name}.new]"
|
196
|
+
end
|
197
|
+
|
198
|
+
ModelClassGenerator.id_registry[fqcn.hash] = fqcn
|
199
|
+
ModelClassGenerator.class_registry[fqcn] = fqcn.hash
|
200
|
+
|
201
|
+
pp "\#\#\# Property method declarations" if @sc.member_count(:properties) > 0
|
202
|
+
@sc.with_each :properties do |property|
|
203
|
+
gen_property property
|
204
|
+
end
|
205
|
+
|
206
|
+
pp "\#\#\# Statistic method declarations" if @sc.member_count(:statistics) > 0
|
207
|
+
@sc.with_each :statistics do |statistic|
|
208
|
+
gen_statistic statistic
|
209
|
+
end
|
210
|
+
|
211
|
+
pp "\#\#\# Schema method declarations"
|
212
|
+
@sc.with_each :methods do |method|
|
213
|
+
gen_method method
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
@package_list.size.times do
|
218
|
+
dec_indent
|
219
|
+
pp "end"
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def gen_property(property)
|
224
|
+
pp ""
|
225
|
+
pp "\# property #{property.name} #{property.kind} #{property.desc}"
|
226
|
+
pp_decl :def, "#{property.name}" do
|
227
|
+
pp "log.debug 'Requested property #{property.name}'"
|
228
|
+
pp "nil"
|
229
|
+
end
|
230
|
+
|
231
|
+
pp ""
|
232
|
+
pp_decl :def, "#{property.name}=", "(val)" do
|
233
|
+
pp "log.debug 'Set property #{property.name} to \#\{val\}'"
|
234
|
+
pp "nil"
|
235
|
+
end
|
236
|
+
|
237
|
+
property.options[:desc] = property.desc if property.desc
|
238
|
+
|
239
|
+
pp ""
|
240
|
+
pp "spqr_property #{property.name.to_sym.inspect}, #{property.kind.to_sym.inspect}, #{property.options.inspect.gsub(/[{}]/, '')}"
|
241
|
+
end
|
242
|
+
|
243
|
+
def gen_statistic(statistic)
|
244
|
+
pp ""
|
245
|
+
pp "\# statistic #{statistic.name}"
|
246
|
+
pp_decl :def, "#{statistic.name}" do
|
247
|
+
pp "log.debug 'Requested statistic #{statistic.name}'"
|
248
|
+
pp "nil"
|
249
|
+
end
|
250
|
+
|
251
|
+
statistic.options[:desc] = statistic.desc if statistic.desc
|
252
|
+
|
253
|
+
pp ""
|
254
|
+
pp "spqr_property #{statistic.name.to_sym.inspect}, #{statistic.kind.to_sym.inspect}, #{statistic.options.inspect.gsub(/[{}]/, '')}"
|
255
|
+
end
|
256
|
+
|
257
|
+
def gen_method(method)
|
258
|
+
pp ""
|
259
|
+
pp "\# #{method.name} #{method.desc}"
|
260
|
+
method.args.each do |arg|
|
261
|
+
pp "\# * #{arg.name} (#{arg.kind}/#{arg.dir})"
|
262
|
+
pp "\# #{arg.desc}"
|
263
|
+
end
|
264
|
+
|
265
|
+
in_params = method.args.select {|arg| ['in', 'i', 'qmf::dir_in'].include? arg.dir.to_s.downcase }
|
266
|
+
out_params = method.args.select {|arg| ['out', 'o', 'qmf::dir_out'].include? arg.dir.to_s.downcase }
|
267
|
+
inout_params = method.args.select {|arg| ['inout', 'io', 'qmf::dir_inout'].include? arg.dir.to_s.downcase }
|
268
|
+
|
269
|
+
pp_decl :def, method.name, "(args)" do
|
270
|
+
|
271
|
+
if in_params.size + inout_params.size > 0
|
272
|
+
what = "in"
|
273
|
+
|
274
|
+
if in_params.size > 0 and inout_params.size > 0
|
275
|
+
what << " and in/out"
|
276
|
+
elsif inout_params.size > 0
|
277
|
+
what << "/out"
|
278
|
+
end
|
279
|
+
|
280
|
+
pp "\# Print values of #{what} parameters"
|
281
|
+
(in_params + inout_params).each do |arg|
|
282
|
+
argdisplay = arg.name.to_s.inspect
|
283
|
+
pp('log.debug "' + "#{arg.name} => " + '#{args[' + "#{argdisplay}" + ']}"' + " \# #{}")
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
if out_params.size + inout_params.size > 0
|
288
|
+
what = "out"
|
289
|
+
|
290
|
+
if out_params.size > 0 and inout_params.size > 0
|
291
|
+
what << " and in/out"
|
292
|
+
elsif inout_params.size > 0
|
293
|
+
what = "in/out"
|
294
|
+
end
|
295
|
+
|
296
|
+
pp "\# Assign values to #{what} parameters"
|
297
|
+
|
298
|
+
(out_params + inout_params).each do |arg|
|
299
|
+
argdisplay = arg.name.to_s.inspect
|
300
|
+
pp "args[#{argdisplay}] = args[#{argdisplay}]"
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
|
306
|
+
pp ""
|
307
|
+
pp_decl :spqr_expose, "#{method.name.to_sym.inspect} do |args|" do
|
308
|
+
{:in => in_params, :inout => inout_params, :out => out_params}.each do |dir,coll|
|
309
|
+
coll.each do |arg|
|
310
|
+
arg_nm = arg.name
|
311
|
+
arg_kd = arg.kind
|
312
|
+
arg_opts = arg.options.inspect.gsub(/^[{](.+)[}]$/, '\1')
|
313
|
+
pp "args.declare :#{arg_nm}, :#{arg_kd}, :#{dir}, #{arg_opts}"
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
class AppBoilerplateGenerator
|
321
|
+
include PrettyPrinter
|
322
|
+
|
323
|
+
# scs is a list of schemaclass objects, fn is an output filename
|
324
|
+
def initialize(scs, fn)
|
325
|
+
@scs = scs
|
326
|
+
@fn = fn
|
327
|
+
end
|
328
|
+
|
329
|
+
# cc is the name of the variable that will hold a collection of schema classes
|
330
|
+
def gen
|
331
|
+
with_output_to @fn do
|
332
|
+
pp "require 'rubygems'"
|
333
|
+
pp "require 'spqr/spqr'"
|
334
|
+
pp "require 'spqr/app'"
|
335
|
+
|
336
|
+
pp ""
|
337
|
+
|
338
|
+
@scs.each do |sc|
|
339
|
+
pp("require '#{sc.package.gsub(/[.]/, '/')}/#{sc.name}'")
|
340
|
+
end
|
341
|
+
|
342
|
+
|
343
|
+
pp ""
|
344
|
+
|
345
|
+
pp "app = SPQR::App.new(:loglevel => :debug)"
|
346
|
+
|
347
|
+
klass_list = @scs.collect do |sc|
|
348
|
+
(sc.package.split(".").collect{|pkg| pkg.capitalize} << sc.name).join("::")
|
349
|
+
end
|
350
|
+
|
351
|
+
pp "app.register #{klass_list.join ','}"
|
352
|
+
|
353
|
+
pp ""
|
354
|
+
|
355
|
+
pp "app.main"
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
class QmfSchemaProcessor
|
361
|
+
include ::SPQR::MiscUtil
|
362
|
+
def initialize(fn)
|
363
|
+
@package = nil
|
364
|
+
@file = fn
|
365
|
+
@doc = nil
|
366
|
+
@indent = 0
|
367
|
+
@schema_classes = []
|
368
|
+
end
|
369
|
+
|
370
|
+
def main
|
371
|
+
File::open(@file, "r") {|infile| @doc = REXML::Document.new(infile)}
|
372
|
+
|
373
|
+
process_schema
|
374
|
+
@schema_classes.each do |klass|
|
375
|
+
ModelClassGenerator.new(klass).gen
|
376
|
+
end
|
377
|
+
|
378
|
+
AppBoilerplateGenerator.new(@schema_classes, "#{$OUTDIR}/agent-app.rb").gen
|
379
|
+
end
|
380
|
+
|
381
|
+
private
|
382
|
+
|
383
|
+
def process_schema
|
384
|
+
@package = @doc.root.attributes["package"]
|
385
|
+
@package_list = @package.split(".")
|
386
|
+
|
387
|
+
@package_dir = "#{$OUTDIR}/#{@package_list.join('/')}"
|
388
|
+
|
389
|
+
FileUtils.mkdir_p @package_dir
|
390
|
+
|
391
|
+
REXML::XPath.each(@doc.root, "/schema/class") do |elt|
|
392
|
+
@schema_classes << process_class(elt)
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
def process_class(elt)
|
397
|
+
classname = "#{elt.attributes['name']}"
|
398
|
+
|
399
|
+
SchemaClass.declare classname, @package do |klass|
|
400
|
+
REXML::XPath.each(elt, "property") do |property|
|
401
|
+
name = property.attributes['name']
|
402
|
+
kind = property.attributes['type']
|
403
|
+
opts = symbolize_dict(property.attributes, [:desc, :index, :access, :optional, :min, :max, :maxlen, :unit])
|
404
|
+
opts[:index] = (opts[:index].to_s == '1' or opts[:index].to_s.downcase == 'y')
|
405
|
+
klass.declare_property name, kind, opts
|
406
|
+
end
|
407
|
+
|
408
|
+
REXML::XPath.each(elt, "statistic") do |statistic|
|
409
|
+
name = statistic.attributes['name']
|
410
|
+
kind = statistic.attributes['type']
|
411
|
+
opts = symbolize_dict(statistic.attributes, [:desc, :unit])
|
412
|
+
klass.declare_statistic name, kind, {}
|
413
|
+
end
|
414
|
+
|
415
|
+
REXML::XPath.each(elt, "method") do |method|
|
416
|
+
name = method.attributes['name']
|
417
|
+
desc = method.attributes['desc']
|
418
|
+
klass.declare_method name, desc, {} do |args|
|
419
|
+
REXML::XPath.each(method, "arg") do |arg|
|
420
|
+
opts = symbolize_dict(arg.attributes, [:name, :type, :refPackage, :refClass, :dir, :unit, :min, :max, :maxlen, :desc, :default, :references])
|
421
|
+
name = opts.delete(:name)
|
422
|
+
kind = opts.delete(:type)
|
423
|
+
dir = opts.delete(:dir)
|
424
|
+
desc = opts.delete(:desc)
|
425
|
+
args.declare(name, kind, dir, desc, opts)
|
426
|
+
end
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
klass
|
431
|
+
end
|
432
|
+
end
|
433
|
+
end
|
434
|
+
end
|