Class: Arrow::Applet

Inherits:
Object show all
Defined in:
lib/arrow/applet.rb

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

:include: LICENSE

Please see the file LICENSE in the BASE directory for licensing details.

Direct Known Subclasses

Service

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

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

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

.derivativesObject (readonly)

The Array of loaded applet classes (derivatives)



193
194
195
# File 'lib/arrow/applet.rb', line 193

def derivatives
  @derivatives
end

.filenameObject

The file containing the applet’s class definition



200
201
202
# File 'lib/arrow/applet.rb', line 200

def filename
  @filename
end

.newly_loadedObject (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

#actionsObject (readonly)

The list of all valid actions on the applet



472
473
474
# File 'lib/arrow/applet.rb', line 472

def actions
  @actions
end

#configObject

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_countObject (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

#signatureObject (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_factoryObject (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_stimeObject (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_utimeObject (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

#uriObject (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?

Returns:

  • (Boolean)


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_signatureObject

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_nameObject

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

.signatureObject

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.

Returns:

  • (Boolean)


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.

Raises:

  • (Arrow::AppletError)


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_usageObject

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.

Returns:

  • (Boolean)


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.

Yields:

  • (chain)


499
500
501
# File 'lib/arrow/applet.rb', line 499

def delegate( txn, chain, *args )
	yield( chain )
end

#inspectObject

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