module Strelka::Discovery

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

Public Class Methods

add_inherited_class( subclass ) click to toggle source

Register the given subclass as having inherited a class that has been extended with Discovery.

# File lib/strelka/discovery.rb, line 235
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() click to toggle source

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 167
def self::app_discovery_files
        return Gem.find_latest_files( self.app_discovery_file )
end
discover_data_dirs() click to toggle source

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 175
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() click to toggle source

Return a Hash of apps discovered by loading app_discovery_files.

# File lib/strelka/discovery.rb, line 152
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 ) click to toggle source

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 202
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 ) click to toggle source

Load the specified file and return the first class that extends Strelka::Discovery.

# File lib/strelka/discovery.rb, line 211
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() click to toggle source

Return the Pathname of the file being loaded by the current thread (if there is one)

# File lib/strelka/discovery.rb, line 228
def self::loading_file
        return Thread.current[ :__loading_file ]
end
register_app( name, path ) click to toggle source

Register an app with the specified name that can be loaded from the given path.

# File lib/strelka/discovery.rb, line 129
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 ) click to toggle source

Register multiple apps by passing a_hash of names and paths.

# File lib/strelka/discovery.rb, line 144
def self::register_apps( a_hash )
        a_hash.each do |name, path|
                self.register_app( name, path )
        end
end

Public Instance Methods

discovered_classes() click to toggle source

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 113
singleton_attr_reader :discovered_classes
inherited( subclass ) click to toggle source

Inheritance callback – register the subclass with its parent for discovery.

Calls superclass method
# File lib/strelka/discovery.rb, line 242
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() click to toggle source

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

# File lib/strelka/discovery.rb, line 117
singleton_attr_reader :loading_file