[prev in list] [next in list] [prev in thread] [next in thread] 

List:       ruby-talk
Subject:    Re: loading all plugins in a directory
From:       Martin DeMello <martindemello () gmail ! com>
Date:       2015-08-27 6:54:16
Message-ID: CAFrFfuGgfg0v1TOfT_9_507ShKzrqBDo9=pNhjp6tnj64B0i6g () mail ! gmail ! com
[Download RAW message or body]

I ended up doing it like this:

# Load all the gem dependencies of a plugin
def require_for_plugin(name, gems)
  missing = []
  gems.each do |gem|
    begin
      require gem
    rescue LoadError => e
      # If requiring a gem raises something other than LoadError let it
      # propagate upwards.
      missing << gem
    end
  end
  if !missing.empty?
    raise PluginDependencyError.new(name, missing)
  end
end

class Plugin
  def self.load_plugin(filename)
    begin
      require filename
    rescue PluginDependencyError => e
      MISSING_DEPS[e.name] = e.gems
    rescue StandardError => e
      FAILED << "#{File.basename(filename)}: #{e}"
    end
  end
end

so that I can report on missing versus failed plugins separately in the UI

martin




On Wed, Aug 26, 2015 at 1:49 PM, Martin DeMello <martindemello@gmail.com>
wrote:

