ribbon-plugins 0.1.0

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: 230fbea0e698049dd095f097a6d977eca0e17359
4
+ data.tar.gz: 65c29190976d2a231e41493ad9840dbf9bdf90f3
5
+ SHA512:
6
+ metadata.gz: 6635638f54be4b62c15e4e988f60636d2e37d2bbb0d8686f785e2cba13a11a994d7f7a51dc664085f8cd021de9a2920a66c6a9531276b1c188cd9e7979537e8f
7
+ data.tar.gz: c70c4dec948b31c09c49e8bf61be462386095da0bce1ae5d3032b7951a46f77ec660932a3fd0a0bc7f6477f432f4c3b8b1bb0b446302477aa62bf5584298064b
@@ -0,0 +1,63 @@
1
+ class Ribbon::Plugins
2
+ class AroundStack
3
+ attr_reader :subject
4
+
5
+ def initialize(subject)
6
+ @subject = subject.to_sym
7
+ @_stack = []
8
+ end
9
+
10
+ def push(&block)
11
+ raise Errors::Error, "Must pass block" unless block_given?
12
+
13
+ AroundWrapper.new(subject, &block).tap { |wrapper|
14
+ @_stack.push(wrapper)
15
+ }
16
+ end
17
+
18
+ def call(*args, &block)
19
+ raise Errors::Error, "Must pass block" unless block_given?
20
+ call_stack = @_stack.dup
21
+
22
+ inner_most = WrappedBlock.new(&block)
23
+ call_stack.unshift(inner_most)
24
+
25
+ outer_most = call_stack.pop
26
+ outer_most.call(call_stack, *args)
27
+ end
28
+
29
+ class AroundWrapper
30
+ attr_reader :subject, :block
31
+
32
+ def initialize(subject, &block)
33
+ @subject = subject
34
+ @block = block
35
+ end
36
+
37
+ def call(call_stack, *args)
38
+ wrapped = call_stack.pop
39
+ raise Errors::Error, 'call stack too short' unless wrapped
40
+
41
+ define_singleton_method("perform_#{subject}") { |*new_args|
42
+ args = new_args unless new_args.empty?
43
+ wrapped.call(call_stack, *args)
44
+ }
45
+
46
+ instance_exec(*args, &block)
47
+ end
48
+ end
49
+
50
+ class WrappedBlock
51
+ attr_reader :block
52
+
53
+ def initialize(&block)
54
+ @block = block
55
+ end
56
+
57
+ def call(call_stack, *args)
58
+ raise Errors::Error, 'receiving non-empty call stack' unless call_stack.empty?
59
+ block.call(*args)
60
+ end
61
+ end
62
+ end # AroundStack
63
+ end # Ribbon::Plugins
@@ -0,0 +1,15 @@
1
+ class Ribbon::Plugins
2
+ class BlockStack
3
+ def initialize
4
+ @_stack = []
5
+ end
6
+
7
+ def push(&block)
8
+ @_stack.push(block)
9
+ end
10
+
11
+ def call(*args)
12
+ @_stack.reverse_each { |block| block.call(*args) }
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,6 @@
1
+ class Ribbon::Plugins
2
+ module Errors
3
+ class Error < StandardError; end
4
+ class LoadError < Error; end
5
+ end
6
+ end
@@ -0,0 +1,53 @@
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
+ def before(subject, *args)
37
+ _callbacks(:before, subject).call(*args)
38
+ end
39
+
40
+ def after(subject, *args)
41
+ _callbacks(:after, subject).call(*args)
42
+ end
43
+
44
+ def around(subject, *args, &block)
45
+ _callbacks(:around, subject).call(*args, &block)
46
+ end
47
+
48
+ private
49
+ def _callbacks(type, subject)
50
+ self.class._callbacks(type, subject)
51
+ end
52
+ end # Plugin
53
+ end # Ribbon::Plugins
@@ -0,0 +1,5 @@
1
+ module Ribbon
2
+ class Plugins
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,89 @@
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
+
10
+ def initialize(plugin_module=nil)
11
+ @_plugin_module = plugin_module
12
+ end
13
+
14
+ def add(plugin=nil, &block)
15
+ plugin = _load(plugin, &block)
16
+ _add_plugin(plugin.new)
17
+ end
18
+
19
+ def clear
20
+ @_plugins = []
21
+ nil
22
+ end
23
+
24
+ def before(subject, *args)
25
+ _plugins.reverse_each { |plugin| plugin.before(subject, *args) }
26
+ end
27
+
28
+ def after(subject, *args)
29
+ _plugins.reverse_each { |plugin| plugin.after(subject, *args) }
30
+ end
31
+
32
+ def around(subject, *args, &block)
33
+ _around_stack.call(subject, *args) { |subject, *args| block.call(*args) }
34
+ end
35
+
36
+ def perform(subject, *args, &block)
37
+ before(subject, *args)
38
+ around(subject, *args, &block)
39
+ after(subject, *args)
40
+ end
41
+
42
+ private
43
+ def _plugins
44
+ @_plugins ||= []
45
+ end
46
+
47
+ def _around_stack
48
+ @__around_stack ||= AroundStack.new(:block)
49
+ end
50
+
51
+ def _add_plugin(plugin)
52
+ _plugins.push(plugin)
53
+ _around_stack.push { |subject, *args|
54
+ plugin.around(subject, *args) {
55
+ perform_block
56
+ }
57
+ }
58
+ end
59
+
60
+ def _load(plugin, &block)
61
+ if plugin
62
+ _load_plugin(plugin)
63
+ elsif block_given?
64
+ Plugin.create(&block)
65
+ else
66
+ raise Errors::LoadError, 'No plugin information provided'
67
+ end
68
+ end
69
+
70
+ def _load_plugin(plugin)
71
+ case plugin
72
+ when Class
73
+ plugin < Plugin && plugin or
74
+ raise Errors::LoadError, "Invalid plugin class: #{plugin.inspect} Must extend Plugin."
75
+ when Symbol, String
76
+ _load_plugin_by_name(plugin)
77
+ when Proc
78
+ Plugin.create(&plugin)
79
+ else
80
+ raise Errors::LoadError, "Invalid plugin identifier: #{plugin.inspect}"
81
+ end
82
+ end
83
+
84
+ def _load_plugin_by_name(name)
85
+ @_plugin_module && @_plugin_module.const_get("#{name.upcase}Plugin") or
86
+ raise Errors::LoadError, "Cannot load plugin by name. No plugin module provided."
87
+ end
88
+ end # Plugins
89
+ end # Ribbon
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ribbon-plugins
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Robert Honer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-03-02 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/errors.rb
44
+ - lib/ribbon/plugins/plugin.rb
45
+ - lib/ribbon/plugins/version.rb
46
+ homepage: http://github.com/ribbon/plugins
47
+ licenses:
48
+ - BSD
49
+ metadata: {}
50
+ post_install_message:
51
+ rdoc_options: []
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - '>='
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubyforge_project:
66
+ rubygems_version: 2.2.2
67
+ signing_key:
68
+ specification_version: 4
69
+ summary: A flexible plugins framework.
70
+ test_files: []