ddplugin 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0de5a9e0217483b21eacdc6668deb299a6dd3e06
4
+ data.tar.gz: 2e1afa6aed108a66552d3b2e65ecde95cddd838f
5
+ SHA512:
6
+ metadata.gz: 68d16f23635d03879c49c1322c006a023157292ea6f1844bae05c2bba59f7d6c3e315a54799668c5ba43b98f3ec087f51d57cd27c3f5f366a973a6ad24078626
7
+ data.tar.gz: fa3a128aac7969bced4dbaf7248871d604fb2e15b97c205a014be6c3d0dc61e62a53026c41c6467ad05fa4a2216de0fc2f3dd1fb988f2c642ce0d82631745295
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'minitest'
6
+ gem 'rake'
7
+ gem 'coveralls', require: false
data/Gemfile.lock ADDED
@@ -0,0 +1,40 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ ddplugin (0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ coveralls (0.7.0)
10
+ multi_json (~> 1.3)
11
+ rest-client
12
+ simplecov (>= 0.7)
13
+ term-ansicolor
14
+ thor
15
+ docile (1.1.1)
16
+ mime-types (2.0)
17
+ minitest (5.0.8)
18
+ multi_json (1.8.2)
19
+ rake (10.1.0)
20
+ rest-client (1.6.7)
21
+ mime-types (>= 1.16)
22
+ simplecov (0.8.2)
23
+ docile (~> 1.1.0)
24
+ multi_json
25
+ simplecov-html (~> 0.8.0)
26
+ simplecov-html (0.8.0)
27
+ term-ansicolor (1.2.2)
28
+ tins (~> 0.8)
29
+ thor (0.18.1)
30
+ tins (0.13.1)
31
+
32
+ PLATFORMS
33
+ ruby
34
+
35
+ DEPENDENCIES
36
+ bundler
37
+ coveralls
38
+ ddplugin!
39
+ minitest
40
+ rake
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2013 Denis Defreyne
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/NEWS.md ADDED
File without changes
data/README.md ADDED
@@ -0,0 +1,137 @@
1
+ [![Build Status](https://travis-ci.org/ddfreyne/ddplugin.png)](https://travis-ci.org/ddfreyne/ddplugin)
2
+ [![Code Climate](https://codeclimate.com/github/ddfreyne/ddplugin.png)](https://codeclimate.com/github/ddfreyne/ddplugin)
3
+ [![Coverage Status](https://coveralls.io/repos/ddfreyne/ddplugin/badge.png?branch=master)](https://coveralls.io/r/ddfreyne/ddplugin)
4
+
5
+ # ddplugin
6
+
7
+ *ddplugin* is a library for managing plugins.
8
+
9
+ Designing a library so that third parties can easily extend it greatly improves its usefulness. *ddplugin* helps solve this problem using *plugins*, which are classes of a certain type and with a given identifier (Ruby symbol).
10
+
11
+ This code was extracted from nanoc, where it has been in production for years.
12
+
13
+ ## Use case
14
+
15
+ Many projects can make use of plugins. Here are a few examples:
16
+
17
+ * a **text processing library** with *filters* such as `colorize-syntax`, `markdown` and `smartify-quotes`.
18
+
19
+ * an **image processing library** with *filters* such as `resize`, `desaturate` and `rotate`.
20
+
21
+ * a **database driver abstraction** with *connectors* such as `postgres`, `sqlite3` and `mysql`.
22
+
23
+ * a **document management system** with *data sources* such as `filesystem` and `database`.
24
+
25
+ In *ddplugin*, the filters, connectors and data sources would be *plugin types*, while the actual plugins, such as `markdown`, `rotate`, `postgres` and `database` would be *plugins*.
26
+
27
+ A typical way to use plugins would be to store the plugin names in a configuration file, so that the actual plugin implementations can be discovered at runtime.
28
+
29
+ ## Dependencies
30
+
31
+ *ddplugin* requires Ruby 1.9.3 or higher.
32
+
33
+ ## Versioning
34
+
35
+ *ddplugin* adheres to [Semantic Versioning 2.0.0](http://semver.org).
36
+
37
+ ## Installation
38
+
39
+ **NOTE:** *ddplugin* has not been released as a gem yet, so these installation instructions are not usable yet.
40
+
41
+ If your library where you want to use *ddplugin* has a gemspec, add *ddplugin* as a runtime dependency to the gemspec:
42
+
43
+ ```ruby
44
+ spec.add_runtime_dependency 'ddplugin' '~> 1.0'
45
+ ```
46
+
47
+ If you use Bundler instead, add it to the `Gemfile`:
48
+
49
+ ```ruby
50
+ gem 'ddplugin', '~> 1.0'
51
+ ```
52
+
53
+ ## Usage
54
+
55
+ Plugin type are classes that extend `DDPlugin::Plugin`:
56
+
57
+ ```ruby
58
+ class Filter
59
+ extend DDPlugin::Plugin
60
+ end
61
+
62
+ class DataSource
63
+ extend DDPlugin::Plugin
64
+ end
65
+ ```
66
+
67
+ To define a plugin, create a class that inherits from the plugin type and sets the identifier:
68
+
69
+ ```ruby
70
+ class ERBFilter < Filter
71
+ identifier :erb
72
+ end
73
+
74
+ class HamlFilter < Filter
75
+ identifier :haml
76
+ end
77
+
78
+ class FilesystemDataSource < DataSource
79
+ identifier :filesystem
80
+ end
81
+
82
+ class PostgresDataSource < DataSource
83
+ identifier :postgres
84
+ end
85
+ ```
86
+
87
+ To find a plugin of a given type and with a given identifier, call `.named` on the plugin type, passing an identifier:
88
+
89
+ ```ruby
90
+ Filter.named(:erb)
91
+ # => ERBFilter
92
+
93
+ Filter.named(:haml)
94
+ # => HamlFilter
95
+
96
+ DataSource.named(:filesystem)
97
+ # => FilesystemDataSource
98
+
99
+ DataSource.named(:postgres)
100
+ # => PostgresDataSource
101
+ ```
102
+
103
+ To get all plugins of a given type, call `.all` on the plugin type:
104
+
105
+ ```ruby
106
+ Filter.all
107
+ # => [ ERBFilter, HamlFilter ]
108
+
109
+ DataSource.all
110
+ # => [ FilesystemDataSource, PostgresDataSource ]
111
+ ```
112
+
113
+ To get the identifier of a plugin, call `.identifier`:
114
+
115
+ ```ruby
116
+ Filter.named(:erb).identifier
117
+ # => :erb
118
+
119
+ PostgresDataSource.identifier
120
+ # => :postgres
121
+ ```
122
+
123
+ ## Development
124
+
125
+ Pull requests and issues are greatly appreciated.
126
+
127
+ When you submit a pull request, make sure that your change is covered by tests, and that the `README` and [YARD](http://yardoc.org/) source code documentation are still up-to-date.
128
+
129
+ To run the tests, execute `rake`:
130
+
131
+ ```
132
+ % rake
133
+ ```
134
+
135
+ ## Contact
136
+
137
+ *ddplugin* is written by Denis Defreyne <denis.defreyne@stoneship.org>
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs = %w( lib test )
7
+ t.test_files = FileList['test/**/test_*.rb', 'test/**/*_spec.rb']
8
+ end
9
+
10
+ task :default => :test
data/ddplugin.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+
3
+ $LOAD_PATH.unshift(File.expand_path('../lib/', __FILE__))
4
+ require 'ddplugin/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = 'ddplugin'
8
+ s.version = DDPlugin::VERSION
9
+ s.homepage = 'http://github.com/ddfreyne/ddplugin/'
10
+ s.summary = 'Plugins for Ruby apps'
11
+ s.description = 'Provides plugin management for Ruby projects'
12
+
13
+ s.author = 'Denis Defreyne'
14
+ s.email = 'denis.defreyne@stoneship.org'
15
+ s.license = 'MIT'
16
+
17
+ s.required_ruby_version = '>= 1.9.3'
18
+
19
+ s.files = Dir['[A-Z]*'] +
20
+ Dir['{lib,test}/**/*'] +
21
+ [ 'ddplugin.gemspec' ]
22
+ s.require_paths = [ 'lib' ]
23
+
24
+ s.rdoc_options = [ '--main', 'README.md' ]
25
+ s.extra_rdoc_files = [ 'LICENSE', 'README.md', 'NEWS.md' ]
26
+
27
+ s.add_development_dependency('bundler')
28
+ end
data/lib/ddplugin.rb ADDED
@@ -0,0 +1,5 @@
1
+ # encoding: utf-8
2
+
3
+ require 'ddplugin/version'
4
+ require 'ddplugin/plugin'
5
+ require 'ddplugin/registry'
@@ -0,0 +1,70 @@
1
+ # encoding: utf-8
2
+
3
+ module DDPlugin
4
+
5
+ # A module that contains class methods for plugins. It provides functions
6
+ # for setting identifiers and finding plugins. Plugin classes should extend
7
+ # this module.
8
+ module Plugin
9
+
10
+ # @overload identifiers(*identifiers)
11
+ #
12
+ # Sets the identifiers for this class.
13
+ #
14
+ # @param [Array<Symbol>] identifiers A list of identifiers to assign to
15
+ # this class.
16
+ #
17
+ # @return [void]
18
+ #
19
+ # @overload identifiers
20
+ #
21
+ # @return [Array<Symbol>] The identifiers for this class
22
+ def identifiers(*identifiers)
23
+ if identifiers.empty?
24
+ DDPlugin::Registry.instance.identifiers_of(root_class, self)
25
+ else
26
+ DDPlugin::Registry.instance.register(root_class, self, *identifiers)
27
+ end
28
+ end
29
+
30
+ # @return [Class] The root class for this class
31
+ def root_class
32
+ klass = self
33
+ klass = klass.superclass while klass.superclass.respond_to?(:identifiers)
34
+ klass
35
+ end
36
+
37
+ # @overload identifier(identifier)
38
+ #
39
+ # Sets the identifier for this class.
40
+ #
41
+ # @param [Symbol] identifier The identifier to assign to this class.
42
+ #
43
+ # @return [void]
44
+ #
45
+ # @overload identifier
46
+ #
47
+ # @return [Symbol] The first identifier for this class
48
+ def identifier(identifier=nil)
49
+ if identifier
50
+ self.identifiers(identifier)
51
+ else
52
+ self.identifiers.first
53
+ end
54
+ end
55
+
56
+ # @return [Enumerable<Class>] All classes of this type
57
+ def all
58
+ DDPlugin::Registry.instance.find_all(self)
59
+ end
60
+
61
+ # @param [Symbol] identifier The identifier of the class to find
62
+ #
63
+ # @return [Class] The class with the given identifier
64
+ def named(identifier)
65
+ DDPlugin::Registry.instance.find(self, identifier)
66
+ end
67
+
68
+ end
69
+
70
+ end
@@ -0,0 +1,76 @@
1
+ # encoding: utf-8
2
+
3
+ module DDPlugin
4
+
5
+ # The registry is responsible for keeping track of all loaded plugins.
6
+ class Registry
7
+
8
+ # Returns the shared {DDPlugin::Registry} instance, creating it if none
9
+ # exists yet.
10
+ #
11
+ # @return [DDPlugin::Registry] The shared plugin registry
12
+ def self.instance
13
+ @instance ||= self.new
14
+ end
15
+
16
+ # @api private
17
+ def initialize
18
+ @identifiers_to_classes = Hash.new { |h,k| h[k] = {}.dup }
19
+ @classes_to_identifiers = Hash.new { |h,k| h[k] = {}.dup }
20
+ end
21
+
22
+ # Registers the given class as a plugin.
23
+ #
24
+ # @param [Class] root_class The root class of the class to register
25
+ #
26
+ # @param [Class] klass The class to register
27
+ #
28
+ # @param [Symbol] identifiers One or more symbols identifying the class
29
+ #
30
+ # @return [void]
31
+ def register(root_class, klass, *identifiers)
32
+ identifiers.each do |identifier|
33
+ @classes_to_identifiers[root_class][klass] ||= []
34
+
35
+ @identifiers_to_classes[root_class][identifier] = klass
36
+ @classes_to_identifiers[root_class][klass] << identifier
37
+ end
38
+ end
39
+
40
+ # @param [Class] root_class The root class of the class to find the
41
+ # identifiers for
42
+ #
43
+ # @param [Class] klass The class to get the identifiers for
44
+ #
45
+ # @return [Array<Symbol>] The identifiers for the given class
46
+ def identifiers_of(root_class, klass)
47
+ @classes_to_identifiers[root_class] ||= {}
48
+ @classes_to_identifiers[root_class].fetch(klass, [])
49
+ end
50
+
51
+ # Finds the class that is a descendant of the given class and has the given
52
+ # identifier.
53
+ #
54
+ # @param [Class] root_class The root class of the class to return
55
+ #
56
+ # @param [Symbol] identifier The identifier of the class to return
57
+ #
58
+ # @return [Class, nil] The class with the given identifier
59
+ def find(root_class, identifier)
60
+ @identifiers_to_classes[root_class] ||= {}
61
+ @identifiers_to_classes[root_class][identifier]
62
+ end
63
+
64
+ # Returns all classes that are registered descendants of the given class.
65
+ #
66
+ # @param [Class] root_class The root class of the class to return
67
+ #
68
+ # @return [Enumerable<Class>] A collection of registered classes
69
+ def find_all(root_class)
70
+ @identifiers_to_classes[root_class] ||= {}
71
+ @identifiers_to_classes[root_class].values
72
+ end
73
+
74
+ end
75
+
76
+ end
@@ -0,0 +1,7 @@
1
+ # encoding: utf-8
2
+
3
+ module DDPlugin
4
+
5
+ VERSION = '0.1'
6
+
7
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,9 @@
1
+ # encoding: utf-8
2
+
3
+ require 'coveralls'
4
+ Coveralls.wear!
5
+
6
+ require 'minitest'
7
+ require 'minitest/autorun'
8
+
9
+ require 'ddplugin'
@@ -0,0 +1,80 @@
1
+ # encoding: utf-8
2
+
3
+ require 'helper'
4
+
5
+ class DDPlugin::PluginTest < Minitest::Test
6
+
7
+ class IdentifierSample
8
+ extend DDPlugin::Plugin
9
+ end
10
+
11
+ class NamedSample
12
+ extend DDPlugin::Plugin
13
+ end
14
+
15
+ class AllSample
16
+ extend DDPlugin::Plugin
17
+ end
18
+
19
+ class InheritanceSample
20
+ extend DDPlugin::Plugin
21
+ end
22
+
23
+ def test_identifier
24
+ klass = Class.new(IdentifierSample)
25
+ assert_nil klass.identifier
26
+
27
+ klass.identifier :foo
28
+ assert_equal :foo, klass.identifier
29
+
30
+ klass.identifier :bar
31
+ assert_equal :foo, klass.identifier
32
+ end
33
+
34
+ def test_identifiers
35
+ klass = Class.new(IdentifierSample)
36
+ assert_empty klass.identifiers
37
+
38
+ klass.identifiers :foo1, :foo2
39
+ assert_equal [ :foo1, :foo2 ], klass.identifiers
40
+
41
+ klass.identifiers :bar1, :bar2
42
+ assert_equal [ :foo1, :foo2, :bar1, :bar2 ], klass.identifiers
43
+ end
44
+
45
+ def test_root
46
+ superklass = Class.new(InheritanceSample)
47
+ superklass.identifier :super
48
+
49
+ subklass = Class.new(superklass)
50
+ subklass.identifiers :sub, :also_sub
51
+
52
+ assert_equal superklass, InheritanceSample.named(:super)
53
+ assert_equal subklass, InheritanceSample.named(:sub)
54
+
55
+ assert_equal :sub, subklass.identifier
56
+ assert_equal [ :sub, :also_sub ], subklass.identifiers
57
+
58
+ assert_equal InheritanceSample, superklass.root_class
59
+ assert_equal InheritanceSample, subklass.root_class
60
+ end
61
+
62
+ def test_named
63
+ klass = Class.new(NamedSample)
64
+ klass.identifier :named_test
65
+
66
+ assert_nil NamedSample.named(:unknown)
67
+ assert_equal klass, NamedSample.named(:named_test)
68
+ end
69
+
70
+ def test_all
71
+ klass1 = Class.new(AllSample)
72
+ klass1.identifier :one
73
+
74
+ klass2 = Class.new(AllSample)
75
+ klass2.identifier :two
76
+
77
+ assert_equal [ klass1, klass2 ], AllSample.all
78
+ end
79
+
80
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ddplugin
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Denis Defreyne
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-12-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: Provides plugin management for Ruby projects
28
+ email: denis.defreyne@stoneship.org
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files:
32
+ - LICENSE
33
+ - README.md
34
+ - NEWS.md
35
+ files:
36
+ - Gemfile
37
+ - Gemfile.lock
38
+ - LICENSE
39
+ - NEWS.md
40
+ - Rakefile
41
+ - README.md
42
+ - lib/ddplugin/plugin.rb
43
+ - lib/ddplugin/registry.rb
44
+ - lib/ddplugin/version.rb
45
+ - lib/ddplugin.rb
46
+ - test/helper.rb
47
+ - test/test_plugin.rb
48
+ - ddplugin.gemspec
49
+ homepage: http://github.com/ddfreyne/ddplugin/
50
+ licenses:
51
+ - MIT
52
+ metadata: {}
53
+ post_install_message:
54
+ rdoc_options:
55
+ - "--main"
56
+ - README.md
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: 1.9.3
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ requirements: []
70
+ rubyforge_project:
71
+ rubygems_version: 2.0.3
72
+ signing_key:
73
+ specification_version: 4
74
+ summary: Plugins for Ruby apps
75
+ test_files: []
76
+ has_rdoc: