Class: Arrow::AppletRegistry
- Inherits:
-
Object
- Object
- Object
- Arrow::AppletRegistry
- Extends:
- Forwardable
- Includes:
- Loggable, Enumerable
- Defined in:
- lib/arrow/appletregistry.rb
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
Please see the file LICENSE in the top-level directory for licensing details.
Defined Under Namespace
Classes: AppletFile, ChainLink
Constant Summary
- IDENTIFIER =
Pattern for matching valid components of the uri
/^\w[-\w]*/
Instance Attribute Summary
-
- (Object) config
readonly
The Arrow::Config object which specified the registry’s behavior.
-
- (Object) filemap
readonly
The internal hash of Entry objects keyed by the file they were loaded from.
-
- (Object) load_time
The Time when the registry was last loaded.
-
- (Object) path
readonly
The path the registry will search when looking for new/updated/deleted applets.
-
- (Object) template_factory
readonly
The Arrow::TemplateFactory which will be given to any loaded applet.
-
- (Object) urispace
readonly
The internal hash of Entry objects, keyed by URI.
Class Method Summary
-
+ (Object) get_safe_gemhome
Get the ‘gem home’ from RubyGems and check it for sanity.
Instance Method Summary
-
- (Object) build_classmap
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.
-
- (Object) check_for_updates
Check the applets path for new/updated/deleted applets if the poll interval has passed.
-
- (Object) find_applet_chain(uri)
Find the chain of applets indicated by the given uri and return an Array of ChainLink structs.
-
- (Object) find_appletfiles(excludeList = [])
Find applet files by looking in the applets path of the registry’s configuration for files matching the configured pattern.
-
- (AppletRegistry) initialize(config)
constructor
Create a new Arrow::AppletRegistry object.
-
- (Object) initialize_copy(other)
Copy initializer — reload applets for cloned registries.
-
- (Object) load_applets
(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.
-
- (Object) load_applets_from_file(path)
Load the applet classes from the given path and return them in an Array.
-
- (Object) load_gems
(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.
-
- (Object) purge_deleted_applets(*missing_files)
Remove the applets that were loaded from the given missing_files from the registry.
-
- (Object) register_applet_class(klass)
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.
Methods included from Loggable
Methods inherited from Object
deprecate_class_method, deprecate_method, inherited
Methods included from Loggable
Constructor Details
- (AppletRegistry) initialize(config)
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
- (Object) config (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 |
- (Object) filemap (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 |
- (Object) load_time
The Time when the registry was last loaded
279 280 281 |
# File 'lib/arrow/appletregistry.rb', line 279 def load_time @load_time end |
- (Object) path (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 |
- (Object) template_factory (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 |
- (Object) urispace (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
+ (Object) get_safe_gemhome
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
- (Object) build_classmap
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 |
- (Object) check_for_updates
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 |
- (Object) find_applet_chain(uri)
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 |
- (Object) find_appletfiles(excludeList = [])
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 |
- (Object) initialize_copy(other)
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 |
- (Object) load_applets 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 |
- (Object) load_applets_from_file(path)
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 |
- (Object) load_gems 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 |
- (Object) purge_deleted_applets(*missing_files)
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 |
- (Object) register_applet_class(klass)
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 |