Strelka::

Discovery

module
Extended With
Loggability
Configurability
Strelka::MethodUtilities

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:

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 ::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.

Constants

CONFIG_DEFAULTS

Default config

Public Class Methods

anchor
add_inherited_class( subclass )

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
anchor
app_discovery_files()

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
anchor
configure( config=nil )

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
anchor
discover_data_dirs()

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
anchor
discovered_apps()

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
anchor
load( app_name )

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
anchor
load_file( file )

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
anchor
loading_file()

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
anchor
register_app( name, path )

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
anchor
register_apps( a_hash )

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

Public Instance Methods

anchor
app_discovery_file()

The glob(3) pattern for matching the discovery hook file.

# File lib/strelka/discovery.rb, line 113
singleton_attr_accessor :app_discovery_file
anchor
discovered_classes()

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
anchor
inherited( subclass )

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
anchor
loading_file()

The name of the file that's currently being loaded (if any)

# File lib/strelka/discovery.rb, line 122
singleton_attr_reader :loading_file
anchor
local_data_dirs()

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