spqr 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,3 +1,7 @@
1
+ version 0.3.1
2
+
3
+ * "qmf_singleton" declaration for boilerplate-free singleton classes
4
+
1
5
  version 0.3.0 (04c1eb70855b6382925c65f350fbb69e9eb58c88)
2
6
 
3
7
  * first-class QMF event support via the SPQR::Raiseable mixin.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.0
1
+ 0.3.1
@@ -0,0 +1,33 @@
1
+ module Examples
2
+ module Codegen
3
+ class EchoAgent
4
+ include SPQR::Manageable
5
+
6
+ spqr_package 'examples.codegen'
7
+ spqr_class 'EchoAgent'
8
+ # Find method (NB: you must implement this)
9
+ def EchoAgent.find_by_id(objid)
10
+ EchoAgent.new
11
+ end
12
+ # Find-all method (NB: you must implement this)
13
+ def EchoAgent.find_all
14
+ [EchoAgent.new]
15
+ end
16
+ ### Schema method declarations
17
+
18
+ # echo returns its argument
19
+ # * arg (lstr/IO)
20
+ #
21
+ def echo(args)
22
+ # Print values of in/out parameters
23
+ log.debug "arg => #{args["arg"]}" #
24
+ # Assign values to in/out parameters
25
+ args["arg"] = args["arg"]
26
+ end
27
+
28
+ spqr_expose :echo do |args|
29
+ args.declare :arg, :lstr, :inout, {}
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # This is a simple logging service that operates over QMF. It is very similar
4
+ # to logservice.rb, except it makes QMF events for log events instead of
5
+ # database records. LogEventService has the same API as LogService; LogEvent
6
+ # is a SPQR event class. See the comments for details on how to use QMF events.
7
+
8
+ require 'spqr/spqr'
9
+ require 'spqr/app'
10
+
11
+ class LogEventService
12
+ include SPQR::Manageable
13
+
14
+ [:debug, :warn, :info, :error].each do |name|
15
+ define_method name do |msg|
16
+ # Construct a new event by passing arguments to .new in the order that they were declared
17
+ ev = LogEvent.new(Time.now.utc.to_i * 1000000000, "#{name.to_s.upcase}", msg.dup)
18
+
19
+ # You can also set arguments of an event object individually, like this:
20
+ # ev = LogEvent.new
21
+ # ev.l_when = Time.now
22
+ # ev.severity = name.to_s.upcase
23
+ # ev.msg = msg.dup
24
+
25
+ # Once all of the arguments are set, raise the event:
26
+ ev.bang!
27
+ end
28
+
29
+ expose name do |args|
30
+ args.declare :msg, :lstr, :in
31
+ end
32
+ end
33
+
34
+ def self.find_all
35
+ @singleton ||= LogEventService.new
36
+ [@singleton]
37
+ end
38
+
39
+ def self.find_by_id(i)
40
+ @singleton ||= LogEventService.new
41
+ end
42
+
43
+ qmf_package_name :examples
44
+ qmf_class_name :LogEventService
45
+ end
46
+
47
+ class LogEvent
48
+ # To declare an event class, include SPQR::Raiseable
49
+ include ::SPQR::Raiseable
50
+
51
+ # Declare arguments with their name, type, and (optional) description
52
+ arg :l_when, :absTime, "When the event happened"
53
+ arg :severity, :sstr, "Event severity: DEBUG, WARN, INFO, or ERROR"
54
+ arg :msg, :lstr, "Log message"
55
+
56
+ # Declare metadata as appropriate
57
+ qmf_package_name :examples
58
+ qmf_class_name :LogEvent
59
+ end
60
+
61
+ app = SPQR::App.new(:loglevel => :debug)
62
+
63
+ # Event classes must be registered just like object classes
64
+ app.register LogEventService, LogEvent
65
+
66
+ app.main
@@ -157,8 +157,22 @@ module SPQR
157
157
  # Exposes a method to QMF
158
158
  def expose(name, description=nil, options=nil, &blk)
159
159
  spqr_meta.declare_method(name, description, options, blk)
160
- end
161
-
160
+ end
161
+
162
+ def qmf_singleton
163
+ def self.instances
164
+ @instances ||= [self.new]
165
+ end
166
+
167
+ def self.find_all
168
+ instances
169
+ end
170
+
171
+ def self.find_by_id(id)
172
+ instances[0]
173
+ end
174
+ end
175
+
162
176
  def qmf_package_name(nm)
163
177
  spqr_meta.package = nm
164
178
  end
@@ -1,15 +1,17 @@
1
1
  class QmfUserAndContext
2
2
  include ::SPQR::Manageable
3
3
 
4
- def QmfUserAndContext.find_by_id(oid)
5
- @singleton ||= QmfUserAndContext.new
6
- @singleton
7
- end
4
+ qmf_singleton
5
+
6
+ # def QmfUserAndContext.find_by_id(oid)
7
+ # @singleton ||= QmfUserAndContext.new
8
+ # @singleton
9
+ # end
8
10
 
9
- def QmfUserAndContext.find_all
10
- @singleton ||= QmfUserAndContext.new
11
- [@singleton]
12
- end
11
+ # def QmfUserAndContext.find_all
12
+ # @singleton ||= QmfUserAndContext.new
13
+ # [@singleton]
14
+ # end
13
15
 
14
16
  expose :qmf_user_id do |args|
15
17
  args.declare :uid, :sstr, :out
metadata CHANGED
@@ -1,7 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spqr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 3
8
+ - 1
9
+ version: 0.3.1
5
10
  platform: ruby
6
11
  authors:
7
12
  - William Benton
@@ -9,19 +14,23 @@ autorequire:
9
14
  bindir: bin
10
15
  cert_chain: []
11
16
 
12
- date: 2010-05-10 00:00:00 -05:00
17
+ date: 2010-05-29 00:00:00 -05:00
13
18
  default_executable: spqr-gen.rb
14
19
  dependencies:
15
20
  - !ruby/object:Gem::Dependency
16
21
  name: rspec
17
- type: :development
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
20
24
  requirements:
21
25
  - - ">="
22
26
  - !ruby/object:Gem::Version
27
+ segments:
28
+ - 1
29
+ - 2
30
+ - 9
23
31
  version: 1.2.9
24
- version:
32
+ type: :development
33
+ version_requirements: *id001
25
34
  description: SPQR makes it very simple to expose methods on Ruby objects over QMF. You must install ruby-qmf in order to use SPQR.
26
35
  email: willb@redhat.com
27
36
  executables:
@@ -44,6 +53,7 @@ files:
44
53
  - bin/spqr-gen.rb
45
54
  - examples/codegen-schema.xml
46
55
  - examples/hello.rb
56
+ - examples/logdaemon.rb
47
57
  - examples/logservice.rb
48
58
  - lib/spqr/app.rb
49
59
  - lib/spqr/codegen.rb
@@ -82,18 +92,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
82
92
  requirements:
83
93
  - - ">="
84
94
  - !ruby/object:Gem::Version
95
+ segments:
96
+ - 0
85
97
  version: "0"
86
- version:
87
98
  required_rubygems_version: !ruby/object:Gem::Requirement
88
99
  requirements:
89
100
  - - ">="
90
101
  - !ruby/object:Gem::Version
102
+ segments:
103
+ - 0
91
104
  version: "0"
92
- version:
93
105
  requirements: []
94
106
 
95
107
  rubyforge_project:
96
- rubygems_version: 1.3.5
108
+ rubygems_version: 1.3.6
97
109
  signing_key:
98
110
  specification_version: 3
99
111
  summary: "SPQR: {Schema Processor|Straightforward Publishing} for QMF agents in Ruby"
@@ -108,10 +120,11 @@ test_files:
108
120
  - test/helper.rb
109
121
  - test/test_failbot.rb
110
122
  - test/test_spqr_boolprop.rb
111
- - test/standalone-test.rb
112
123
  - test/test_spqr_clicker.rb
113
124
  - test/test_spqr_integerprop.rb
114
125
  - test/test_user_and_context.rb
115
126
  - test/test_spqr_hello.rb
127
+ - examples/logdaemon.rb
116
128
  - examples/hello.rb
129
+ - examples/codegen/EchoAgent.rb
117
130
  - examples/logservice.rb
