spqr 0.3.0 → 0.3.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 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