ddplugin 0.1

Sign up to get free protection for your applications and to get access to all the features.
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: