Coder Social home page Coder Social logo

pluginator's Introduction

Pluginator

Gem Version Code Climate Coverage Status Build Status Dependency Status Inline docs Yard Docs Github Code

Gem plugin system management, detects plugins using Gem.find_file, $LOAD_PATH and $LOADED_FEATURES.

Pluginator works with ruby 1.9.3+ and rubygems 2.0.0+.

Pluginator tries to stay out of your way, you do not have to include or inherit anything. Pluginator only finds and groups plugins, rest is up to you, you decide what methods to define and how to find them.

Defining plugins

create a gem with a path:

lib/plugins/<group>/<type>/<name>.rb

with a class inside:

<group>::<type>::<name>

where <type> can be nested

Loading plugins

rvm2plugins = Pluginator.find("<group>")
type_plugins = rvm2plugins["<type>"]
types = rvm2plugins.types

Usage

Pluginator.find("<group>") => Pluginator object
plugins = Pluginator.find("<group>", type: "<type>", prefix: "/(lib|local_lib)", extends: %i[<extensions>])
plugins["<type>"] => Array of plugins
plugins.type      => Array of plugins for type defined with `type: "<type>"`
plugins.types     => Array of types
  • "<group>" - Load plugins for given group.
  • type: "<type>" - Load plugins only of given type, optional, makes type method accessible.
  • prefix: "/prefix" - Load plugins only from this paths, optional, default /lib.
  • extends: %i[<extensions>] - Extend pluginator with given extensions.

Extensions

Pluginator comes with few handful extensions.

Class exist

Check if plugin with given class name exists.

plugins = Pluginator.find("<group>", extends: %i[class_exist]})
plugins.class_exist?("<type>", "<name>") => true or false

First ask

Call a method on plugin and return first one that returns true.

plugins = Pluginator.find("<group>", extends: %i[first_ask])
plugins.first_ask( "<type>", "method_to_call", *params) => plugin or nil
plugins.first_ask!("<type>", "method_to_call", *params) => plugin or exception PluginatorError

First class

Find first plugin that class matches the given name.

plugins = Pluginator.find("<group>", extends: %i[first_class])
plugins.first_class( "<type>", "<name>") => plugin or nil
plugins.first_class!("<type>", "<name>") => plugin or exception PluginatorError

Matching

Map array of names to available plugins.

plugins = Pluginator.find("<group>", extends: %i[matching])
plugins.matching( "<type>", [<array_of_names>]) => [plugins] # nil for missing ones
plugins.matching!("<type>", [<array_of_names>]) => [plugins] or exception PluginatorError

Your own ones

You can define your own extensions for pluginator, for example:

plugins/pluginator/extensions/first_one.rb

with:

module Pluginator::Extensions
  class FirstOne
    def first_one(type)
      @plugins[type].first
    end
  end
end

And now you can use it:

plugins = Pluginator.find("<group>", extends: %i[first_one])
plugins.first_one("<type>") => first_plugin # nil when none

Exceptions

  • PluginatorError - base error for all Pluginator errors
  • MissingPlugin - raised when plugin can not be found, generated by *! methods
  • MissingType - raised when type can not be found, generated by *! methods

Versioning plugins

In case plugin gets moved to other gem you can specify which gem to use for loading the plugin by specifying plugin version in gems gemspec metadata:

s.metadata = {
  "plugins/v2test/stats/max.rb" => "1"
}

Examples

Example 1 - task plugins

plugins/rvm2/cli/echo.rb:

class Rvm2::Cli::Echo
  def self.question? command
    command == "echo"
  end
  def answer param
    puts param
  end
end

where question? and answer are user defined methods

Now the plugin can be used:

require "pluginator"

rvm2plugins = Pluginator.find("rvm2")
plugin = rvm2plugins["cli"].first{ |plugin|
  plugin.question?("echo")
}
plugin.new.answer("Hello world")

Or using extensions:

require "pluginator"

plugin = Pluginator.find("rvm2", extends: %i[first_ask]).first_ask("cli", &:question?, "echo")
plugin.new.answer("Hello world")

Example 2 - hook plugins

plugins/rvm2/hooks/after_install/show.rb:

class Rvm2::Hooks::AfterInstall::Show
  def self.execute name, path
    puts "Ruby #{name.inspect} was installed in #{path.inspect}."
  end
end

and using hooks:

require "pluginator"

Pluginator.find("rvm2", type: "hooks/after_install").type.each{ |plugin|
  plugin.execute(name, path)
}

Testing

NOEXEC_DISABLE=1 rake test

License

Copyright 2013-2017 Michal Papis [email protected]

pluginator is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

pluginator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with pluginator. If not, see http://www.gnu.org/licenses/.

For details on adding copyright visit: https://www.gnu.org/licenses/gpl-howto.html

pluginator's People

Contributors

envygeeks avatar jkogara avatar logicminds avatar mose avatar mpapis avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pluginator's Issues

Add tests

Add tests so we can be sure it works

use different plugin path

I am trying to fix another gem's problem that uses plugins and wanted to know if the pluginator could use a different path besides lib/plugins/.

I would like the lookup path to be 'lib/kitchen' or 'lib/inspec' and since these tools are well established there is no way to change the plugin directory to lib/plugins

error running tests

I can't seem to run these tests locally. Am I missing something?

docker run -ti --rm -w /app -v ${PWD}:/app ruby:2.3 bash
root@48f9b6ff0907:/app# bundle install
Fetching gem metadata from https://rubygems.org/.............
Fetching version metadata from https://rubygems.org/.
Installing rake 12.0.0
Installing ansi 1.5.0
Installing ast 2.3.0
Installing builder 3.2.3
Installing minitest 5.10.2
Installing ruby-progressbar 1.8.1
Installing parallel 1.11.2
Using pluginator 1.5.0 from source at `.`
Installing powerpack 0.1.1
Installing unicode-display_width 1.2.1
Using bundler 1.14.6
Installing rainbow 2.2.2 with native extensions
Installing parser 2.4.0.0
Installing minitest-reporters 1.1.14
Installing rubocop 0.49.1
Bundle complete! 5 Gemfile dependencies, 15 gems now installed.
Bundled gems are installed into /usr/local/bundle.
root@48f9b6ff0907:/app# rake
/usr/local/bin/ruby -w -I"lib:test" -I"/usr/local/bundle/gems/rake-12.0.0/lib" "/usr/local/bundle/gems/rake-12.0.0/lib/rake/rake_test_loader.rb" "test/pluginator/autodetect_test.rb" "test/pluginator/extendable_autodetect_test.rb" "test/pluginator/group_test.rb" "test/pluginator/name_converter_test.rb" "test/pluginator_test.rb" "test/plugins_test/extensions/class_exist_test.rb" "test/plugins_test/extensions/conversions_test.rb" "test/plugins_test/extensions/first_ask_test.rb" "test/plugins_test/extensions/first_class_test.rb" "test/plugins_test/extensions/matching_test.rb" "test/plugins_test/extensions/plugins_map_test.rb"
/app/lib/pluginator/autodetect.rb:25:in `<module:Pluginator>': superclass mismatch for class Autodetect (TypeError)
	from /app/lib/pluginator/autodetect.rb:24:in `<top (required)>'
	from /app/test/test_helper.rb:70:in `require'
	from /app/test/test_helper.rb:70:in `block in <top (required)>'
	from /app/test/test_helper.rb:70:in `each'
	from /app/test/test_helper.rb:70:in `<top (required)>'
	from /app/test/pluginator/autodetect_test.rb:20:in `require'
	from /app/test/pluginator/autodetect_test.rb:20:in `<top (required)>'
	from /usr/local/bundle/gems/rake-12.0.0/lib/rake/rake_test_loader.rb:15:in `require'
	from /usr/local/bundle/gems/rake-12.0.0/lib/rake/rake_test_loader.rb:15:in `block in <main>'
	from /usr/local/bundle/gems/rake-12.0.0/lib/rake/rake_test_loader.rb:4:in `select'
	from /usr/local/bundle/gems/rake-12.0.0/lib/rake/rake_test_loader.rb:4:in `<main>'
rake aborted!
Command failed with status (1): [ruby -w -I"lib:test" -I"/usr/local/bundle/gems/rake-12.0.0/lib" "/usr/local/bundle/gems/rake-12.0.0/lib/rake/rake_test_loader.rb" "test/pluginator/autodetect_test.rb" "test/pluginator/extendable_autodetect_test.rb" "test/pluginator/group_test.rb" "test/pluginator/name_converter_test.rb" "test/pluginator_test.rb" "test/plugins_test/extensions/class_exist_test.rb" "test/plugins_test/extensions/conversions_test.rb" "test/plugins_test/extensions/first_ask_test.rb" "test/plugins_test/extensions/first_class_test.rb" "test/plugins_test/extensions/matching_test.rb" "test/plugins_test/extensions/plugins_map_test.rb" ]
/usr/local/bundle/gems/rake-12.0.0/exe/rake:27:in `<top (required)>'
Tasks: TOP => default => test
(See full trace by running task with --trace)

find_files_from_load_path undefined method

NoMethodError: undefined method find_files_from_load_path' for Gem:Module from vendor/bundle/ruby/2.0.0/gems/pluginator-1.4.1/lib/pluginator/autodetect/finder.rb:60:in find_load_path_plugins'

This occurs in pry when doing the following:

require 'pluginator'
Pluginator.find('puppet-debugger')

Bundler 1.15.0 incompatibility

A new version of bundler has been published a couple days ago and it causes some error.
There may be more, but this is the first that shows up.

jonatanklosko@mockingjay:~/projects/worldcubeassociation.org/WcaOnRails (master) $ bundle exec pre-commit run git
bundler: failed to load command: pre-commit (/home/jonatanklosko/.rbenv/versions/2.4.1/bin/pre-commit)
TypeError: no implicit conversion of Bundler::SpecSet into Array
  /home/jonatanklosko/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/pluginator-1.4.0/lib/pluginator/autodetect/formatted_finder.rb:65:in `find_gem_specifications'
  /home/jonatanklosko/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/pluginator-1.4.0/lib/pluginator/autodetect/formatted_finder.rb:49:in `map_gem_plugins'
  /home/jonatanklosko/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/pluginator-1.4.0/lib/pluginator/autodetect/formatted_finder.rb:33:in `initialize'
  /home/jonatanklosko/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/pluginator-1.4.0/lib/pluginator/autodetect.rb:53:in `new'
  /home/jonatanklosko/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/pluginator-1.4.0/lib/pluginator/autodetect.rb:53:in `refresh'
  /home/jonatanklosko/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/pluginator-1.4.0/lib/pluginator/autodetect.rb:44:in `initialize'
  /home/jonatanklosko/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/pluginator-1.4.0/lib/pluginator/extendable_autodetect.rb:42:in `initialize'
  /home/jonatanklosko/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/pluginator-1.4.0/lib/pluginator.rb:33:in `new'
  /home/jonatanklosko/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/pluginator-1.4.0/lib/pluginator.rb:33:in `find'
  /home/jonatanklosko/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/pre-commit-0.33.0/lib/pre-commit/plugins_list.rb:4:in `pluginator'
  /home/jonatanklosko/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/pre-commit-0.33.0/lib/pre-commit/runner.rb:17:in `initialize'
  /home/jonatanklosko/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/pre-commit-0.33.0/lib/pre-commit/cli.rb:38:in `new'
  /home/jonatanklosko/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/pre-commit-0.33.0/lib/pre-commit/cli.rb:38:in `execute_run'
  /home/jonatanklosko/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/pre-commit-0.33.0/lib/pre-commit/cli.rb:21:in `execute'
  /home/jonatanklosko/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/pre-commit-0.33.0/bin/pre-commit:10:in `<top (required)>'
  /home/jonatanklosko/.rbenv/versions/2.4.1/bin/pre-commit:22:in `load'
  /home/jonatanklosko/.rbenv/versions/2.4.1/bin/pre-commit:22:in `<top (required)>'

Pluginator finds deleted files in older versions of gems

If pluginator searches for a file that was removed, it may find it in an older version.

Scenario: gem foo has a file called bar.rb in version 2.0. In version 3.0 the file bar.rb is removed.

The user still has both versions of the gem installed 2.0 and 3.0. When scanning for files, pluginator finds many files associated with the gem at version 3.0. However, when it comes to bar.rb, it does not find the file in version 3.0, but it does find the file in version 2.0.

When we try to activate the foo gem version 2.0, an error is raised:

~/.rubies/ruby-2.0.0-p247/lib/ruby/site_ruby/2.0.0/rubygems/specification.rb:1986:in `raise_if_conflicts': can't activate foo-2.0, already activated foo-3.0 (Gem::LoadError)
    from ~/.rubies/ruby-2.0.0-p247/lib/ruby/site_ruby/2.0.0/rubygems/specification.rb:1235:in `activate'
    from ~/.gem/ruby/2.0.0/gems/pluginator-1.0.1/lib/pluginator/autodetect.rb:52:in `load_plugin'

cf: jish/pre-commit#129

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.