{"id":19816902,"url":"https://github.com/rvm/pluginator","last_synced_at":"2025-07-31T03:35:01.321Z","repository":{"id":6024783,"uuid":"7248703","full_name":"rvm/pluginator","owner":"rvm","description":"A simple plugin system based on Gem.find_files","archived":false,"fork":false,"pushed_at":"2017-06-25T17:50:51.000Z","size":167,"stargazers_count":34,"open_issues_count":4,"forks_count":8,"subscribers_count":18,"default_branch":"master","last_synced_at":"2025-04-26T23:09:52.780Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rvm.png","metadata":{"files":{"readme":"README.md","changelog":"Changelog.md","contributing":null,"funding":null,"license":"COPYING","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2012-12-19T22:45:51.000Z","updated_at":"2024-12-18T02:34:19.000Z","dependencies_parsed_at":"2022-08-21T00:50:52.859Z","dependency_job_id":null,"html_url":"https://github.com/rvm/pluginator","commit_stats":null,"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rvm%2Fpluginator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rvm%2Fpluginator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rvm%2Fpluginator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rvm%2Fpluginator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rvm","download_url":"https://codeload.github.com/rvm/pluginator/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251860557,"owners_count":21655768,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-12T10:11:00.799Z","updated_at":"2025-05-01T10:33:17.408Z","avatar_url":"https://github.com/rvm.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Pluginator\n\n[![Gem Version](https://badge.fury.io/rb/pluginator.png)](http://rubygems.org/gems/pluginator)\n[![Code Climate](https://codeclimate.com/github/rvm/pluginator.png)](https://codeclimate.com/github/rvm/pluginator)\n[![Coverage Status](https://coveralls.io/repos/rvm/pluginator/badge.png)](https://coveralls.io/r/rvm/pluginator)\n[![Build Status](https://travis-ci.org/rvm/pluginator.png)](https://travis-ci.org/rvm/pluginator)\n[![Dependency Status](https://gemnasium.com/rvm/pluginator.png)](https://gemnasium.com/rvm/pluginator)\n[![Inline docs](http://inch-ci.org/github/rvm/pluginator.png)](http://inch-ci.org/github/rvm/pluginator)\n[![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://rubydoc.info/github/rvm/pluginator/master/frames)\n[![Github Code](http://img.shields.io/badge/github-code-blue.svg)](https://github.com/rvm/pluginator)\n\nGem plugin system management, detects plugins using `Gem.find_file`,\n`$LOAD_PATH` and `$LOADED_FEATURES`.\n\nPluginator works with *ruby 1.9.3+* and *rubygems 2.0.0+*.\n\nPluginator tries to stay out of your way, you do not have to include or inherit anything.\nPluginator only finds and groups plugins, rest is up to you,\nyou decide what methods to define and how to find them.\n\n## Defining plugins\n\ncreate a gem with a path:\n\n```ruby\nlib/plugins/\u003cgroup\u003e/\u003ctype\u003e/\u003cname\u003e.rb\n```\n\nwith a class inside:\n\n```ruby\n\u003cgroup\u003e::\u003ctype\u003e::\u003cname\u003e\n```\n\nwhere `\u003ctype\u003e` can be nested\n\n## Loading plugins\n\n```ruby\nrvm2plugins = Pluginator.find(\"\u003cgroup\u003e\")\ntype_plugins = rvm2plugins[\"\u003ctype\u003e\"]\ntypes = rvm2plugins.types\n```\n\n## Usage\n\n```ruby\nPluginator.find(\"\u003cgroup\u003e\") =\u003e Pluginator object\nplugins = Pluginator.find(\"\u003cgroup\u003e\", type: \"\u003ctype\u003e\", prefix: \"/(lib|local_lib)\", extends: %i[\u003cextensions\u003e])\nplugins[\"\u003ctype\u003e\"] =\u003e Array of plugins\nplugins.type      =\u003e Array of plugins for type defined with `type: \"\u003ctype\u003e\"`\nplugins.types     =\u003e Array of types\n```\n\n- `\"\u003cgroup\u003e\"` - Load plugins for given group.\n- `type: \"\u003ctype\u003e\"` - Load plugins only of given type, optional, makes `type` method accessible.\n- `prefix: \"/prefix\"` - Load plugins only from this paths, optional, default `/lib`.\n- `extends: %i[\u003cextensions\u003e]` - Extend pluginator with given extensions.\n\n## Extensions\n\nPluginator comes with few handful extensions.\n\n### Class exist\n\nCheck if plugin with given class name exists.\n\n```ruby\nplugins = Pluginator.find(\"\u003cgroup\u003e\", extends: %i[class_exist]})\nplugins.class_exist?(\"\u003ctype\u003e\", \"\u003cname\u003e\") =\u003e true or false\n```\n\n### First ask\n\nCall a method on plugin and return first one that returns `true`.\n\n```ruby\nplugins = Pluginator.find(\"\u003cgroup\u003e\", extends: %i[first_ask])\nplugins.first_ask( \"\u003ctype\u003e\", \"method_to_call\", *params) =\u003e plugin or nil\nplugins.first_ask!(\"\u003ctype\u003e\", \"method_to_call\", *params) =\u003e plugin or exception PluginatorError\n```\n\n### First class\n\nFind first plugin that class matches the given name.\n\n```ruby\nplugins = Pluginator.find(\"\u003cgroup\u003e\", extends: %i[first_class])\nplugins.first_class( \"\u003ctype\u003e\", \"\u003cname\u003e\") =\u003e plugin or nil\nplugins.first_class!(\"\u003ctype\u003e\", \"\u003cname\u003e\") =\u003e plugin or exception PluginatorError\n```\n\n### Matching\n\nMap array of names to available plugins.\n\n```ruby\nplugins = Pluginator.find(\"\u003cgroup\u003e\", extends: %i[matching])\nplugins.matching( \"\u003ctype\u003e\", [\u003carray_of_names\u003e]) =\u003e [plugins] # nil for missing ones\nplugins.matching!(\"\u003ctype\u003e\", [\u003carray_of_names\u003e]) =\u003e [plugins] or exception PluginatorError\n```\n\n### Your own ones\n\nYou can define your own extensions for `pluginator`, for example:\n\n```shell\nplugins/pluginator/extensions/first_one.rb\n```\n\nwith:\n\n```ruby\nmodule Pluginator::Extensions\n  class FirstOne\n    def first_one(type)\n      @plugins[type].first\n    end\n  end\nend\n```\n\nAnd now you can use it:\n\n```ruby\nplugins = Pluginator.find(\"\u003cgroup\u003e\", extends: %i[first_one])\nplugins.first_one(\"\u003ctype\u003e\") =\u003e first_plugin # nil when none\n```\n\n\n## Exceptions\n\n- `PluginatorError` - base error for all Pluginator errors\n- `MissingPlugin`   - raised when plugin can not be found, generated by `*!` methods\n- `MissingType`     - raised when type   can not be found, generated by `*!` methods\n\n## Versioning plugins\n\nIn case plugin gets moved to other gem you can specify which gem to\nuse for loading the plugin by specifying plugin version in gems gemspec\nmetadata:\n\n```ruby\ns.metadata = {\n  \"plugins/v2test/stats/max.rb\" =\u003e \"1\"\n}\n```\n\n## Examples\n\n### Example 1 - task plugins\n\n`plugins/rvm2/cli/echo.rb`:\n\n```ruby\nclass Rvm2::Cli::Echo\n  def self.question? command\n    command == \"echo\"\n  end\n  def answer param\n    puts param\n  end\nend\n```\n\nwhere `question?` and `answer` are user defined methods\n\nNow the plugin can be used:\n\n```ruby\nrequire \"pluginator\"\n\nrvm2plugins = Pluginator.find(\"rvm2\")\nplugin = rvm2plugins[\"cli\"].first{ |plugin|\n  plugin.question?(\"echo\")\n}\nplugin.new.answer(\"Hello world\")\n```\n\nOr using extensions:\n\n```ruby\nrequire \"pluginator\"\n\nplugin = Pluginator.find(\"rvm2\", extends: %i[first_ask]).first_ask(\"cli\", \u0026:question?, \"echo\")\nplugin.new.answer(\"Hello world\")\n```\n\n### Example 2 - hook plugins\n\n`plugins/rvm2/hooks/after_install/show.rb`:\n\n```ruby\nclass Rvm2::Hooks::AfterInstall::Show\n  def self.execute name, path\n    puts \"Ruby #{name.inspect} was installed in #{path.inspect}.\"\n  end\nend\n```\n\nand using hooks:\n\n```ruby\nrequire \"pluginator\"\n\nPluginator.find(\"rvm2\", type: \"hooks/after_install\").type.each{ |plugin|\n  plugin.execute(name, path)\n}\n```\n\n## Testing\n\n```bash\nNOEXEC_DISABLE=1 rake test\n```\n\n## License\n\nCopyright 2013-2017 Michal Papis \u003cmpapis@gmail.com\u003e\n\npluginator is free software: you can redistribute it and/or modify\nit under the terms of the GNU Lesser General Public License as published\nby the Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\npluginator is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU Lesser General Public License for more details.\n\nYou should have received a copy of the GNU Lesser General Public License\nalong with pluginator.  If not, see \u003chttp://www.gnu.org/licenses/\u003e.\n\nFor details on adding copyright visit:\nhttps://www.gnu.org/licenses/gpl-howto.html\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frvm%2Fpluginator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frvm%2Fpluginator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frvm%2Fpluginator/lists"}