The Strelka application-discovery system.
This module provides a mechanism for registering Strelka apps and their resources for discovery by the strelka CLI and other systems.
It's responsible for three kinds of discovery:
Discovery of data directories for strelka apps
As such it can be used in several different ways.
If you have an app that you wish to be discoverable, create a
lib/strelka/apps.rb
file. This file will be added to those
returned by the ::app_discovery_files
call, which is the list loaded by ::discovered_apps.
To add a name and file path to ::discovered_apps, you
can call ::register_app.
This will check to make sure no other apps are registered with the same
name. To register several at the same time, call ::register_apps with a
Hash of name => path
pairs.
To load a discovered app, call ::load with its registered name.
This will load the associated file and returns the first Ruby class to inherit from a discoverable app class like Strelka::App or Strelka::WebSocketServer.
Say, for example, you were putting together an acme-apps
gem
for the Acme company that contained Strelka
apps for a web store and a CMS. You could add a
lib/strelka/apps.rb
file to the acme-apps
gem
that contained the following:
# -*- ruby -*- require 'strelka/discovery' Strelka::Discovery.register_apps( 'acme-store' => 'lib/acme/store.rb', 'acme-cms' => 'lib/acme/cms.rb' )
This would let you do:
$ gem install acme-apps $ strelka start acme-store
If your app requires some filesystem resources, a good way to distribute
these is in your gem's “data directory”. This is a directory in your
gem called data/«your gem name»
, and can be found via:
Gem.datadir( your_gem_name )
Strelka::Discoverable builds on top of this, and can return a Hash of glob patterns that will match the data directories of all gems that depend on Strelka, keyed by gem name. You can use this to populate search paths for templates, static assets, etc.
template_paths = Strelka::Discovery.discover_data_dirs. flat_map {|_, pattern| Dir.glob(pattern + '/templates') }
If you write your own app base class (e.g., Strelka::App, Strelka::WebSocketServer), you can make it discoverable by extending it with this module. You typically won't have to do this unless you're working on Strelka itself.
Default config
Register the given subclass
as having inherited a class that
has been extended with Discovery.
# File lib/strelka/discovery.rb, line 250
def self::add_inherited_class( subclass )
self.log.debug "Registering discovered subclass %p" % [ subclass ]
self.discovered_classes[ self.loading_file ] << subclass
end
Return an Array of app discovery hook files found in the latest installed gems and the current $LOAD_PATH.
# File lib/strelka/discovery.rb, line 171
def self::app_discovery_files
return Gem.find_latest_files( self.app_discovery_file )
end
Configure the App. Override this if you wish to add additional configuration to the 'app' section of the config that will be passed to you when the config is loaded.
# File lib/strelka/discovery.rb, line 179
def self::configure( config=nil )
config = self.defaults.merge( config || {} )
self.app_discovery_file = config[:app_discovery_file]
self.local_data_dirs = config[:local_data_dirs]
end
Return a Hash of glob patterns for matching data directories for the latest versions of all installed gems which have a dependency on Strelka, keyed by gem name.
# File lib/strelka/discovery.rb, line 190
def self::discover_data_dirs
datadirs = {
'' => self.local_data_dirs
}
# Find all the gems that depend on Strelka
gems = Gem::Specification.find_all do |gemspec|
gemspec.dependencies.find {|dep| dep.name == 'strelka'}
end
self.log.debug "Found %d gems with a Strelka dependency" % [ gems.length ]
# Find all the files under those gems' data directories that match the application
# pattern
gems.sort.reverse.each do |gemspec|
# Only look at the latest version of the gem
next if datadirs.key?( gemspec.name )
datadirs[ gemspec.name ] = File.join( gemspec.full_gem_path, "data", gemspec.name )
end
self.log.debug " returning data directories: %p" % [ datadirs ]
return datadirs
end
Return a Hash of apps discovered by loading app_discovery_files.
# File lib/strelka/discovery.rb, line 156
def self::discovered_apps
unless @discovered_apps
@discovered_apps ||= {}
self.app_discovery_files.each do |path|
self.log.debug "Loading discovery file %p" % [ path ]
Kernel.load( path )
end
end
return @discovered_apps
end
Attempt to load the file associated with the specified
app_name
and return the first Strelka::App class declared in the process.
# File lib/strelka/discovery.rb, line 217
def self::load( app_name )
apps = self.discovered_apps or return nil
file = apps[ app_name ] or return nil
return self.load_file( file )
end
Load the specified file
and return the first class that
extends Strelka::Discovery.
# File lib/strelka/discovery.rb, line 226
def self::load_file( file )
self.log.debug "Loading application/s from %p" % [ file ]
Thread.current[ :__loading_file ] = loading_file = file
self.discovered_classes.delete( loading_file )
Kernel.load( loading_file.to_s )
new_subclasses = self.discovered_classes[ loading_file ]
self.log.debug " loaded %d new app class/es" % [ new_subclasses.size ]
return new_subclasses.first
ensure
Thread.current[ :__loading_file ] = nil
end
Return the Pathname of the file being loaded by the current thread (if there is one)
# File lib/strelka/discovery.rb, line 243
def self::loading_file
return Thread.current[ :__loading_file ]
end
Register an app with the specified name
that can be loaded
from the given path
.
# File lib/strelka/discovery.rb, line 134
def self::register_app( name, path )
@discovered_apps ||= {}
if @discovered_apps.key?( name )
raise "Can't register a second '%s' app at %s; already have one at %s" %
[ name, path, @discovered_apps[name] ]
end
self.log.debug "Registered app at %s as %p" % [ path, name ]
@discovered_apps[ name ] = path
end
Register multiple apps by passing a_hash
of names and paths.
# File lib/strelka/discovery.rb, line 148
def self::register_apps( a_hash )
a_hash.each do |name, path|
self.register_app( name, path )
end
end
The glob(3) pattern for matching the discovery hook file.
# File lib/strelka/discovery.rb, line 113
singleton_attr_accessor :app_discovery_file
The Hash of Strelka::App subclasses, keyed by the
Pathname of the file they were loaded from, or nil
if they
weren't loaded via ::load.
# File lib/strelka/discovery.rb, line 109
singleton_attr_reader :discovered_classes
Inheritance callback – register the subclass with its parent for discovery.
# File lib/strelka/discovery.rb, line 257
def inherited( subclass )
super
Strelka::Discovery.log.info "%p inherited by discoverable class %p" % [ self, subclass ]
Strelka::Discovery.add_inherited_class( subclass )
end
The name of the file that's currently being loaded (if any)
# File lib/strelka/discovery.rb, line 122
singleton_attr_reader :loading_file
The glob(3) pattern for matching local data directories during discovery. Local data directories are evaluated relative to the CWD.
# File lib/strelka/discovery.rb, line 118
singleton_attr_accessor :local_data_dirs