Module: Strelka::PluginLoader

Included in:
App, WebSocketServer
Defined in:
lib/strelka/plugins.rb

Overview

Module API for the plugin system. This mixin adds the ability to load and install plugins into the extended object.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#loaded_pluginsObject

The Hash of loaded plugin modules, keyed by their downcased and symbolified name (e.g., Strelka::App::Templating => :templating)



136
137
138
# File 'lib/strelka/plugins.rb', line 136

def loaded_plugins
  @loaded_plugins
end

#plugin_path_prefixObject

The prefix path for loading plugins



147
148
149
# File 'lib/strelka/plugins.rb', line 147

def plugin_path_prefix
  @plugin_path_prefix
end

#plugins_installed_fromObject

If plugins have already been installed, this will be the call frame they were first installed from. This is used to warn about installing plugins twice.



142
143
144
# File 'lib/strelka/plugins.rb', line 142

def plugins_installed_from
  @plugins_installed_from
end

Class Method Details

.extended(mod) ⇒ Object

Extension callback -- initialize some data structures in the extended object.



124
125
126
127
128
129
130
# File 'lib/strelka/plugins.rb', line 124

def self::extended( mod )
  super
  mod.extend( Loggability )
  mod.log_to( :strelka )
  mod.loaded_plugins = Strelka::PluginRegistry.new
  mod.plugin_path_prefix = mod.name.downcase.gsub( /::/, File::SEPARATOR )
end

Instance Method Details

#application_stackObject

Return the list of plugin modules that are in effect for the current app.



289
290
291
292
# File 'lib/strelka/plugins.rb', line 289

def application_stack
  self.log.debug "Ancestors are: %p" % [ self.class.ancestors ]
  return self.ancestors.select {|mod| mod.respond_to?(:plugin_name) }
end

#dump_application_stackObject

Output the application stack into the logfile.



296
297
298
299
# File 'lib/strelka/plugins.rb', line 296

def dump_application_stack
  stack = self.application_stack.map( &:plugin_name )
  self.log.info "Application stack: request -> %s" % [ stack.join(" -> ") ]
end

#inherited(subclass) ⇒ Object

Extension callback -- add instance variables to extending objects.



158
159
160
161
162
163
164
165
166
# File 'lib/strelka/plugins.rb', line 158

def inherited( subclass )
  super
  @plugins ||= []

  subclass.loaded_plugins = self.loaded_plugins
  subclass.plugin_path_prefix = self.plugin_path_prefix
  subclass.plugins_installed_from = nil
  subclass.instance_variable_set( :@plugins, @plugins.dup )
end

#install_pluginsObject

Install the mixin part of the plugin, in the order determined by the plugin registry based on the run_outside and run_inside specifications of the plugins themselves.



259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/strelka/plugins.rb', line 259

def install_plugins
  if self.plugins_installed?
    self.log.warn "Plugins were already installed for %p from %p" %
      [ self, self.plugins_installed_from ]
    self.log.info "I'll attempt to install any new ones, but plugin ordering"
    self.log.info "and other functionality might exhibit strange behavior."
  else
    self.log.info "Installing plugins for %p." % [ self ]
  end

  sorted_plugins = self.loaded_plugins.tsort.reverse

  sorted_plugins.each do |name|
    mod = self.loaded_plugins[ name ]

    unless @plugins.include?( name ) || @plugins.include?( mod )
      self.log.debug "  skipping %s" % [ name ]
      next
    end

    self.log.info "  including %p in %p." % [ mod, self ]
    include( mod )
  end

  self.plugins_installed_from = caller( 1 ).first
end

#load_plugin(name) ⇒ Object

Load the plugin with the given name



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/strelka/plugins.rb', line 197

def load_plugin( name )

  # Just return Modules as-is
  return name if name.is_a?( Strelka::Plugin )
  mod = self.loaded_plugins[ name.to_sym ]

  unless mod.is_a?( Module )
    pluginpath = File.join( self.plugin_path_prefix, name.to_s )
    require( pluginpath )
    mod = self.loaded_plugins[ name.to_sym ] or
      raise "#{name} plugin didn't load correctly."
  end

  return mod
end

#newObject

Install the mixin part of plugins immediately before the first instance is created.



250
251
252
253
# File 'lib/strelka/plugins.rb', line 250

def new( * )
  self.install_plugins unless self.plugins_installed?
  super
end

#plugins(*names, &block) ⇒ Object Also known as: plugin

Load the plugins with the given names and install them.



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/strelka/plugins.rb', line 170

def plugins( *names, &block )
  self.log.info "Adding plugins: %s" % [ names.flatten.map(&:to_s).join(', ') ]

  # Load the associated Plugin Module objects
  names.flatten.each {|name| self.load_plugin(name) }

  # Add the name/s to the list of mixins to apply on startup
  @plugins |= names

  # Install the declarative half of the plugin immediately
  names.each do |name|
    plugin = nil

    if name.is_a?( Module )
      plugin = name
    else
      plugin = self.loaded_plugins[ name ]
    end

    self.log.debug "  registering %p" % [ name ]
    self.register_plugin( plugin, &block )
  end
end

#plugins_installed?Boolean

Returns true if the plugins for the extended app class have already been installed.

Returns:

  • (Boolean)


152
153
154
# File 'lib/strelka/plugins.rb', line 152

def plugins_installed?
  return !self.plugins_installed_from.nil?
end

#register_plugin(mod, &block) ⇒ Object

Register the plugin mod in the receiving class. This adds any declaratives and class-level data necessary for configuring the plugin.



217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/strelka/plugins.rb', line 217

def register_plugin( mod, &block )
  if mod.const_defined?( :ClassMethods )
    cm_mod = mod.const_get(:ClassMethods)
    self.log.debug "  adding class methods from %p" % [ cm_mod ]

    extend( cm_mod )
    cm_mod.instance_variables.each do |ivar|
      next if instance_variable_defined?( ivar )

      ival = cm_mod.instance_variable_get( ivar )
      copy = Strelka::DataUtilities.deep_copy( ival )

      self.log.debug "  copying class instance variable %s (%p)" % [ ivar, copy ]

      instance_variable_set( ivar, copy )
      self.log.debug "  instance variable %p set to %p in %p" %
        [ ivar, self.instance_variable_get(ivar), self ]
    end
  end

  if block
    if mod.respond_to?( :configure_block )
      mod.configure_block( self, &block )
    else
      self.log.warn "Blocked passed to %p, which doesn't support block config." %
        [ mod ]
    end
  end
end