spqr 0.0.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/.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
|