> On Wed, Aug 26, 2015 at 12:41 PM, Greg Navis <contact@gregnavis.com>
> wrote:
>
>> I think your approach is fine, Martin. I have a couple minor comments.
>>
>> 1. You can split the process of loading plugins into two parts:
>> discovering plugin files and loading a single plugin. This would make the
>> method above look like:
>>
>> def self.load_all
>>   plugin_files.each do |plugin_file|
>>     load_plugin(plugin_file)
>>   end
>> end
>>
>> You get two benefits: better testability (you can test plugin discovery
>> w/o loading the plugins) and readability. If the code grows you can
>> encapsulate both actions in objects (e.g. a plugin finder and loader).
>>
>
> Thanks, that's a nice idea.
>
>
>> 2. I'm not sure whether your problem domain is but I think it's better to
>> fail when a plugin cannot be initialised for any reason. Will this work for
>> your case?
>>
>
> I don't think so; I'm writing something like pandoc but for crosswords [
> https://github.com/martindemello/pangrid], so some plugins may depend on
> third party gems, but be optional because, e.g., if you don't care about
> exporting to excel you should not need to install an excel library just to
> use pangrid.
>
> A possible solution might be to explicitly rescue load failures and
> reraise a custom MissingGem error, and then only allow that to carry on
> running, but I think that even if only one plugin loads, but it's the
> plugin you need, the tool should carry on and use it.
>
> martin
>
>

[Attachment #3 (text/html)]

<div dir="ltr">I ended up doing it like this:<div><br></div><div><div><font \
face="monospace, monospace"># Load all the gem dependencies of a \
plugin</font></div><div><font face="monospace, monospace">def \
require_for_plugin(name, gems)</font></div><div><font face="monospace, monospace">   \
missing = []</font></div><div><font face="monospace, monospace">   gems.each do \
|gem|</font></div><div><font face="monospace, monospace">      \
begin</font></div><div><font face="monospace, monospace">         require \
gem</font></div><div><font face="monospace, monospace">      rescue LoadError =&gt; \
e</font></div><div><font face="monospace, monospace">         # If requiring a gem \
raises something other than LoadError let it</font></div><div><font face="monospace, \
monospace">         # propagate upwards.</font></div><div><font face="monospace, \
monospace">         missing &lt;&lt; gem</font></div><div><font face="monospace, \
monospace">      end</font></div><div><font face="monospace, monospace">   \
end</font></div><div><font face="monospace, monospace">   if \
!missing.empty?</font></div><div><font face="monospace, monospace">      raise \
PluginDependencyError.new(name, missing)</font></div><div><font face="monospace, \
monospace">   end</font></div><div><font face="monospace, \
monospace">end</font></div><div><font face="monospace, \
monospace"><br></font></div><div><font face="monospace, monospace">class \
Plugin</font></div><div><font face="monospace, monospace">   def \
self.load_plugin(filename)</font></div><div><font face="monospace, monospace">      \
begin</font></div><div><font face="monospace, monospace">         require \
filename</font></div><div><font face="monospace, monospace">      rescue \
PluginDependencyError =&gt; e</font></div><div><font face="monospace, monospace">     \
MISSING_DEPS[<a href="http://e.name">e.name</a>] = e.gems</font></div><div><font \
face="monospace, monospace">      rescue StandardError =&gt; e</font></div><div><font \
face="monospace, monospace">         FAILED &lt;&lt; \
&quot;#{File.basename(filename)}: #{e}&quot;</font></div><div><font face="monospace, \
monospace">      end</font></div></div><div><font face="monospace, monospace">   \
end</font></div><div><font face="monospace, monospace">end</font></div><div><font \
face="monospace, monospace"><br></font></div><div><font face="arial, helvetica, \
sans-serif">so that I can report on missing versus failed plugins separately in the \
UI</font></div><div><font face="arial, helvetica, \
sans-serif"><br></font></div><div><font face="arial, helvetica, \
sans-serif">martin</font></div><div><font face="monospace, \
monospace"><br></font></div><div><font face="monospace, \
monospace"><br></font></div><div><br></div></div><div class="gmail_extra"><br><div \
class="gmail_quote">On Wed, Aug 26, 2015 at 1:49 PM, Martin DeMello <span \
dir="ltr">&lt;<a href="mailto:martindemello@gmail.com" \
target="_blank">martindemello@gmail.com</a>&gt;</span> wrote:<br><blockquote \
class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc \
solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div \
class="gmail_quote"><span class="">On Wed, Aug 26, 2015 at 12:41 PM, Greg Navis <span \
dir="ltr">&lt;<a href="mailto:contact@gregnavis.com" \
target="_blank">contact@gregnavis.com</a>&gt;</span> wrote:<br><blockquote \
class="gmail_quote" style="margin:0px 0px 0px \
0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div \
dir="ltr"><div>I think your approach is fine, Martin. I have a couple minor \
comments.<br></div><div><br></div><div>1. You can split the process of loading \
plugins into two parts: discovering plugin files and loading a single plugin. This \
would make the method above look like:</div><div><br></div><div>def \
self.load_all</div><div>   plugin_files.each do |plugin_file|</div><div>      \
load_plugin(plugin_file)</div><div>   end</div><div>end</div><div><br></div><div>You \
get two benefits: better testability (you can test plugin discovery w/o loading the \
plugins) and readability. If the code grows you can encapsulate both actions in \
objects (e.g. a plugin finder and \
loader).</div></div></blockquote><div><br></div></span><div>Thanks, that&#39;s a nice \
idea.</div><span class=""><div>  </div><blockquote class="gmail_quote" \
style="margin:0px 0px 0px \
0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div \
dir="ltr"><div>2. I&#39;m not sure whether your problem domain is but I think \
it&#39;s better to fail when a plugin cannot be initialised for any reason. Will this \
work for your case?</div></div></blockquote><div><br></div></span><div>I don&#39;t \
think so; I&#39;m writing something like pandoc but for crosswords [<a \
href="https://github.com/martindemello/pangrid" \
target="_blank">https://github.com/martindemello/pangrid</a>], so some plugins may \
depend on third party gems, but be optional because, e.g., if you don&#39;t care \
about exporting to excel you should not need to install an excel library just to use \
pangrid.</div><div><br></div><div>A possible solution might be to explicitly rescue \
load failures and reraise a custom MissingGem error, and then only allow that to \
carry on running, but I think that even if only one plugin loads, but it&#39;s the \
plugin you need, the tool should carry on and use it.</div><span class="HOEnZb"><font \
color="#888888"><div><br></div><div>martin</div><div>  \
</div></font></span></div></div></div> </blockquote></div><br></div>



[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic