Loading "plugins"

I am writing a log analyzer (hans.fugal.net/src/clog) that uses a sort of plugin (I call them agents) which is basically a class that gets instantiated and then called to do work. I want to include various agents with the distribution.

Where should the files end up? How should I discover the agents?

The sysadmin will be able to specify a directory (or directories) that contain his own agents, in addition to the included agents. I can scan that directory for .rb files and load them. That part works. But I don't want to make the sysadmin specify where the standard agents are in the config file - that should be implied. "require 'clog/agents/*'" would express what I want to do, although that isn't valid. I don't want to hardcode a path to /usr/local/lib/site_ruby/whatever, either.

Ultimately, the sysadmin should refer to the agents with a simple one-word name, e.g. Postfix or Bogofilter.

I don't know if my question makes sense, but I'm wondering what you all would do because there is no obvious best way to do this to me at the moment.

(and yes, I know IoC or DI would fit here but I don't want to put the burden of figuring out what the heck those are on the poor sysadmin who just needs to specify what agent to use)

What is wrong with something like:

base = "."
Dir.glob("#{base}/*.rb").each { |plugin|
  require plugin
}

where you can obviously set base to anything you want.

Patrick

···

On Sat, 26 Mar 2005 03:29:50 +0900, Hans Fugal <fugalh@xmission.com> wrote:

I am writing a log analyzer (hans.fugal.net/src/clog) that uses a sort
of plugin (I call them agents) which is basically a class that gets
instantiated and then called to do work. I want to include various
agents with the distribution.

Where should the files end up? How should I discover the agents?

The sysadmin will be able to specify a directory (or directories) that
contain his own agents, in addition to the included agents. I can scan
that directory for .rb files and load them. That part works. But I don't
want to make the sysadmin specify where the standard agents are in the
config file - that should be implied. "require 'clog/agents/*'" would
express what I want to do, although that isn't valid. I don't want to
hardcode a path to /usr/local/lib/site_ruby/whatever, either.

Ultimately, the sysadmin should refer to the agents with a simple
one-word name, e.g. Postfix or Bogofilter.

I don't know if my question makes sense, but I'm wondering what you all
would do because there is no obvious best way to do this to me at the
moment.

(and yes, I know IoC or DI would fit here but I don't want to put the
burden of figuring out what the heck those are on the poor sysadmin who
just needs to specify what agent to use)

Hi!

I am writing a log analyzer (hans.fugal.net/src/clog) that uses a sort
of plugin (I call them agents) which is basically a class that gets
instantiated and then called to do work.

I'm not sure if it fits the bill, but take a look at FreeBASE[1]. It
is part of the FreeRIDE[2] project, and also available as an
independent library(though I could not find a working download link
for just that upon basic googling)

HTH!
Shajith

1: http://freeride.rubyforge.org/wiki/wiki.pl?FreeBASE
2: http://freeride.rubyforge.org/wiki/wiki.pl

···

On Sat, 26 Mar 2005 03:29:50 +0900, Hans Fugal <fugalh@xmission.com> wrote:

The unix way would be to have /usr/share/yourapp/plugins/FooClass.rb
be the FooClass plugin, and perhaps optionally also search a user's
home directory, ~/.yourapp/plugins/FooClass.rb

Simple, concise, understandable and scalable.

···

On Sat, 26 Mar 2005 03:29:50 +0900, Hans Fugal <fugalh@xmission.com> wrote:

I am writing a log analyzer (hans.fugal.net/src/clog) that uses a sort
of plugin (I call them agents) which is basically a class that gets
instantiated and then called to do work. I want to include various
agents with the distribution.

Where should the files end up? How should I discover the agents?

The sysadmin will be able to specify a directory (or directories) that
contain his own agents, in addition to the included agents. I can scan
that directory for .rb files and load them. That part works. But I don't
want to make the sysadmin specify where the standard agents are in the
config file - that should be implied. "require 'clog/agents/*'" would
express what I want to do, although that isn't valid. I don't want to
hardcode a path to /usr/local/lib/site_ruby/whatever, either.

Ultimately, the sysadmin should refer to the agents with a simple
one-word name, e.g. Postfix or Bogofilter.

Oops I realized you might want to "discover" what classes you loaded
so something like this might be closer:

before_classes = []
ObjectSpace.each_object(Class) { |obj| before_classes << obj }

base= "."
Dir.glob("#{base}/*.rb").each { |plugin| require plugin }

new_classes = []
ObjectSpace.each_object(Class) { |obj|
  new_classes << obj unless before_classes.include?(obj)
}

And then you can further check the loaded classes (in new_classes
array) by using the respond_to? method (check to see if it is a Duck
:slight_smile:

Patrick

Shajith wrote:

Hi!

I am writing a log analyzer (hans.fugal.net/src/clog) that uses a sort
of plugin (I call them agents) which is basically a class that gets
instantiated and then called to do work.

I'm not sure if it fits the bill, but take a look at FreeBASE[1]. It
is part of the FreeRIDE[2] project, and also available as an
independent library(though I could not find a working download link
for just that upon basic googling)

HTH!
Shajith

1: http://freeride.rubyforge.org/wiki/wiki.pl?FreeBASE
2: http://freeride.rubyforge.org/wiki/wiki.pl

FreeBASE is probably overkill for this.

Curt

···

On Sat, 26 Mar 2005 03:29:50 +0900, Hans Fugal <fugalh@xmission.com> wrote:

That's a little heavy on memory (and time). Alternatively, you could do

class Object
  # Object.inherited callback
  def Object.inherited new_class
    return unless @@classes
    @@classes << new_class
  end

  def self.start_log
    @@classes =
  end

  def self.end_log
    res = @@classes
    @@classes = nil
    res
  end
end

Object.start_log
# require all plugins here
new_classes = Object.end_log

Bill

···

On Sat, 26 Mar 2005 04:03:48 +0900, Patrick Hurley <phurley@gmail.com> wrote:

Oops I realized you might want to "discover" what classes you loaded
so something like this might be closer:

before_classes =
ObjectSpace.each_object(Class) { |obj| before_classes << obj }

base= "."
Dir.glob("#{base}/*.rb").each { |plugin| require plugin }

new_classes =
ObjectSpace.each_object(Class) { |obj|
  new_classes << obj unless before_classes.include?(obj)
}

And then you can further check the loaded classes (in new_classes
array) by using the respond_to? method (check to see if it is a Duck
:slight_smile:

Patrick

--
$stdout.sync = true
"Just another Ruby hacker.".each_byte do |b|
  ('a'..'z').step do|c|print c+"\b";sleep 0.007 end;print b.chr
end; print "\n"

Patrick Hurley wrote:

Oops I realized you might want to "discover" what classes you loaded
so something like this might be closer:

before_classes =
ObjectSpace.each_object(Class) { |obj| before_classes << obj }

base= "."
Dir.glob("#{base}/*.rb").each { |plugin| require plugin }

new_classes =
ObjectSpace.each_object(Class) { |obj| new_classes << obj unless before_classes.include?(obj)
}

And then you can further check the loaded classes (in new_classes
array) by using the respond_to? method (check to see if it is a Duck
:slight_smile:

Patrick

Thanks for the ideas, but the question isn't so much about mechanics as about design. Should the agents reside in /usr/local/lib/site_ruby/... or should they reside in /usr/local/share/clog and how do I make that directory configurable at installation time or auto-discover it.