plugins 0.2.4
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 +7 -0
- data/lib/ribbon/plugins/around_stack.rb +105 -0
- data/lib/ribbon/plugins/block_stack.rb +30 -0
- data/lib/ribbon/plugins/component_mixin.rb +47 -0
- data/lib/ribbon/plugins/errors.rb +6 -0
- data/lib/ribbon/plugins/plugin.rb +62 -0
- data/lib/ribbon/plugins/version.rb +5 -0
- data/lib/ribbon/plugins.rb +97 -0
- metadata +71 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 95e5717b6ed8f94cb9b10852dd0ffe859694411c
|
4
|
+
data.tar.gz: 38b115995d982f05f361b66724af2b7b24e4f514
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 96d3f53560dcb1f37190b5ac7bd093de98b164a7238ea09cdd5cc5eae98bcb3d53aff42d72aa9c4f1f83b35452ae68d1fc8ac604797f40f9b090d5e02e064708
|
7
|
+
data.tar.gz: 14322d806fa42ab7f954b5393eeffc5a6a5fcfaddf3fd7bb256b326051a1d001e926ea149a2b17c49ecea617feea711ba351f5b5c6889c43e74ad798c0096254
|
@@ -0,0 +1,105 @@
|
|
1
|
+
class Ribbon::Plugins
|
2
|
+
class AroundStack
|
3
|
+
attr_reader :subject
|
4
|
+
attr_accessor :scope
|
5
|
+
|
6
|
+
def initialize(subject, scope=nil)
|
7
|
+
@subject = subject.to_sym
|
8
|
+
@scope = scope
|
9
|
+
@_stack = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def push(&block)
|
13
|
+
raise Errors::Error, "Must pass block" unless block_given?
|
14
|
+
|
15
|
+
AroundWrapper.new(self, subject, &block).tap { |wrapper|
|
16
|
+
@_stack.push(wrapper)
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def dup
|
21
|
+
AroundStack.new(subject, scope).tap { |stack|
|
22
|
+
@_stack.each { |wrapper|
|
23
|
+
stack.push(&wrapper.block)
|
24
|
+
}
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def call(*args, &block)
|
29
|
+
raise Errors::Error, "Must pass block" unless block_given?
|
30
|
+
call_stack = @_stack.dup
|
31
|
+
|
32
|
+
inner_most = WrappedBlock.new(&block)
|
33
|
+
call_stack.unshift(inner_most)
|
34
|
+
|
35
|
+
outer_most = call_stack.pop
|
36
|
+
outer_most.call(call_stack, *args)
|
37
|
+
|
38
|
+
# This shouldn't happen unless the AroundStack isn't functioning properly.
|
39
|
+
raise Errors::Error, "Block passed was not called!" unless inner_most.called?
|
40
|
+
|
41
|
+
inner_most.retval
|
42
|
+
end
|
43
|
+
|
44
|
+
class AroundWrapper
|
45
|
+
attr_reader :stack, :subject, :block
|
46
|
+
|
47
|
+
def initialize(stack, subject, &block)
|
48
|
+
@stack = stack
|
49
|
+
@subject = subject
|
50
|
+
@block = block
|
51
|
+
end
|
52
|
+
|
53
|
+
def scope
|
54
|
+
stack.scope
|
55
|
+
end
|
56
|
+
|
57
|
+
def method_missing(meth, *args, &block)
|
58
|
+
super unless scope
|
59
|
+
scope.send(meth, *args, &block)
|
60
|
+
end
|
61
|
+
|
62
|
+
def call(call_stack, *args)
|
63
|
+
wrapped = call_stack.pop
|
64
|
+
raise Errors::Error, 'call stack too short' unless wrapped
|
65
|
+
|
66
|
+
define_singleton_method("perform_#{subject}") { |*new_args|
|
67
|
+
args = new_args unless new_args.empty?
|
68
|
+
wrapped.call(call_stack, *args)
|
69
|
+
}
|
70
|
+
|
71
|
+
singleton_class.instance_exec(subject) { |subject|
|
72
|
+
alias_method subject, "perform_#{subject}"
|
73
|
+
|
74
|
+
# Don't allow these to be overriden
|
75
|
+
attr_reader :stack, :subject, :block
|
76
|
+
}
|
77
|
+
|
78
|
+
instance_exec(*args, &block)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class WrappedBlock
|
83
|
+
attr_reader :block
|
84
|
+
attr_reader :retval
|
85
|
+
|
86
|
+
def initialize(&block)
|
87
|
+
@block = block
|
88
|
+
end
|
89
|
+
|
90
|
+
def called?
|
91
|
+
!!@_called
|
92
|
+
end
|
93
|
+
|
94
|
+
##
|
95
|
+
# Call the wrapped block, ignoring the scope and call_stack arguments.
|
96
|
+
def call(call_stack, *args)
|
97
|
+
raise Errors::Error, 'receiving non-empty call stack' unless call_stack.empty?
|
98
|
+
block.call(*args).tap { |retval|
|
99
|
+
@retval = retval
|
100
|
+
@_called = true
|
101
|
+
}
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end # AroundStack
|
105
|
+
end # Ribbon::Plugins
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class Ribbon::Plugins
|
2
|
+
class BlockStack
|
3
|
+
attr_accessor :scope
|
4
|
+
|
5
|
+
def initialize(scope=nil)
|
6
|
+
@scope = scope
|
7
|
+
@_stack = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def dup
|
11
|
+
BlockStack.new.tap { |stack|
|
12
|
+
@_stack.each { |block| stack.push(&block) }
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
def push(&block)
|
17
|
+
@_stack.push(block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def call(*args)
|
21
|
+
@_stack.reverse_each { |block|
|
22
|
+
if scope
|
23
|
+
scope.instance_exec(*args, &block)
|
24
|
+
else
|
25
|
+
block.call(*args)
|
26
|
+
end
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Ribbon
|
2
|
+
class Plugins
|
3
|
+
##
|
4
|
+
# Intended to be mixed into any class utilizing the plugins functionality.
|
5
|
+
module ComponentMixin
|
6
|
+
def self.included(base)
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
##
|
12
|
+
# Get or define the plugin loader.
|
13
|
+
#
|
14
|
+
# This block will be used to load a plugin given the value passed to the
|
15
|
+
# +plugin+ instance method. It's the responsibility of this block to
|
16
|
+
# translate the inputted value into either a Class that extends Plugin
|
17
|
+
# or a Proc.
|
18
|
+
#
|
19
|
+
# If for a particular value you wish to not perform any translation,
|
20
|
+
# return falsey.
|
21
|
+
def plugin_loader(&block)
|
22
|
+
if block_given?
|
23
|
+
@_plugin_loader = block
|
24
|
+
else
|
25
|
+
@_plugin_loader
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
###
|
31
|
+
# Instance Methods
|
32
|
+
###
|
33
|
+
|
34
|
+
##
|
35
|
+
# Reference to the Plugins instance for the component.
|
36
|
+
def plugins
|
37
|
+
@plugins ||= Plugins.new(self, &self.class.plugin_loader)
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# Add a plugin.
|
42
|
+
def plugin(*args, &block)
|
43
|
+
plugins.add(*args, &block)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
class Ribbon::Plugins
|
2
|
+
class Plugin
|
3
|
+
class << self
|
4
|
+
def create(&block)
|
5
|
+
Class.new(Plugin).tap { |k| k.class_eval(&block) if block }
|
6
|
+
end
|
7
|
+
|
8
|
+
def method_missing(meth, *args, &block)
|
9
|
+
if /^(before|after|around)_(\w+)$/.match(meth.to_s)
|
10
|
+
_define_callback($1, $2, &block)
|
11
|
+
else
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def _define_callback(type, subject, &block)
|
17
|
+
_callbacks(type, subject).push(&block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def _callbacks(type, subject)
|
21
|
+
((@__callbacks ||= {})[type.to_sym] ||= {})[subject.to_sym] ||= _empty_stack_for(type, subject)
|
22
|
+
end
|
23
|
+
|
24
|
+
def _empty_stack_for(type, subject)
|
25
|
+
case type.to_sym
|
26
|
+
when :before, :after
|
27
|
+
BlockStack.new
|
28
|
+
when :around
|
29
|
+
AroundStack.new(subject)
|
30
|
+
else
|
31
|
+
raise Errors::Error, "Invalid type: #{type}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
attr_reader :plugins
|
37
|
+
|
38
|
+
def initialize(plugins=nil)
|
39
|
+
@plugins = plugins
|
40
|
+
end
|
41
|
+
|
42
|
+
def before(subject, *args)
|
43
|
+
_callbacks(:before, subject).call(*args)
|
44
|
+
end
|
45
|
+
|
46
|
+
def after(subject, *args)
|
47
|
+
_callbacks(:after, subject).call(*args)
|
48
|
+
end
|
49
|
+
|
50
|
+
def around(subject, *args, &block)
|
51
|
+
_callbacks(:around, subject).call(*args, &block)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
def _callbacks(type, subject)
|
56
|
+
((@_callbacks ||= {})[type.to_sym] ||= {})[subject.to_sym] ||=
|
57
|
+
self.class._callbacks(type, subject).dup.tap { |stack|
|
58
|
+
stack.scope = self
|
59
|
+
}
|
60
|
+
end
|
61
|
+
end # Plugin
|
62
|
+
end # Ribbon::Plugins
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'ribbon/plugins/version'
|
2
|
+
|
3
|
+
module Ribbon
|
4
|
+
class Plugins
|
5
|
+
autoload(:Errors, 'ribbon/plugins/errors')
|
6
|
+
autoload(:Plugin, 'ribbon/plugins/plugin')
|
7
|
+
autoload(:AroundStack, 'ribbon/plugins/around_stack')
|
8
|
+
autoload(:BlockStack, 'ribbon/plugins/block_stack')
|
9
|
+
autoload(:ComponentMixin, 'ribbon/plugins/component_mixin')
|
10
|
+
|
11
|
+
attr_reader :component, :plugin_loader
|
12
|
+
|
13
|
+
def initialize(component=nil, &block)
|
14
|
+
@component = component
|
15
|
+
@plugin_loader = block
|
16
|
+
end
|
17
|
+
|
18
|
+
def add(plugin=nil, *args, &block)
|
19
|
+
plugin = _load(plugin, &block)
|
20
|
+
_add_plugin(plugin.new(self, *args))
|
21
|
+
end
|
22
|
+
|
23
|
+
def clear
|
24
|
+
@_plugins = nil
|
25
|
+
@_around_stack = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def before(subject, *args)
|
29
|
+
_plugins.reverse_each { |plugin| plugin.before(subject, *args) }
|
30
|
+
end
|
31
|
+
|
32
|
+
def after(subject, *args)
|
33
|
+
_plugins.reverse_each { |plugin| plugin.after(subject, *args) }
|
34
|
+
end
|
35
|
+
|
36
|
+
def around(subject, *args, &block)
|
37
|
+
_around_stack.call(subject, *args) { |subject, *args| block.call(*args) }
|
38
|
+
end
|
39
|
+
|
40
|
+
def perform(subject, *args, &block)
|
41
|
+
before(subject, *args)
|
42
|
+
retval = around(subject, *args, &block)
|
43
|
+
after(subject, *args)
|
44
|
+
|
45
|
+
retval
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
def _plugins
|
50
|
+
@_plugins ||= []
|
51
|
+
end
|
52
|
+
|
53
|
+
def _around_stack
|
54
|
+
@_around_stack ||= AroundStack.new(:block, self)
|
55
|
+
end
|
56
|
+
|
57
|
+
def _add_plugin(plugin)
|
58
|
+
_plugins.push(plugin)
|
59
|
+
|
60
|
+
_around_stack.push { |subject, *args|
|
61
|
+
plugin.around(subject, *args) { |*args|
|
62
|
+
perform_block(subject, *args)
|
63
|
+
}
|
64
|
+
}
|
65
|
+
|
66
|
+
plugin
|
67
|
+
end
|
68
|
+
|
69
|
+
def _load(plugin, &block)
|
70
|
+
if plugin
|
71
|
+
_load_plugin(plugin)
|
72
|
+
elsif block_given?
|
73
|
+
Plugin.create(&block)
|
74
|
+
else
|
75
|
+
raise Errors::LoadError, 'No plugin information provided'
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def _load_plugin(plugin)
|
80
|
+
_call_plugin_loader(plugin).tap { |p| plugin = p if p }
|
81
|
+
|
82
|
+
case plugin
|
83
|
+
when Class
|
84
|
+
plugin < Plugin && plugin or
|
85
|
+
raise Errors::LoadError, "Invalid plugin class: #{plugin.inspect} Must extend Plugin."
|
86
|
+
when Proc
|
87
|
+
Plugin.create(&plugin)
|
88
|
+
else
|
89
|
+
raise Errors::LoadError, "Invalid plugin identifier: #{plugin.inspect}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def _call_plugin_loader(plugin)
|
94
|
+
plugin_loader && component.instance_exec(plugin, &plugin_loader)
|
95
|
+
end
|
96
|
+
end # Plugins
|
97
|
+
end # Ribbon
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: plugins
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.4
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Robert Honer
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-10-05 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.2'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 3.2.0
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '3.2'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 3.2.0
|
33
|
+
description: A flexible plugins framework.
|
34
|
+
email:
|
35
|
+
- robert@ribbonpayments.com
|
36
|
+
executables: []
|
37
|
+
extensions: []
|
38
|
+
extra_rdoc_files: []
|
39
|
+
files:
|
40
|
+
- lib/ribbon/plugins.rb
|
41
|
+
- lib/ribbon/plugins/around_stack.rb
|
42
|
+
- lib/ribbon/plugins/block_stack.rb
|
43
|
+
- lib/ribbon/plugins/component_mixin.rb
|
44
|
+
- lib/ribbon/plugins/errors.rb
|
45
|
+
- lib/ribbon/plugins/plugin.rb
|
46
|
+
- lib/ribbon/plugins/version.rb
|
47
|
+
homepage: http://github.com/ribbon/plugins
|
48
|
+
licenses:
|
49
|
+
- BSD
|
50
|
+
metadata: {}
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
requirements: []
|
66
|
+
rubyforge_project:
|
67
|
+
rubygems_version: 2.4.6
|
68
|
+
signing_key:
|
69
|
+
specification_version: 4
|
70
|
+
summary: A flexible plugins framework.
|
71
|
+
test_files: []
|