@@ -1,330 +0,0 @@
1
- require 'qmf'
2
-
3
- class App < Qmf::AgentHandler
4
- class ClassMeta < Struct.new(:object_class, :schema_class) ; end
5
-
6
- attr_reader :agent
7
-
8
- def initialize(options=nil)
9
- defaults = {:logfile=>STDERR, :loglevel=>Logger::WARN, :notifier=>nil, :server=>"localhost", :port=>5672}
10
-
11
- # convenient shorthands for log levels
12
- loglevels = {:debug => Logger::DEBUG, :info => Logger::INFO, :warn => Logger::WARN, :error => Logger::ERROR, :fatal => Logger::FATAL}
13
-
14
- options = defaults unless options
15
-
16
- # set unsupplied options to defaults
17
- defaults.each do |k,v|
18
- options[k] = v unless options[k]
19
- end
20
-
21
- # fix up shorthands
22
- options[:loglevel] = loglevels[options[:loglevel]] if loglevels[options[:loglevel]]
23
-
24
- logger_opts = ([options[:logfile]] + [options[:logoptions]]).flatten.compact
25
-
26
- @log = Logger.new(*logger_opts)
27
- @log.level = options[:loglevel]
28
-
29
- @log.info("initializing SPQR app....")
30
-
31
- @classes_by_name = {}
32
- @classes_by_id = {}
33
- @pipe = options[:notifier]
34
- @app_name = (options[:appname] or "SPQR application [#{Process.pid}]")
35
- @qmf_host = options[:server]
36
- @qmf_port = options[:port]
37
- @qmf_sendUserId = if options.has_key?(:send_user_id)
38
- options[:send_user_id]
39
- else
40
- (options.has_key?(:user) || options.has_key?(:password))
41
- end
42
-
43
- @qmf_user = options[:user]
44
- @qmf_password = options[:password]
45
- end
46
-
47
- def register(*ks)
48
- manageable_ks = ks.select {|kl| manageable? kl}
49
- unmanageable_ks = ks.select {|kl| not manageable? kl}
50
- manageable_ks.each do |klass|
51
- schemaclass = schematize(klass)
52
-
53
- klass.log = @log
54
-
55
- # XXX
56
- if klass.included_modules.include?(::SPQR::Manageable)
57
- @classes_by_id[klass.class_id] = klass
58
- @classes_by_name[klass.spqr_meta.classname.to_s] = ClassMeta.new(klass, schemaclass)
59
- else
60
- @log.info "NOT registering query/lookup info for #{klass}; is it an event class?"
61
- end
62
-
63
- @log.info("SETTING #{klass.spqr_meta.classname}.app to #{self.inspect}")
64
- klass.app = self
65
- end
66
-
67
- unmanageable_ks.each do |klass|
68
- @log.warn("SPQR can't manage #{klass}, which was registered")
69
- end
70
- end
71
-
72
-
73
- def method_call(context, name, obj_id, args, user_id)
74
- begin
75
- status = 0
76
- message = "OK"
77
- failed = false
78
-
79
- class_id = obj_id.object_num_high
80
- obj_id = obj_id.object_num_low
81
-
82
- Thread.current[:qmf_user_id] = user_id
83
- Thread.current[:qmf_context] = context
84
-
85
- @log.debug "calling method: context=#{context} method=#{name} object_id=#{obj_id}, user=#{user_id}"
86
-
87
- managed_object = find_object(context, class_id, obj_id)
88
- @log.debug("managed object is #{managed_object}")
89
- managed_method = managed_object.class.spqr_meta.mmethods[name.to_sym]
90
-
91
- raise RuntimeError.new("#{managed_object.class} does not have #{name} exposed as a manageable method; has #{managed_object.class.spqr_meta.mmethods.inspect}") unless managed_method
92
-
93
- # Extract actual parameters from the Qmf::Arguments structure into a proper ruby list
94
- @log.debug("actual params are: #{args.instance_variable_get(:@by_hash).inspect}") rescue nil
95
- actuals_in = managed_method.formals_in.inject([]) {|acc,nm| acc << args[nm]}
96
- actual_count = actuals_in.size
97
-
98
- @log.debug("managed_object.respond_to? #{managed_method.name.to_sym} ==> #{managed_object.respond_to? managed_method.name.to_sym}")
99
- @log.debug("managed_object.class.spqr_meta.mmethods.include? #{name.to_sym} ==> #{managed_object.class.spqr_meta.mmethods.include? name.to_sym}")
100
- @log.debug("formals: #{managed_method.formals_in.inspect}")
101
- @log.debug("actuals: #{actuals_in.inspect}")
102
-
103
- actuals_out = []
104
-
105
- begin
106
- actuals_out = case actual_count
107
- when 0 then managed_object.send(name.to_sym)
108
- when 1 then managed_object.send(name.to_sym, actuals_in[0])
109
- else managed_object.send(name.to_sym, *actuals_in)
110
- end
111
-
112
- raise RuntimeError.new("#{managed_object.class} did not return the appropriate number of return values; got '#{actuals_out.inspect}', but expected #{managed_method.types_out.inspect}") unless result_valid(actuals_out, managed_method)
113
-
114
- rescue ::SPQR::ManageableObjectError => failure
115
- @log.info "#{name} called SPQR::Manageable#fail: #{failure}"
116
- status = failure.status
117
- message = failure.message || "ERROR"
118
- # XXX: failure.result is currently ignored
119
- actuals_out = failure.result || managed_method.formals_out.inject([]) {|acc, val| acc << args[val]; acc}
120
- failed = true
121
- end
122
-
123
- if managed_method.formals_out.size == 0
124
- actuals_out = [] # ignore return value in this case
125
- elsif managed_method.formals_out.size == 1
126
- actuals_out = [actuals_out] # wrap this up in a list
127
- end
128
-
129
- @log.debug("formals_out == #{managed_method.formals_out.inspect}")
130
- @log.debug("actuals_out == #{actuals_out.inspect}")
131
-
132
- unless failed
133
- # Copy any out parameters from return value to the
134
- # Qmf::Arguments structure; see XXX above
135
- managed_method.formals_out.zip(actuals_out).each do |k,v|
136
- @log.debug("fixing up out params: #{k.inspect} --> #{v.inspect}")
137
- encoded_val = encode_object(v)
138
- args[k] = encoded_val
139
- end
140
- end
141
-
142
- @agent.method_response(context, status, message, args)
143
- rescue Exception => ex
144
- @log.error "Error calling #{name}: #{ex}"
145
- @log.error " " + ex.backtrace.join("\n ")
146
- @agent.method_response(context, 1, "ERROR: #{ex}", args)
147
- end
148
- end
149
-
150
- def get_query(context, query, user_id)
151
- @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})"
152
-
153
- cmeta = @classes_by_name[query.class_name]
154
- objs = []
155
-
156
- # XXX: are these cases mutually exclusive?
157
-
158
- # handle queries for a certain class
159
- if cmeta
160
- objs = objs + cmeta.object_class.find_all.collect {|obj| qmfify(obj)}
161
- end
162
-
163
- # handle queries for a specific object
164
- o = find_object(context, query.object_id.object_num_high, query.object_id.object_num_low) rescue nil
165
- if o
166
- objs << qmfify(o)
167
- end
168
-
169
- objs.each do |obj|
170
- @log.debug("query_response of: #{obj.inspect}")
171
- @agent.query_response(context, obj) rescue @log.error($!.inspect)
172
- end
173
-
174
- @log.debug("completing query....")
175
- @agent.query_complete(context)
176
- end
177
-
178
- def main
179
- settings = Qmf::ConnectionSettings.new
180
- settings.host = @qmf_host
181
- settings.port = @qmf_port
182
- settings.sendUserId = @qmf_sendUserId
183
-
184
- settings.username = @qmf_user if @qmf_sendUserId
185
- settings.password = @qmf_password if @qmf_sendUserId
186
-
187
- @schemaclass = Qmf::SchemaObjectClass.new("test", "testclass")
188
-
189
- @connection = Qmf::Connection.new(settings)
190
- @log.debug(" +-- @connection created: #{@connection}")
191
- @log.debug(" +-- app name is '#{@app_name}'")
192
-
193
- @agent = Qmf::Agent.new(self, @app_name)
194
- @log.debug(" +-- @agent created: #{@agent}")
195
-
196
- @agent.set_connection(@connection)
197
- @log.debug(" +-- @agent.set_connection called")
198
-
199
- @log.debug(" +-- registering classes...")
200
- @classes_by_name.values.each do |km|
201
- @agent.register_class(km.schema_class)
202
- @log.debug(" +--+-- #{km.schema_class.package_name} #{km.schema_class.class_name} registered")
203
- end
204
-
205
- @log.debug("entering orbit....")
206
-
207
- sleep
208
- end
209
-
210
- private
211
-
212
- def result_valid(actuals, mm)
213
- (actuals.kind_of?(Array) and mm.formals_out.size == actuals.size) or mm.formals_out.size <= 1
214
- end
215
-
216
- def qmf_arguments_to_hash(args)
217
- result = {}
218
- args.each do |k,v|
219
- result[k] = v
220
- end
221
- result
222
- end
223
-
224
- def encode_object(o)
225
- return o unless o.kind_of? ::SPQR::Manageable
226
- @agent.alloc_object_id(*(o.qmf_id))
227
- end
228
-
229
- def find_object(ctx, c_id, obj_id)
230
- # XXX: context is currently ignored
231
- @log.debug("in find_object; class ID is #{c_id}, object ID is #{obj_id}...")
232
- klass = @classes_by_id[c_id]
233
- @log.debug("found class #{klass.inspect}")
234
- klass.find_by_id(obj_id) if klass
235
- end
236
-
237
- def schematize(klass)
238
- @log.info("Making a QMF schema for #{klass.spqr_meta.classname}")
239
-
240
- if klass.respond_to? :schematize
241
- @log.info("#{klass.spqr_meta.classname} knows how to schematize itself; it's probably an event class")
242
- return klass.schematize
243
- else
244
- @log.info("#{klass.spqr_meta.classname} doesn't know how to schematize itself; it's probably an object class")
245
- return schematize_object_class(klass)
246
- end
247
- end
248
-
249
- def schematize_object_class(klass)
250
- meta = klass.spqr_meta
251
- package = meta.package.to_s
252
- classname = meta.classname.to_s
253
- @log.info("+-- class #{classname} is in package #{package}")
254
-
255
- sc = Qmf::SchemaObjectClass.new(package, classname)
256
-
257
- meta.manageable_methods.each do |mm|
258
- @log.info("+-- creating a QMF schema for method #{mm}")
259
- m_opts = mm.options
260
- m_opts[:desc] ||= mm.description if mm.description
261
-
262
- method = Qmf::SchemaMethod.new(mm.name.to_s, m_opts)
263
-
264
- mm.args.each do |arg|
265
- @log.info("| +-- creating a QMF schema for arg #{arg}")
266
-
267
- encode_argument(arg, method)
268
- end
269
-
270
- sc.add_method(method)
271
- end
272
-
273
- add_attributes(sc, meta.properties, :add_property, Qmf::SchemaProperty)
274
- add_attributes(sc, meta.statistics, :add_statistic, Qmf::SchemaStatistic)
275
-
276
- sc
277
- end
278
-
279
- def add_attributes(sc, collection, msg, klass, what=nil)
280
- what ||= (msg.to_s.split("_").pop rescue "property or statistic")
281
- collection.each do |basic|
282
- basic_name = basic.name.to_s
283
- basic_type = get_xml_constant(basic.kind.to_s, ::SPQR::XmlConstants::Type)
284
- @log.debug("+-- creating a QMF schema for #{what} #{basic_name} (#{basic_type}) with options #{basic.options.inspect}")
285
- sc.send(msg, klass.new(basic_name, basic_type, basic.options))
286
- end
287
- end
288
-
289
- include ::SPQR::Util
290
-
291
- # turns an instance of a managed object into a QmfObject
292
- def qmfify(obj)
293
- @log.debug("trying to qmfify #{obj}: qmf_oid is #{obj.qmf_oid} and class_id is #{obj.class.class_id}")
294
- cm = @classes_by_name[obj.class.spqr_meta.classname.to_s]
295
- return nil unless cm
296
-
297
- qmfobj = Qmf::AgentObject.new(cm.schema_class)
298
-
299
- set_attrs(qmfobj, obj)
300
-
301
- @log.debug("calling alloc_object_id(#{obj.qmf_oid}, #{obj.class.class_id})")
302
- oid = @agent.alloc_object_id(obj.qmf_oid, obj.class.class_id)
303
-
304
- @log.debug("calling qmfobj.set_object_id(#{oid})")
305
- qmfobj.set_object_id(oid)
306
-
307
- @log.debug("returning from qmfify")
308
- qmfobj
309
- end
310
-
311
- def set_attrs(qo, o)
312
- return unless o.class.respond_to? :spqr_meta
313
-
314
- attrs = o.class.spqr_meta.properties + o.class.spqr_meta.statistics
315
-
316
- attrs.each do |a|
317
- getter = a.name.to_s
318
- @log.debug("setting property/statistic #{getter} to its value from #{o}: #{o.send(getter) if o.respond_to?(getter)}")
319
- value = o.send(getter) if o.respond_to?(getter)
320
-
321
- if value || a.kind == :bool
322
- # XXX: remove this line when/if Manageable includes an
323
- # appropriate impl method
324
- value = encode_object(value) if value.kind_of?(::SPQR::Manageable)
325
- qo[getter] = value
326
- end
327
-
328
- end
329
- end
330
- end