Module: Strelka::Discovery
- Extended by:
- Configurability, Loggability, MethodUtilities
- Included in:
- App, WebSocketServer
- Defined in:
- lib/strelka/discovery.rb
Overview
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 Strelka app files via Rubygems discovery
- Discovery and loading of Strelka app classes by name
- Discovery of data directories for strelka apps
As such it can be used in several different ways.
App File Discovery
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.
App Discovery Registration
To add a name and file path to Strelka::Discovery.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.
Loading Discovered Apps
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.
Putting it all together
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
Data Directory Discovery
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') }
Making a class Discoverable
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.
Class Method Summary collapse
-
.add_inherited_class(subclass) ⇒ Object
Register the given
subclassas having inherited a class that has been extended with Discovery. -
.app_discovery_files ⇒ Object
Return an Array of app discovery hook files found in the latest installed gems and the current $LOAD_PATH.
-
.discover_data_dirs ⇒ Object
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.
-
.discovered_apps ⇒ Object
Return a Hash of apps discovered by loading #app_discovery_files.
-
.load(app_name) ⇒ Object
Attempt to load the file associated with the specified
app_nameand return the first Strelka::App class declared in the process. -
.load_file(file) ⇒ Object
Load the specified
fileand return the first class that extends Strelka::Discovery. -
.loading_file ⇒ Object
Return the Pathname of the file being loaded by the current thread (if there is one).
-
.register_app(name, path) ⇒ Object
Register an app with the specified
namethat can be loaded from the givenpath. -
.register_apps(a_hash) ⇒ Object
Register multiple apps by passing
a_hashof names and paths.
Instance Method Summary collapse
-
#discovered_classes ⇒ Object
The Hash of Strelka::App subclasses, keyed by the Pathname of the file they were loaded from, or
nilif they weren't loaded via ::load. -
#inherited(subclass) ⇒ Object
Inheritance callback -- register the subclass with its parent for discovery.
-
#loading_file ⇒ Object
The name of the file that's currently being loaded (if any).
Methods included from MethodUtilities
attr_predicate, attr_predicate_accessor, singleton_attr_accessor, singleton_attr_reader, singleton_attr_writer, singleton_method_alias, singleton_predicate_accessor, singleton_predicate_reader
Class Method Details
.add_inherited_class(subclass) ⇒ Object
Register the given subclass as having inherited a class that has been extended
with Discovery.
236 237 238 239 |
# File 'lib/strelka/discovery.rb', line 236 def self::add_inherited_class( subclass ) self.log.debug "Registering discovered subclass %p" % [ subclass ] self.discovered_classes[ self.loading_file ] << subclass end |
.app_discovery_files ⇒ Object
Return an Array of app discovery hook files found in the latest installed gems and the current $LOAD_PATH.
168 169 170 |
# File 'lib/strelka/discovery.rb', line 168 def self::app_discovery_files return Gem.find_latest_files( self.app_discovery_file ) end |
.discover_data_dirs ⇒ Object
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.
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/strelka/discovery.rb', line 176 def self::discover_data_dirs datadirs = { '' => self.local_data_dirs } # Find all the gems that depend on Strelka gems = Gem::Specification.latest_specs.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 |
.discovered_apps ⇒ Object
Return a Hash of apps discovered by loading #app_discovery_files.
153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/strelka/discovery.rb', line 153 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 |
.load(app_name) ⇒ Object
Attempt to load the file associated with the specified app_name and return
the first Strelka::App class declared in the process.
203 204 205 206 207 208 |
# File 'lib/strelka/discovery.rb', line 203 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_file(file) ⇒ Object
Load the specified file and return the first class that extends Strelka::Discovery.
212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
# File 'lib/strelka/discovery.rb', line 212 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.last ensure Thread.current[ :__loading_file ] = nil end |
.loading_file ⇒ Object
Return the Pathname of the file being loaded by the current thread (if there is one)
229 230 231 |
# File 'lib/strelka/discovery.rb', line 229 def self::loading_file return Thread.current[ :__loading_file ] end |
.register_app(name, path) ⇒ Object
Register an app with the specified name that can be loaded from the given
path.
130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/strelka/discovery.rb', line 130 def self::register_app( name, path ) @discovered_apps ||= {} if @discovered_apps.key?( name ) warn "Can't register a second '%s' app at %s; already have one at %s" % [ name, path, @discovered_apps[name] ] return end self.log.debug "Registered app at %s as %p" % [ path, name ] @discovered_apps[ name ] = path end |
.register_apps(a_hash) ⇒ Object
Register multiple apps by passing a_hash of names and paths.
145 146 147 148 149 |
# File 'lib/strelka/discovery.rb', line 145 def self::register_apps( a_hash ) a_hash.each do |name, path| self.register_app( name, path ) end end |
Instance Method Details
#discovered_classes ⇒ Object
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.
114 |
# File 'lib/strelka/discovery.rb', line 114 singleton_attr_reader :discovered_classes |
#inherited(subclass) ⇒ Object
Inheritance callback -- register the subclass with its parent for discovery.
243 244 245 246 247 |
# File 'lib/strelka/discovery.rb', line 243 def inherited( subclass ) super Strelka::Discovery.log.info "%p inherited by discoverable class %p" % [ self, subclass ] Strelka::Discovery.add_inherited_class( subclass ) end |
#loading_file ⇒ Object
The name of the file that's currently being loaded (if any)
118 |
# File 'lib/strelka/discovery.rb', line 118 singleton_attr_reader :loading_file |