Class: Arrow::AppletRegistry
Overview
The Arrow::AppletRegistry class, a derivative of Arrow::Object. Instances of this class are responsible for loading and maintaining the collection of Arrow::Applets registered with an Arrow::Broker.
VCS Id
$Id$
Authors
-
Michael Granger <[email protected]>
Please see the file LICENSE in the top-level directory for licensing details.
Defined Under Namespace
Classes: AppletFile, ChainLink
Constant Summary collapse
- IDENTIFIER =
Pattern for matching valid components of the uri
/^\w[-\w]*/
Instance Attribute Summary collapse
-
#config ⇒ Object
readonly
The Arrow::Config object which specified the registry’s behavior.
-
#filemap ⇒ Object
readonly
The internal hash of Entry objects keyed by the file they were loaded from.
-
#load_time ⇒ Object
The Time when the registry was last loaded.
-
#path ⇒ Object
readonly
The path the registry will search when looking for new/updated/deleted applets.
-
#template_factory ⇒ Object
readonly
The Arrow::TemplateFactory which will be given to any loaded applet.
-
#urispace ⇒ Object
readonly
The internal hash of Entry objects, keyed by URI.
Class Method Summary collapse
-
.get_safe_gemhome ⇒ Object
Get the ‘gem home’ from RubyGems and check it for sanity.
Instance Method Summary collapse
-
#build_classmap ⇒ Object
Make and return a Hash which inverts the registry’s applet layout into a map of class name to the URIs onto which instances of them should be installed.
-
#check_for_updates ⇒ Object
Check the applets path for new/updated/deleted applets if the poll interval has passed.
-
#find_applet_chain(uri) ⇒ Object
Find the chain of applets indicated by the given
uri
and return an Array of ChainLink structs. -
#find_appletfiles(excludeList = []) ⇒ Object
Find applet files by looking in the applets path of the registry’s configuration for files matching the configured pattern.
-
#initialize(config) ⇒ AppletRegistry
constructor
Create a new Arrow::AppletRegistry object.
-
#initialize_copy(other) ⇒ Object
Copy initializer – reload applets for cloned registries.
-
#load_applets ⇒ Object
(also: #reload_applets)
Load any new applets in the registry’s path, reload any previously- loaded applets whose files have changed, and discard any applets whose files have disappeared.
-
#load_applets_from_file(path) ⇒ Object
Load the applet classes from the given
path
and return them in an Array. -
#load_gems ⇒ Object
(also: #reload_gems)
Check the config for any gems to load, load them, and add their template and applet directories to the appropriate parts of the config.
-
#purge_deleted_applets(*missing_files) ⇒ Object
Remove the applets that were loaded from the given
missing_files
from the registry. -
#register_applet_class(klass) ⇒ Object
Register an instance of the given
klass
with the broker if the classmap includes it, returning the URIs which were mapped to instances of theklass
.
Methods inherited from Object
deprecate_class_method, deprecate_method, inherited
Constructor Details
#initialize(config) ⇒ AppletRegistry
Create a new Arrow::AppletRegistry object.
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
# File 'lib/arrow/appletregistry.rb', line 226 def initialize( config ) @config = config @path = @config.applets.path @classmap = nil @filemap = {} @urispace = {} @template_factory = Arrow::TemplateFactory.new( config ) @load_time = nil self.load_gems self.load_applets super() end |
Instance Attribute Details
#config ⇒ Object (readonly)
The Arrow::Config object which specified the registry’s behavior.
273 274 275 |
# File 'lib/arrow/appletregistry.rb', line 273 def config @config end |
#filemap ⇒ Object (readonly)
The internal hash of Entry objects keyed by the file they were loaded from
270 271 272 |
# File 'lib/arrow/appletregistry.rb', line 270 def filemap @filemap end |
#load_time ⇒ Object
The Time when the registry was last loaded
279 280 281 |
# File 'lib/arrow/appletregistry.rb', line 279 def load_time @load_time end |
#path ⇒ Object (readonly)
The path the registry will search when looking for new/updated/deleted applets
282 283 284 |
# File 'lib/arrow/appletregistry.rb', line 282 def path @path end |
#template_factory ⇒ Object (readonly)
The Arrow::TemplateFactory which will be given to any loaded applet
276 277 278 |
# File 'lib/arrow/appletregistry.rb', line 276 def template_factory @template_factory end |
#urispace ⇒ Object (readonly)
The internal hash of Entry objects, keyed by URI
266 267 268 |
# File 'lib/arrow/appletregistry.rb', line 266 def urispace @urispace end |
Class Method Details
.get_safe_gemhome ⇒ Object
Get the ‘gem home’ from RubyGems and check it for sanity. Returns nil
if it is not an extant, non-world-writable directory.
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
# File 'lib/arrow/appletregistry.rb', line 197 def self::get_safe_gemhome gemhome = Pathname.new( Gem.user_home ) + 'gems' gemhome.untaint if ! gemhome.directory? Arrow::Logger[ self ].notice "Gem home '%s' is not a directory; ignoring it" % [ gemhome ] return nil elsif (gemhome.stat.mode & 0002).nonzero? Arrow::Logger[ self ].notice "Gem home '%s' is world-writable; ignoring it" % [ gemhome ] return nil end Arrow::Logger[ self ].info "Got safe gem home: %p" % [ gemhome ] return gemhome end |
Instance Method Details
#build_classmap ⇒ Object
Make and return a Hash which inverts the registry’s applet layout into a map of class name to the URIs onto which instances of them should be installed.
527 528 529 530 531 532 533 534 535 536 537 538 539 |
# File 'lib/arrow/appletregistry.rb', line 527 def build_classmap classmap = Hash.new {|ary,k| ary[k] = []} # Invert the applet layout into Class => [ uris ] so as classes # load, we know where to put 'em. @config.applets.layout.each do |uri, klassname| uri = uri.to_s.sub( %r{^/}, '' ) self.log.debug "Mapping %p to %p" % [ klassname, uri ] classmap[ klassname ] << uri end return classmap end |
#check_for_updates ⇒ Object
Check the applets path for new/updated/deleted applets if the poll interval has passed.
411 412 413 414 415 416 417 418 419 420 421 422 |
# File 'lib/arrow/appletregistry.rb', line 411 def check_for_updates interval = @config.applets.pollInterval if interval.nonzero? if Time.now - self.load_time > interval self.log.debug "Checking for applet updates: poll interval at %ds" % [ interval ] self.reload_applets self.reload_gems end else self.log.debug "Dynamic applet reloading turned off, continuing" end end |
#find_applet_chain(uri) ⇒ Object
Find the chain of applets indicated by the given uri
and return an Array of ChainLink structs.
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 |
# File 'lib/arrow/appletregistry.rb', line 378 def find_applet_chain( uri ) self.log.debug "Searching urispace for appletchain for %p" % [ uri ] uri_parts = uri.sub(%r{^/(?=.)}, '').split(%r{/}).grep( IDENTIFIER ) appletchain = [] args = [] # If there's an applet installed at the base, prepend it to the # appletchain if @urispace.key?( "" ) appletchain << ChainLink.new( @urispace[""], "", uri_parts ) self.log.debug "Added base applet to chain." end # Only allow reference to internal handlers (handlers mapped to # directories that start with '_') if allow_internal is set. self.log.debug "Split URI into parts: %p" % [uri_parts] # Map uri fragments onto registry entries, stopping at any element # which isn't a valid Ruby identifier. uri_parts.each_index do |i| newuri = uri_parts[0,i+1].join("/") # self.log.debug "Testing %s against %p" % [ newuri, @urispace.keys.sort ] appletchain << ChainLink.new( @urispace[newuri], newuri, uri_parts[(i+1)..-1] ) if @urispace.key?( newuri ) end return appletchain end |
#find_appletfiles(excludeList = []) ⇒ Object
Find applet files by looking in the applets path of the registry’s configuration for files matching the configured pattern. Return an Array of fully-qualified applet files. If the optional excludeList
is given, exclude any files specified from the return value.
546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 |
# File 'lib/arrow/appletregistry.rb', line 546 def find_appletfiles( excludeList=[] ) files = [] dirCount = 0 # The Arrow::Path object will only give us extant directories... @path.each do |path| # Look for files under a directory dirCount += 1 pat = File.join( path, @config.applets.pattern ) pat.untaint self.log.debug "Looking for applets: %p" % [ pat ] files.push( *Dir[ pat ] ) end self.log.info "Fetched %d applet file paths from %d directories (out of %d)" % [ files.nitems, dirCount, @path.dirs.nitems ] files.each {|file| file.untaint } return files - excludeList end |
#initialize_copy(other) ⇒ Object
Copy initializer – reload applets for cloned registries.
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/arrow/appletregistry.rb', line 244 def initialize_copy( other ) # :nodoc: @config = other.config.dup @path = @config.applets.path.dup @classmap = nil @filemap = {} @urispace = {} @template_factory = Arrow::TemplateFactory.new( config ) @load_time = nil self.load_gems self.load_applets super end |
#load_applets ⇒ Object Also known as: reload_applets
Load any new applets in the registry’s path, reload any previously- loaded applets whose files have changed, and discard any applets whose files have disappeared.
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 |
# File 'lib/arrow/appletregistry.rb', line 352 def load_applets self.log.debug "Loading applet registry" @classmap = self.build_classmap filelist = self.find_appletfiles # Remove applet files which correspond to files that are no longer # in the list self.purge_deleted_applets( @filemap.keys - filelist ) unless @filemap.empty? # Now search the applet path for applet files filelist.each do |appletfile| self.log.debug "Found applet file %p" % appletfile self.load_applets_from_file( appletfile ) self.log.debug "After %s, registry has %d entries" % [ appletfile, @urispace.length ] end self.load_time = Time.now end |
#load_applets_from_file(path) ⇒ Object
Load the applet classes from the given path
and return them in an Array. If a block is given, then each loaded class is yielded to the block in turn, and the return values are used in the Array instead.
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 |
# File 'lib/arrow/appletregistry.rb', line 449 def load_applets_from_file( path ) # Reload mode -- don't do anything unless the file's been updated if @filemap.key?( path ) file = @filemap[ path ] if file.has_changed? self.log.info "File %p has changed since loaded. Reloading." % [path] self.purge_deleted_applets( path ) elsif !file.loaded_okay? self.log.warning "File %s could not be loaded: %s" % [path, file.exception.] file.exception.backtrace.each do |frame| self.log.debug " " + frame end else self.log.debug "File %p has not changed." % [path] return nil end end self.log.debug "Attempting to load applet objects from %p" % path @filemap[ path ] = AppletFile.new( path ) @filemap[ path ].appletclasses.each do |appletclass| self.log.debug "Registering applet class %s from %p" % [appletclass.name, path] begin uris = self.register_applet_class( appletclass ) @filemap[ path ].uris << uris rescue ::Exception => err frames = filter_backtrace( err.backtrace ) self.log.error "%s loaded, but failed to initialize: %s" % [ appletclass.normalized_name, err., ] self.log.debug " " + frames.collect {|frame| "[%s]" % frame }.join(" ") @filemap[ path ].exception = err end end end |
#load_gems ⇒ Object Also known as: reload_gems
Check the config for any gems to load, load them, and add their template and applet directories to the appropriate parts of the config.
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 |
# File 'lib/arrow/appletregistry.rb', line 291 def load_gems self.log.info "Loading gems." unless @config.respond_to?( :gems ) self.log.debug "No gems section in the config; skipping gemified applets" return end self.log.debug " using gem config: %p" % [ config.gems ] # Make sure the 'gem home' is a directory and not world-writable; don't use it # otherwise gemhome = self.class.get_safe_gemhome paths = @config.gems.path.collect {|path| path.untaint } self.log.debug " safe gem paths: %p" % [ paths ] Gem.use_paths( Apache.server_root, paths ) @config.gems.applets.to_h.each do |gemname, reqstring| self.log.debug " trying to load %s %s" % [ gemname, reqstring ] reqstring = '>= 0' if reqstring.nil? or reqstring.empty? begin self.log.info "Activating gem %s (%s)" % [ gemname, reqstring ] Gem.activate( gemname.to_s, reqstring ) self.log.info " gem %s activated." % [ gemname ] rescue LoadError => err self.log.crit "%s while activating '%s': %s" % [ err.class.name, gemname, err. ] err.backtrace.each do |frame| self.log.debug " " + frame end else datadir = Pathname.new( Gem.datadir(gemname.to_s) ) appletdir = datadir + 'applets' templatedir = datadir + 'templates' self.log.debug "Adding appletdir %p and templatedir %p" % [ appletdir, templatedir ] @path << appletdir.to_s @template_factory.path << templatedir.to_s end end self.log.info " done loading gems (path is now: %p)." % [ @path ] end |
#purge_deleted_applets(*missing_files) ⇒ Object
Remove the applets that were loaded from the given missing_files
from the registry.
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 |
# File 'lib/arrow/appletregistry.rb', line 427 def purge_deleted_applets( *missing_files ) # For each filename, find the applets which were loaded from it, # map the name of each applet to a uri via the classmap, and delete # the entries by uri missing_files.flatten.each do |filename| self.log.info "Unregistering old applets from %p" % [ filename ] @filemap[ filename ].uris.each do |uri| self.log.debug " Removing %p, registered at %p" % [ @urispace[uri], uri ] @urispace.delete( uri ) end @filemap.delete( filename ) end end |
#register_applet_class(klass) ⇒ Object
Register an instance of the given klass
with the broker if the classmap includes it, returning the URIs which were mapped to instances of the klass
.
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 |
# File 'lib/arrow/appletregistry.rb', line 495 def register_applet_class( klass ) uris = [] # Trim the Module serving as private namespace from the # class name appletname = klass.normalized_name self.log.debug "Registering %p applet as %p" % [ klass.name, appletname ] # Look for a uri corresponding to the loaded class, and instantiate it # if there is one. if @classmap.key?( appletname ) self.log.debug " Found one or more uris for '%s'" % appletname # Create a new instance of the applet for each uri it's # registered under, then wrap that in a RegistryEntry # and put it in the entries hash we'll return later. @classmap[ appletname ].each do |uri| @urispace[ uri ] = klass.new( @config, @template_factory, uri ) uris << uri end else self.log.debug "No uri for '%s': Not instantiated" % appletname end return uris end |