Class: Arrow::Applet
Overview
An abstract base class for Arrow applets. Provides execution logic, argument-parsing/untainting/validation, and templating through an injected factory.
Synopsis
require 'arrow/applet'
class MyApplet < Arrow::Applet applet_name “My Applet” applet_description ‘Displays a block of whatever character is ’ + ‘passed as argument’ applet_maintainer ‘Michael Granger <[email protected]>’ applet_version ‘1.01’ default_action :form
# Define the 'display' action
def_action :display do |txn|
char = txn.vargs[:char] || 'x'
char_page = self.make_character_page( char )
templ = self.load_template( :main )
templ.char_page = char_page
return templ
end
template :main, “main.tmpl”
# Define the 'form' action -- display a form that can be used to set
# the character the block is composed of. Save the returned proxy so
# the related signature values can be set.
formaction = def_action :form do |txn|
templ = self.load_template( :form )
templ.txn = txn
return templ
end
formaction.template = "form.tmpl"
# Make a page full of character +char+.
def make_character_page( char )
page = ''
40.times do
page << (char * 80) << "\n"
end
end
end
Subversion Id
$Id$
Authors
-
Michael Granger <[email protected]>
:include: LICENSE
–
Please see the file LICENSE in the BASE directory for licensing details.
Direct Known Subclasses
Defined Under Namespace
Classes: SigProxy, SignatureStruct
Constant Summary collapse
- SignatureStructDefaults =
Default-generators for Signatures which are missing one or more of the optional pairs.
{ :name => proc {|rawsig, klass| klass.name}, :description => "(none)", :maintainer => "", # Workaround for RDoc :version => nil, # Workaround for RDoc :default_action => '_default', :config => {}, :templates => {}, :validator_profiles => { :__default__ => { :optional => [:action], :constraints => { :action => /^\w+$/, }, }, } }
Class Attribute Summary collapse
-
.derivatives ⇒ Object
readonly
The Array of loaded applet classes (derivatives).
-
.filename ⇒ Object
The file containing the applet’s class definition.
-
.newly_loaded ⇒ Object
readonly
The Array of applet classes that were loaded by the most recent call to .load.
Instance Attribute Summary collapse
-
#actions ⇒ Object
readonly
The list of all valid actions on the applet.
-
#config ⇒ Object
The Arrow::Config object which contains the system’s configuration.
-
#run_count ⇒ Object
readonly
The number of times this particular applet object has been run.
-
#signature ⇒ Object
readonly
The Struct that contains the configuration values for this applet.
-
#template_factory ⇒ Object
readonly
The Arrow::TemplateFactory object used to load templates for the applet.
-
#total_stime ⇒ Object
readonly
The number of system seconds spent in this applet’s #run method.
-
#total_utime ⇒ Object
readonly
The number of user seconds spent in this applet’s #run method.
-
#uri ⇒ Object
readonly
The URI the applet answers to.
Class Method Summary collapse
-
.applet_description(desc) ⇒ Object
Set the description of the applet to
desc
. -
.applet_maintainer(info) ⇒ Object
Set the contact information for the maintainer of the applet to
info
. -
.applet_name(name) ⇒ Object
Set the name of the applet to
name
. -
.applet_version(ver) ⇒ Object
Set the contact information for the maintainer of the applet to
info
. -
.def_action(name, &block) ⇒ Object
Define an action for the applet.
-
.default_action(action) ⇒ Object
Set the default action for the applet to
action
. -
.inherited(klass) ⇒ Object
Inheritance callback: register any derivative classes so they can be looked up later.
-
.inherited_from? ⇒ Boolean
Have any subclasses of this class been created?.
-
.load(filename, include_base_classes = false) ⇒ Object
Load any applet classes in the given file and return them.
-
.make_signature ⇒ Object
Signature lookup: look for either a constant or an instance variable of the class that contains the raw signature hash, and convert it to an Arrow::Applet::SignatureStruct object.
-
.method_added(sym) ⇒ Object
Method definition callback: Check newly-defined action methods for appropriate arity.
-
.normalized_name ⇒ Object
Return the name of the applet class after stripping off any namespace-safe prefixes.
-
.signature ⇒ Object
Get the applet’s signature (an Arrow::Applet::SignatureStruct object).
-
.signature? ⇒ Boolean
Returns
true
if the applet class has a signature. -
.template(sym, path = nil) ⇒ Object
(also: templates)
Set the path for the template specified by
sym
topath
. -
.validator(action, rules = {}) ⇒ Object
Set the validator
rules
for the specifiedaction
.
Instance Method Summary collapse
-
#action_missing_action(txn, raction, *args) ⇒ Object
The action invoked if the specified action is not explicitly defined.
-
#average_usage ⇒ Object
Returns the average number of seconds (user + system) per run.
-
#delegable? ⇒ Boolean
(also: #chainable?)
Returns
true
if the receiver has a #delegate method that is inherited from somewhere other than the base Arrow::Applet class. -
#delegate(txn, chain, *args) {|chain| ... } ⇒ Object
Wrapper method for a delegation (chained) request.
-
#initialize(config, template_factory, uri) ⇒ Applet
constructor
Create a new Arrow::Applet object with the specified
config
(an Arrow::Config object),template_factory
(an Arrow::TemplateFactory object), and theuri
the applet will live under in the appserver (a String). -
#inspect ⇒ Object
Return a human-readable String representing the applet.
-
#run(txn, *args) ⇒ Object
Run the specified
action
for the giventxn
and the specifiedargs
.
Methods inherited from Object
deprecate_class_method, deprecate_method
Constructor Details
#initialize(config, template_factory, uri) ⇒ Applet
Create a new Arrow::Applet object with the specified config
(an Arrow::Config object), template_factory
(an Arrow::TemplateFactory object), and the uri
the applet will live under in the appserver (a String).
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 |
# File 'lib/arrow/applet.rb', line 428 def initialize( config, template_factory, uri ) @config = config @template_factory = template_factory @uri = uri @signature = self.class.signature.dup @run_count = 0 @total_utime = 0 @total_stime = 0 # Make a regexp out of all public <something>_action methods @actions = self.public_methods( true ). select {|meth| /^(\w+)_action$/ =~ meth }. collect {|meth| meth.gsub(/_action/, '') } @actions_regexp = Regexp.new( "^(" + actions.join( '|' ) + ")$" ) end |
Class Attribute Details
.derivatives ⇒ Object (readonly)
The Array of loaded applet classes (derivatives)
193 194 195 |
# File 'lib/arrow/applet.rb', line 193 def derivatives @derivatives end |
.filename ⇒ Object
The file containing the applet’s class definition
200 201 202 |
# File 'lib/arrow/applet.rb', line 200 def filename @filename end |
.newly_loaded ⇒ Object (readonly)
The Array of applet classes that were loaded by the most recent call to .load.
197 198 199 |
# File 'lib/arrow/applet.rb', line 197 def newly_loaded @newly_loaded end |
Instance Attribute Details
#actions ⇒ Object (readonly)
The list of all valid actions on the applet
472 473 474 |
# File 'lib/arrow/applet.rb', line 472 def actions @actions end |
#config ⇒ Object
The Arrow::Config object which contains the system’s configuration.
451 452 453 |
# File 'lib/arrow/applet.rb', line 451 def config @config end |
#run_count ⇒ Object (readonly)
The number of times this particular applet object has been run
460 461 462 |
# File 'lib/arrow/applet.rb', line 460 def run_count @run_count end |
#signature ⇒ Object (readonly)
The Struct that contains the configuration values for this applet
457 458 459 |
# File 'lib/arrow/applet.rb', line 457 def signature @signature end |
#template_factory ⇒ Object (readonly)
The Arrow::TemplateFactory object used to load templates for the applet.
469 470 471 |
# File 'lib/arrow/applet.rb', line 469 def template_factory @template_factory end |
#total_stime ⇒ Object (readonly)
The number of system seconds spent in this applet’s #run method.
466 467 468 |
# File 'lib/arrow/applet.rb', line 466 def total_stime @total_stime end |
#total_utime ⇒ Object (readonly)
The number of user seconds spent in this applet’s #run method.
463 464 465 |
# File 'lib/arrow/applet.rb', line 463 def total_utime @total_utime end |
#uri ⇒ Object (readonly)
The URI the applet answers to
454 455 456 |
# File 'lib/arrow/applet.rb', line 454 def uri @uri end |
Class Method Details
.applet_description(desc) ⇒ Object
Set the description of the applet to desc
.
230 231 232 |
# File 'lib/arrow/applet.rb', line 230 def self::applet_description( desc ) self.signature.description = desc end |
.applet_maintainer(info) ⇒ Object
Set the contact information for the maintainer of the applet to info
.
236 237 238 |
# File 'lib/arrow/applet.rb', line 236 def self::applet_maintainer( info ) self.signature.maintainer = info end |
.applet_name(name) ⇒ Object
Set the name of the applet to name
.
224 225 226 |
# File 'lib/arrow/applet.rb', line 224 def self::applet_name( name ) self.signature.name = name end |
.applet_version(ver) ⇒ Object
Set the contact information for the maintainer of the applet to info
.
242 243 244 |
# File 'lib/arrow/applet.rb', line 242 def self::applet_version( ver ) self.signature.version = ver end |
.def_action(name, &block) ⇒ Object
Define an action for the applet. Transactions which include the specified name
as the first directory of the uri after the one the applet is assigned to will be passed to the given block
. The return value from this method is an Arrow::Applet::SigProxy which can be used to set associated values in the applet’s Signature; see the Synopsis in lib/arrow/applet.rb for examples of how to use this.
401 402 403 404 405 406 407 408 409 410 411 412 413 |
# File 'lib/arrow/applet.rb', line 401 def self::def_action( name, &block ) name = '_default' if name.to_s.empty? # Action must accept at least a transaction argument unless block.arity.nonzero? raise ScriptError, "Malformed action #{name}: must accept at least one argument" end methodName = "#{name}_action" define_method( methodName, &block ) SigProxy.new( name, self ) end |
.default_action(action) ⇒ Object
Set the default action for the applet to action
.
248 249 250 |
# File 'lib/arrow/applet.rb', line 248 def self::default_action( action ) self.signature.default_action = action.to_s end |
.inherited(klass) ⇒ Object
Inheritance callback: register any derivative classes so they can be looked up later.
267 268 269 270 271 272 273 274 275 |
# File 'lib/arrow/applet.rb', line 267 def self::inherited( klass ) @inherited_from = true if defined?( @newly_loaded ) @newly_loaded.push( klass ) super else Arrow::Applet.inherited( klass ) end end |
.inherited_from? ⇒ Boolean
Have any subclasses of this class been created?
279 280 281 |
# File 'lib/arrow/applet.rb', line 279 def self::inherited_from? @inherited_from end |
.load(filename, include_base_classes = false) ⇒ Object
Load any applet classes in the given file and return them. Ignores any class which has a subclass in the file unless include_base_classes
is set false
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/arrow/applet.rb', line 297 def self::load( filename, include_base_classes=false ) self.newly_loaded.clear # Load the applet file in an anonymous module. Any applet classes get # collected via the ::inherited hook into @newly_loaded Kernel.load( filename, true ) newderivatives = @newly_loaded.dup @derivatives -= @newly_loaded @derivatives.push( *@newly_loaded ) newderivatives.each do |applet| applet.filename = filename end unless include_base_classes newderivatives.delete_if do |applet| applet.inherited_from? end end return newderivatives end |
.make_signature ⇒ Object
Signature lookup: look for either a constant or an instance variable of the class that contains the raw signature hash, and convert it to an Arrow::Applet::SignatureStruct object.
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 |
# File 'lib/arrow/applet.rb', line 345 def self::make_signature rawsig = nil if self.instance_variables.include?( "@signature" ) rawsig = self.instance_variable_get( :@signature ) elsif self.constants.include?( "Signature" ) rawsig = self.const_get( :Signature ) elsif self.constants.include?( "SIGNATURE" ) rawsig = self.const_get( :SIGNATURE ) else rawsig = {} end # Backward-compatibility: Rewrite the 'vargs' member as # 'validator_profiles' if 'vargs' exists and 'validator_profiles' # doesn't. 'vargs' member will be deleted regardless. rawsig[ :validator_profiles ] ||= rawsig.delete( :vargs ) if rawsig.key?( :vargs ) # If the superclass has a signature, inherit values from it for # pairs that are missing. if self.superclass < Arrow::Applet && self.superclass.signature? self.superclass.signature.each_pair do |member,value| next if [:name, :description, :version].include?( member ) if rawsig[member].nil? rawsig[ member ] = value.dup rescue value end end end # Apply sensible defaults for members that aren't defined SignatureStructDefaults.each do |key,val| next if rawsig[ key ] case val when Proc, Method rawsig[ key ] = val.call( rawsig, self ) when Numeric, NilClass, FalseClass, TrueClass rawsig[ key ] = val else rawsig[ key ] = val.dup end end # Signature = Struct.new( :name, :description, :maintainer, # :version, :config, :default_action, :templates, :validatorArgs, # :monitors ) members = SignatureStruct.members.collect {|m| m.to_sym} return SignatureStruct.new( *rawsig.values_at(*members) ) end |
.method_added(sym) ⇒ Object
Method definition callback: Check newly-defined action methods for appropriate arity.
286 287 288 289 290 291 |
# File 'lib/arrow/applet.rb', line 286 def self::method_added( sym ) if /^(\w+)_action$/.match( sym.to_s ) && self.instance_method( sym ).arity.zero? raise ScriptError, "Inappropriate arity for #{sym}", caller(1) end end |
.normalized_name ⇒ Object
Return the name of the applet class after stripping off any namespace-safe prefixes.
324 325 326 |
# File 'lib/arrow/applet.rb', line 324 def self::normalized_name self.name.sub( /#<Module:0x\w+>::/, '' ) end |
.signature ⇒ Object
Get the applet’s signature (an Arrow::Applet::SignatureStruct object).
331 332 333 |
# File 'lib/arrow/applet.rb', line 331 def self::signature @signature ||= make_signature() end |
.signature? ⇒ Boolean
Returns true
if the applet class has a signature.
337 338 339 |
# File 'lib/arrow/applet.rb', line 337 def self::signature? !self.signature.nil? end |
.template(sym, path = nil) ⇒ Object Also known as: templates
Set the path for the template specified by sym
to path
.
205 206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/arrow/applet.rb', line 205 def self::template( sym, path=nil ) case sym when Symbol, String self.signature.templates[ sym ] = path when Hash self.signature.templates.merge!( sym ) else raise ArgumentError, "cannot convert %s to Symbol" % [ sym ] end end |
.validator(action, rules = {}) ⇒ Object
Set the validator rules
for the specified action
.
255 256 257 258 259 260 261 262 |
# File 'lib/arrow/applet.rb', line 255 def self::validator( action, rules={} ) if action.is_a?( Hash ) && rules.empty? Arrow::Logger[ self ].debug "Assuming hash syntax for validation definition: %p" % [ action ] action, rules = *action.to_a.first end Arrow::Logger[ self ].debug "Defining validator for action %p with rules %p" % [ action, rules ] self.signature.validator_profiles[ action ] = rules end |
Instance Method Details
#action_missing_action(txn, raction, *args) ⇒ Object
The action invoked if the specified action is not explicitly defined. The default implementation will look for a template with the same key as the action, and if found, will load that and return it.
515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 |
# File 'lib/arrow/applet.rb', line 515 def action_missing_action( txn, raction, *args ) self.log.debug "In action_missing_action with: raction = %p, args = %p" % [ raction, args ] if raction && raction.to_s =~ /^([a-z]\w+)$/ tmplkey = $1.untaint self.log.debug "tmpl is: %p (%stainted)" % [ tmplkey, tmplkey.tainted? ? "" : "not " ] if @signature.templates.key?( tmplkey.to_sym ) self.log.debug "Using template sender default action for %s" % raction txn.vargs = self.make_validator( tmplkey, txn ) tmpl = self.load_template( raction.to_sym ) tmpl.txn = txn tmpl.applet = self return tmpl end end raise Arrow::AppletError, "No such action '%s' in %s" % [ raction, self.signature.name ] end |
#average_usage ⇒ Object
Returns the average number of seconds (user + system) per run.
554 555 556 557 |
# File 'lib/arrow/applet.rb', line 554 def average_usage return 0.0 if @run_count.zero? (@total_utime + @total_stime) / @run_count.to_f end |
#delegable? ⇒ Boolean Also known as: chainable?
Returns true
if the receiver has a #delegate method that is inherited from somewhere other than the base Arrow::Applet class.
506 507 508 |
# File 'lib/arrow/applet.rb', line 506 def delegable? return self.method(:delegate).to_s !~ /\(Arrow::Applet\)/ end |
#delegate(txn, chain, *args) {|chain| ... } ⇒ Object
Wrapper method for a delegation (chained) request.
499 500 501 |
# File 'lib/arrow/applet.rb', line 499 def delegate( txn, chain, *args ) yield( chain ) end |
#inspect ⇒ Object
Return a human-readable String representing the applet.
542 543 544 545 546 547 548 549 550 |
# File 'lib/arrow/applet.rb', line 542 def inspect "<%s:0x%08x: %s [%s/%s]>" % [ self.class.name, self.object_id * 2, @signature.name, @signature.version, @signature.maintainer ] end |
#run(txn, *args) ⇒ Object
Run the specified action
for the given txn
and the specified args
.
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 |
# File 'lib/arrow/applet.rb', line 477 def run( txn, *args ) self.log.debug "Running %s" % [ self.signature.name ] return self.time_request do name, *newargs = self.get_action_name( txn, *args ) txn.vargs = self.make_validator( name, txn ) action = self.find_action_method( txn, name, *newargs ) # Decline the request if the action isn't a callable object unless action.respond_to?( :arity ) self.log.info "action method (%p) doesn't do #arity, returning it as-is." % [ action ] return action end self.log.debug "calling action method %p with args (%p)" % [ action, newargs ] self.call_action_method( txn, action, *newargs ) end end |