{"id":16068129,"url":"https://github.com/ged/pluggability","last_synced_at":"2025-04-05T10:14:44.442Z","repository":{"id":7722073,"uuid":"9087989","full_name":"ged/pluggability","owner":"ged","description":"Add pluggability to any Ruby base class","archived":false,"fork":false,"pushed_at":"2023-06-08T15:23:42.000Z","size":196,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-10T20:57:37.063Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ged.png","metadata":{"files":{"readme":"README.md","changelog":"History.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2013-03-28T22:45:29.000Z","updated_at":"2023-06-08T15:23:48.000Z","dependencies_parsed_at":"2024-10-31T02:04:37.706Z","dependency_job_id":"2b03b71d-5c36-4ecf-894a-141fa5b19069","html_url":"https://github.com/ged/pluggability","commit_stats":{"total_commits":213,"total_committers":5,"mean_commits":42.6,"dds":0.2816901408450704,"last_synced_commit":"fcd5a756627a7ae4129427186caef46629ce4b98"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ged%2Fpluggability","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ged%2Fpluggability/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ged%2Fpluggability/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ged%2Fpluggability/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ged","download_url":"https://codeload.github.com/ged/pluggability/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247318746,"owners_count":20919483,"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-10-09T06:08:40.970Z","updated_at":"2025-04-05T10:14:44.422Z","avatar_url":"https://github.com/ged.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pluggability\n\nhome\n: https://hg.sr.ht/~ged/Pluggability\n\ndocs\n: https://deveiate.org/code/pluggability\n\ncode\n: https://hg.sr.ht/~ged/Pluggability/browse\n\ngithub\n: https://github.com/ged/pluggability\n\n\n## Description\n\nPluggability is a toolkit for creating plugins.\n\nIt provides a mixin that extends your class with methods to load and instantiate its subclasses by name. So instead of:\n\n    require 'acme/adapter/png'\n    png_adapter = Acme::Adapter::PNG.new( 'file.png' )\n  \nyou can do:\n\n    require 'acme/adapter'\n    png_adapter = Acme::Adapter.create( :png, 'file.png' )\n\nA full example of where this might be useful is in a program which generates\noutput with a 'driver' object, which provides a unified interface but generates\ndifferent kinds of output.\n\nFirst the abstract base class, which is extended with Pluggability:\n\n    # in mygem/driver.rb:\n    require 'pluggability'\n    require 'mygem' unless defined?( MyGem )\n    \n    class MyGem::Driver\n        extend Pluggability\n        plugin_prefixes \"mygem/drivers\"\n    end\n\nWe can have one driver that outputs PDF documents:\n\n    # mygem/drivers/pdf.rb:\n    require 'mygem/driver' unless defined?( MyGem::Driver )\n    \n    class MyGem::Driver::PDF \u003c Driver\n        ...implementation...\n    end\n\nand another that outputs plain ascii text:\n\n    #mygem/drivers/ascii.rb:\n    require 'mygem/driver' unless defined?( MyGem::Driver )\n    \n    class MyGem::Driver::ASCII \u003c Driver\n        ...implementation...\n    end\n\nNow the driver is configurable by the end-user, who can just set\nit by its short name:\n\n    require 'mygem'\n    \n    config[:driver_type] #=\u003e \"pdf\"\n    driver = MyGem::Driver.create( config[:driver_type] )\n    driver.class #=\u003e MyGem::Driver::PDF\n\n    # You can also pass arguments to the constructor, too:\n    ascii_driver = MyGem::Driver.create( :ascii, :columns =\u003e 80 )\n\n\n### How Plugins Are Loaded\n\nThe `create` class method added to your class by Pluggability searches for your\nmodule using several different strategies. It tries various permutations of the\nbase class's name in combination with the derivative requested. For example,\nassume we want to make a `LogReader` base class, and then use plugins to define\nreaders for different log formats:\n\n    require 'pluggability'\n    \n    class LogReader\n        extend Pluggability\n\n        def read_from_file( path ); end\n    end\n\nWhen you attempt to load the 'apache' logreader class like so:\n\n    LogReader.create( 'apache' )\n\nPluggability searches for modules with the following names:\n\n    ApacheLogReader\n    apachelogreader\n    apache_log_reader\n    apache\n \nObviously the last one might load something other than what is intended, so you\ncan also tell Pluggability that plugins should be loaded from a subdirectory by\ndeclaring one or more `plugin_prefixes` in the base class. Each prefix will be\ntried (in the order they're declared) when searching for a subclass:\n\n    class LogReader\n        extend Pluggability\n        plugin_prefixes 'drivers'\n    end\n\nThis will change the list that is required to:\n\n    drivers/apache_logreader\n    drivers/apache_LogReader\n    drivers/apachelogreader\n    drivers/apacheLogReader\n    drivers/apache\n\nIf you specify more than one subdirectory, each of them will be tried in\nturn:\n\n    class LogReader\n        extend Pluggability\n        plugin_prefixes 'drivers', 'logreader'\n    end\n\nwill change the search to include:\n\n    'drivers/apachelogreader'\n    'drivers/apache_logreader'\n    'drivers/apacheLogReader'\n    'drivers/apache_LogReader'\n    'drivers/ApacheLogReader'\n    'drivers/Apache_LogReader'\n    'drivers/apache'\n    'drivers/Apache'\n    'logreader/apachelogreader'\n    'logreader/apache_logreader'\n    'logreader/apacheLogReader'\n    'logreader/apache_LogReader'\n    'logreader/ApacheLogReader'\n    'logreader/Apache_LogReader'\n    'logreader/apache'\n    'logreader/Apache'\n\nIf the plugin is not found, a Pluggability::PluginError is raised, and the\nmessage will list all the permutations that were tried.\n\n\n### Preloaded Plugins\n\nSometimes you don't want to wait for plugins to be loaded on demand. For that\ncase, Pluggability provides the Pluggability#load_all method. This will find\nall possible matches for plugin files and load them, returning an Array of all\nthe loaded classes:\n\n    class Template::Tag\n        extend Pluggability\n        plugin_prefixes 'tag'\n    end\n\n    tag_classes = Template::Tag.load_all\n\n\n### Excluding Some Files\n\nYou can also prevent some files from being automatically loaded by either\nPluggability#create or Pluggability#load_all by setting one or more exclusion\npatterns:\n\n    LogReader.plugin_exclusions 'spec/*', %r{/test/}\n\nThe patterns can either be Regexps or glob Strings.\n\n\n### Logging\n\nIf you need a little more insight into what's going on, Pluggability uses the\n[Loggability](https://rubygems.org/gems/loggability) library. Just set the log\nlevel to 'debug' and it'll explain what's going on:\n\n    require 'pluggability'\n    require 'loggability'\n    \n    class LogReader\n        extend Pluggability\n    end\n    \n    # Global level\n    Loggability.level = :debug\n\n    # Or just Pluggability's level:\n    Pluggability.logger.level = :debug\n    \n    LogReader.create( 'ringbuffer' )\n\nthis might generate a log that looks (something) like:\n\n    [...] debug {} -- Loading derivative ringbuffer\n    [...] debug {} -- Subdirs are: [\"\"]\n    [...] debug {} -- Path is: [\"ringbuffer_logreader\", \"ringbuffer_LogReader\",\n      \"ringbufferlogreader\", \"ringbufferLogReader\", \"ringbuffer\"]...\n    [...] debug {} -- Trying ringbuffer_logreader...\n    [...] debug {} -- No module at 'ringbuffer_logreader', trying the next alternative:\n      'cannot load such file -- ringbuffer_logreader'\n    [...] debug {} -- Trying ringbuffer_LogReader...\n    [...] debug {} -- No module at 'ringbuffer_LogReader', trying the next alternative:\n      'cannot load such file -- ringbuffer_LogReader'\n    [...] debug {} -- Trying ringbufferlogreader...\n    [...] debug {} -- No module at 'ringbufferlogreader', trying the next alternative:\n      'cannot load such file -- ringbufferlogreader'\n    [...] debug {} -- Trying ringbufferLogReader...\n    [...] debug {} -- No module at 'ringbufferLogReader', trying the next alternative:\n      'cannot load such file -- ringbufferLogReader'\n    [...] debug {} -- Trying ringbuffer...\n    [...] debug {} -- No module at 'ringbuffer', trying the next alternative:\n      'cannot load such file -- ringbuffer'\n    [...] debug {} -- fatals = []\n    [...] error {} -- Couldn't find a LogReader named 'ringbuffer': tried \n      [\"ringbuffer_logreader\", \"ringbuffer_LogReader\", \n      \"ringbufferlogreader\", \"ringbufferLogReader\", \"ringbuffer\"]\n\n\n## Installation\n\n    gem install pluggability\n\n\n## Contributing\n\nYou can check out the current development source with Mercurial via its\n[Mercurial repo](https://bitbucket.org/ged/pluggability). Or if you prefer Git,\nvia [its Github mirror](https://github.com/ged/pluggability).\n\nAfter checking out the source, run:\n\n    $ rake newb\n\nThis task will install any missing dependencies, run the tests/specs,\nand generate the API documentation.\n\n\n## Authors\n\n- Michael Granger \u003cged@faeriemud.org\u003e\n- Martin Chase \u003coutofculture@gmail.com\u003e\n\n\n## License\n\nCopyright (c) 2008-2020, Michael Granger and Martin Chase\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice,\n  this list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n  this list of conditions and the following disclaimer in the documentation\n  and/or other materials provided with the distribution.\n\n* Neither the name of the author/s, nor the names of the project's\n  contributors may be used to endorse or promote products derived from this\n  software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fged%2Fpluggability","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fged%2Fpluggability","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fged%2Fpluggability/lists"}