pipeliner 1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +0 -0
- data/LICENSE +19 -0
- data/README.rdoc +112 -0
- data/lib/pipeliner/Filter.rb +31 -0
- data/lib/pipeliner/FilterManager.rb +109 -0
- data/lib/pipeliner/Pipeline.rb +154 -0
- data/lib/pipeliner.rb +1 -0
- data/pipeliner.gemspec +17 -0
- data/tests/cases/Filter.rb +29 -0
- data/tests/cases/FilterManager.rb +75 -0
- data/tests/cases/Pipeline.rb +111 -0
- data/tests/run_tests.rb +8 -0
- metadata +90 -0
data/CHANGELOG
ADDED
File without changes
|
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2009 spox <spox@modspox.com>
|
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
|
11
|
+
all 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
|
19
|
+
THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
== Pipeliner
|
2
|
+
|
3
|
+
The pipeliner library provides framework for message passing between objects. It allows objects to hook into the pipeline to receive certain types of objects, and process them in any way they see fit. Processing happens using a thread pool, letting message processing happen in an asynchronous fashion.
|
4
|
+
|
5
|
+
=== install (easy):
|
6
|
+
|
7
|
+
gem install pipeliner
|
8
|
+
|
9
|
+
=== install (less easy):
|
10
|
+
|
11
|
+
git clone http://github.com/spox/pipeliner.git
|
12
|
+
cd pipeliner && gem build *.gemspec && gem install ./
|
13
|
+
|
14
|
+
=== install (less easy that's a little easier)
|
15
|
+
|
16
|
+
{rip}[http://hellorip.com/about.html] makes it easy to install directly from a github repository.
|
17
|
+
|
18
|
+
== Gritty Details
|
19
|
+
|
20
|
+
The pipeliner library allows for some fun things, and here is where I'll try and explain some of them. First, lets take a look at a simple example of this library in action:
|
21
|
+
|
22
|
+
require 'pipeliner'
|
23
|
+
|
24
|
+
output = Array.new
|
25
|
+
pipeline = Pipeliner::Pipeline.new
|
26
|
+
pipeline.hook(String){|s| output << "#{s}: modified"}
|
27
|
+
pipeline << "string"
|
28
|
+
pipeline << 100
|
29
|
+
p output
|
30
|
+
|
31
|
+
=> ["string: modified"]
|
32
|
+
|
33
|
+
In the example above, we use a simple block to process any String type objects. Anything else passed placed in the pipeline is ignored by the block. We can also use objects instead of blocks:
|
34
|
+
|
35
|
+
require 'pipeliner'
|
36
|
+
|
37
|
+
class Tester < Pipeliner::Filter
|
38
|
+
attr_reader :store
|
39
|
+
def initialize
|
40
|
+
@store = []
|
41
|
+
end
|
42
|
+
def process(m)
|
43
|
+
@store << m
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
pipeline = Pipeliner::Pipeline.new
|
48
|
+
obj = Tester.new
|
49
|
+
pipeline.hook(Object, obj, :process)
|
50
|
+
pipeline << "String"
|
51
|
+
pipeline << 100
|
52
|
+
pipeline << {:test => :data}
|
53
|
+
p obj.store
|
54
|
+
|
55
|
+
=> ["String", 100, {:test=>:data}]
|
56
|
+
|
57
|
+
But, what if you want anything added to the pipeline to be modified before it is distributed out to hooked processors? Well, then you would use a filter:
|
58
|
+
|
59
|
+
require 'pipeliner'
|
60
|
+
|
61
|
+
output = []
|
62
|
+
pipeline = Pipeliner::Pipeline.new
|
63
|
+
pipeline.filters.add(String){|s| "#{s} -> filtered"}
|
64
|
+
pipeline.hook(Object){|s| output << s}
|
65
|
+
pipeline << "string"
|
66
|
+
p output
|
67
|
+
|
68
|
+
=> ["string -> filtered"]
|
69
|
+
|
70
|
+
Filters allow objects to be modified before reaching any hooked processors, and can even remove an unwanted object from the pipeline. Here is an example that removes all Symbols:
|
71
|
+
|
72
|
+
require 'pipeliner'
|
73
|
+
|
74
|
+
output = []
|
75
|
+
pipeline = Pipeliner::Pipeline.new
|
76
|
+
pipeline.filters.add(Symbol){|s| nil}
|
77
|
+
pipeline.hook(Object){|s| output << s}
|
78
|
+
pipeline << "string"
|
79
|
+
pipeline << 100
|
80
|
+
pipeline << :test
|
81
|
+
p output
|
82
|
+
|
83
|
+
=> ["string", 100]
|
84
|
+
|
85
|
+
Filters can also be created using the Filter object:
|
86
|
+
|
87
|
+
require 'pipeliner'
|
88
|
+
|
89
|
+
class MyFilter < Pipeliner::Filter
|
90
|
+
def do_filter(m)
|
91
|
+
"#{m} - filtered"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
output = []
|
95
|
+
pipeline = Pipeliner::Pipeline.new
|
96
|
+
filter = MyFilter.new(String)
|
97
|
+
pipeline.filters.add(String, filter)
|
98
|
+
pipeline.hook(Object){|s|output << s}
|
99
|
+
pipeline << "string"
|
100
|
+
pipeline << 100
|
101
|
+
p output
|
102
|
+
|
103
|
+
=> ["string - filtered", 100]
|
104
|
+
|
105
|
+
== Last remarks
|
106
|
+
|
107
|
+
If you find any bugs, please report them through {github}[http://github.com/spox/pipeliner/issues]. If you are in need of any help, you can generally find me on DALnet and Freenode.
|
108
|
+
|
109
|
+
== License
|
110
|
+
|
111
|
+
Pipeliner is licensed under the MIT License
|
112
|
+
Copyright (c) 2009 spox <spox@modspox.com>
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Pipeliner
|
2
|
+
class Filter
|
3
|
+
# type of Objects filter is to be applied to
|
4
|
+
attr_reader :type
|
5
|
+
# t:: type of Objects filter is to be applied to
|
6
|
+
# Create a new Filter
|
7
|
+
def initialize(t)
|
8
|
+
raise ArgumentError.new('Expecting Class or String') unless [Class, String].include?(t.class)
|
9
|
+
@type = t
|
10
|
+
end
|
11
|
+
|
12
|
+
# o:: Object from Pipeline
|
13
|
+
# Applies filter to Object
|
14
|
+
def filter(o)
|
15
|
+
raise ArgumentError.new('Wrong type supplied to filter') unless Splib.type_of?(o, @type)
|
16
|
+
do_filter(o)
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
# o:: Object to be filtered
|
22
|
+
# This is where the actual filtering takes place. This
|
23
|
+
# is the method to overload!
|
24
|
+
# NOTE: Objects can be filtered in any way. For a filter to
|
25
|
+
# basically "throw away" an Object, simply return nil and the
|
26
|
+
# Object will not be flushed down the Pipeline
|
27
|
+
def do_filter(o)
|
28
|
+
raise NoMethodError.new('This method has not been implemented')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'pipeliner/Filter'
|
2
|
+
require 'thread'
|
3
|
+
|
4
|
+
module Pipeliner
|
5
|
+
class FilterManager
|
6
|
+
# Create a new FilterManager
|
7
|
+
def initialize
|
8
|
+
@filters = {}
|
9
|
+
@lock = Mutex.new
|
10
|
+
end
|
11
|
+
|
12
|
+
# type:: Object type to apply filter to
|
13
|
+
# filter:: Pipeline::Filter to add
|
14
|
+
# Add a Filter to be applied to the given types
|
15
|
+
def add(type, filter=nil, &block)
|
16
|
+
if((filter.nil? && !block_given?) || (filter && !filter.is_a?(Filter)))
|
17
|
+
raise ArgumentError.new('Filter or proc must be provided for filter')
|
18
|
+
end
|
19
|
+
const = Splib.find_const(type)
|
20
|
+
type = const unless const.nil?
|
21
|
+
@lock.synchronize do
|
22
|
+
@filters[type] ||= {}
|
23
|
+
end
|
24
|
+
if(block_given?)
|
25
|
+
unless(block.arity == 1 || block.arity < 0)
|
26
|
+
raise ArgumentError.new('Block must accept a parameter')
|
27
|
+
end
|
28
|
+
@lock.synchronize do
|
29
|
+
@filters[type][:procs] ||= []
|
30
|
+
unless(@filters[type][:procs].include?(block))
|
31
|
+
@filters[type][:procs] << block
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
if(filter)
|
36
|
+
@lock.synchronize do
|
37
|
+
unless(@filters[type].include?(filter))
|
38
|
+
@filters[type][:filters] ||= []
|
39
|
+
unless(@filters[type][:filters].include?(filter))
|
40
|
+
@filters[type][:filters] << filter
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
filter ? block_given? ? [filter, block] : filter : block
|
46
|
+
end
|
47
|
+
|
48
|
+
# filter:: Pipeline::Filter to remove
|
49
|
+
# type:: Object type filter is applied to.
|
50
|
+
# Remove Filter from given type. If no type is given all references
|
51
|
+
# to the given filter will be removed
|
52
|
+
def remove(filter, type=nil)
|
53
|
+
if(type)
|
54
|
+
const = Splib.find_const(type)
|
55
|
+
type = const unless const.nil?
|
56
|
+
end
|
57
|
+
@lock.synchronize do
|
58
|
+
(type ? [@filters[type]] : @filters.values).each do |set|
|
59
|
+
[:filters, :procs].each do |t|
|
60
|
+
if(set[t])
|
61
|
+
set[t].delete_if{|v| v == filter}
|
62
|
+
set.delete(t) if set[t].empty?
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
@filters.delete_if{|k,v|v.empty?}
|
67
|
+
end
|
68
|
+
nil
|
69
|
+
end
|
70
|
+
|
71
|
+
# type:: Object types
|
72
|
+
# Return filters of given type or all filters
|
73
|
+
# if not type is supplied
|
74
|
+
def filters(type=nil)
|
75
|
+
unless(type)
|
76
|
+
@filters.dup
|
77
|
+
else
|
78
|
+
const = Splib.find_const(type)
|
79
|
+
type = const unless const.nil?
|
80
|
+
@filters[type] ? @filters[type].dup : nil
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# o:: Object to apply filters to
|
85
|
+
# Applies any Filters applicable to object type
|
86
|
+
def apply_filters(o)
|
87
|
+
@filters.keys.find_all{|type| Splib.type_of?(o, type)}.each do |type|
|
88
|
+
@filters[type].each_pair do |k,v|
|
89
|
+
begin
|
90
|
+
case k
|
91
|
+
when :filters
|
92
|
+
v.each{|f|o = f.filter(o)}
|
93
|
+
when :procs
|
94
|
+
v.each{|pr|o = pr.call(o)}
|
95
|
+
end
|
96
|
+
rescue ArgumentError
|
97
|
+
# ignore this
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
o
|
102
|
+
end
|
103
|
+
|
104
|
+
# Remove all filters
|
105
|
+
def clear
|
106
|
+
@filters.clear
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
['thread', 'splib', 'actionpool', 'pipeliner/FilterManager'].each{|f| require f}
|
2
|
+
|
3
|
+
Splib.load :Constants
|
4
|
+
|
5
|
+
module Pipeliner
|
6
|
+
class Pipeline
|
7
|
+
# args:: Hash containing initializations
|
8
|
+
# :pool =>:: ActionPool::Pool for the Pipeline to use
|
9
|
+
# :filters =>:: FilterManager for the Pipeline to use
|
10
|
+
# Create a new Pipeline
|
11
|
+
def initialize(args={})
|
12
|
+
@pool = args[:pool] ? args[:pool] : ActionPool::Pool.new
|
13
|
+
@hooks = {}
|
14
|
+
@lock = Mutex.new
|
15
|
+
@filters = args[:filters] ? args[:filters] : FilterManager.new
|
16
|
+
end
|
17
|
+
|
18
|
+
# Close the pipeline
|
19
|
+
# Note: This is important to at the end of a script when not
|
20
|
+
# providing an ActionPool thread pool to the pipeline.
|
21
|
+
# This will ensure the thread pool is properly shutdown
|
22
|
+
# and avoid the script hanging.
|
23
|
+
def close
|
24
|
+
@pool.shutdown
|
25
|
+
clear
|
26
|
+
@pool = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
# Open the pipeline
|
30
|
+
def open
|
31
|
+
@pool = ActionPool::Pool.new unless @pool
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns current FilterManager
|
35
|
+
def filters
|
36
|
+
@filters
|
37
|
+
end
|
38
|
+
|
39
|
+
# fm:: FilterManager
|
40
|
+
# Set the FilterManager the Pipeline should use
|
41
|
+
def filters=(fm)
|
42
|
+
raise ArgumentError.new('Expecting a FilterManager') unless fm.is_a?(FilterManager)
|
43
|
+
@filters = fm
|
44
|
+
end
|
45
|
+
|
46
|
+
# type:: Type of Objects to pass to object
|
47
|
+
# object:: Object to hook to pipeline
|
48
|
+
# method:: Method to call on object
|
49
|
+
# block:: Block to apply to object (called without object and method set)
|
50
|
+
# Hooks an Object into the pipeline for objects of a given type
|
51
|
+
def hook(type, object=nil, method=nil, &block)
|
52
|
+
raise ArgumentError.new('No object information or block provided for hook') if !block_given? && object.nil? && method.nil?
|
53
|
+
raise ArgumentError.new('Block must accept a parameter') unless block.nil? || block.arity == 1 || block.arity < 0
|
54
|
+
@lock.synchronize do
|
55
|
+
const = Splib.find_const(type)
|
56
|
+
type = const unless const.nil?
|
57
|
+
@hooks[type] ||= {}
|
58
|
+
if(block_given?)
|
59
|
+
@hooks[type][:procs] ||= []
|
60
|
+
@hooks[type][:procs] << block
|
61
|
+
end
|
62
|
+
if(object && method)
|
63
|
+
name = object.class
|
64
|
+
method = method.to_sym
|
65
|
+
raise ArgumentError.new('Given object does not respond to given method') unless object.respond_to?(method)
|
66
|
+
@hooks[type][name] ||= []
|
67
|
+
@hooks[type][name] << {:object => object, :method => method}
|
68
|
+
end
|
69
|
+
end
|
70
|
+
block_given? ? block : nil
|
71
|
+
end
|
72
|
+
|
73
|
+
# object:: Object or Proc to unhook from the pipeline
|
74
|
+
# type:: Type of Objects being received
|
75
|
+
# method:: method registered to call
|
76
|
+
# Remove a hook from the pipeline. If the method and type are not
|
77
|
+
# specified, the given object will be removed from all hooks
|
78
|
+
def unhook(object, type=nil, method=nil)
|
79
|
+
raise ArgumentError.new('Object method provided for a Proc') if object.is_a?(Proc) && method
|
80
|
+
@lock.synchronize do
|
81
|
+
case object
|
82
|
+
when Proc
|
83
|
+
if(type)
|
84
|
+
@hooks[type][:procs].delete(object)
|
85
|
+
@hooks[type].delete(:procs) if @hooks[type][:procs].empty?
|
86
|
+
else
|
87
|
+
@hooks.each_value do |h|
|
88
|
+
h[:procs].delete(object)
|
89
|
+
h.delete(:procs) if h[:procs].empty?
|
90
|
+
end
|
91
|
+
end
|
92
|
+
else
|
93
|
+
if(method.nil? && type.nil?)
|
94
|
+
@hooks.each_value{|v|v.delete_if{|k,z| k == object.class}}
|
95
|
+
else
|
96
|
+
raise ArgumentError.new('Type must be provided') if type.nil?
|
97
|
+
const = Splib.find_const(type)
|
98
|
+
type = const unless const.nil?
|
99
|
+
method = method.to_sym if method
|
100
|
+
name = object.class
|
101
|
+
raise NameError.new('Uninitialized hook type given') unless @hooks[type]
|
102
|
+
raise StandardError.new('No hooks found for given object') unless @hooks[type][name]
|
103
|
+
if(method)
|
104
|
+
@hooks[type][name].delete_if{|x|x[:method] == method}
|
105
|
+
@hooks[type].delete(name) if @hooks[type][name].empty?
|
106
|
+
else
|
107
|
+
@hooks[type].delete(name)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
@hooks.delete_if{|k,v|v.empty?}
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Return current hooks hash
|
116
|
+
def hooks
|
117
|
+
@lock.synchronize{ @hooks.dup }
|
118
|
+
end
|
119
|
+
|
120
|
+
# Remove all hooks from the pipeline
|
121
|
+
def clear
|
122
|
+
@lock.synchronize{ @hooks.clear }
|
123
|
+
end
|
124
|
+
|
125
|
+
# object:: Object to send down the pipeline
|
126
|
+
# Send an object down the pipeline
|
127
|
+
def <<(object)
|
128
|
+
raise StandardError.new('Pipeline is currently closed') if @pool.nil?
|
129
|
+
object = @filters.apply_filters(object)
|
130
|
+
if(object)
|
131
|
+
object.freeze
|
132
|
+
@pool.process{ flush(object) }
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
# o:: Object to flush
|
139
|
+
# Applies object to all matching hooks
|
140
|
+
def flush(o)
|
141
|
+
@hooks.keys.each do |type|
|
142
|
+
next unless Splib.type_of?(o, type)
|
143
|
+
@hooks[type].each_pair do |key, objects|
|
144
|
+
if(key == :procs)
|
145
|
+
objects.each{|pr| @pool.process{ pr.call(o) }}
|
146
|
+
else
|
147
|
+
objects.each{|h| @pool.process{ h[:object].send(h[:method], o)}}
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
nil
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
data/lib/pipeliner.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'pipeliner/Pipeline'
|
data/pipeliner.gemspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
spec = Gem::Specification.new do |s|
|
2
|
+
s.name = 'pipeliner'
|
3
|
+
s.author = 'spox'
|
4
|
+
s.email = 'spox@modspox.com'
|
5
|
+
s.version = '1.0'
|
6
|
+
s.summary = 'Object Pipeline'
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.files = Dir['**/*']
|
9
|
+
s.rdoc_options = %w(--title pipeliner --main README.rdoc --line-numbers)
|
10
|
+
s.extra_rdoc_files = %w(README.rdoc CHANGELOG)
|
11
|
+
s.require_paths = %w(lib)
|
12
|
+
s.required_ruby_version = '>= 1.8.6'
|
13
|
+
s.add_dependency 'ActionPool', '~> 0.2.0'
|
14
|
+
s.add_dependency 'splib', '~> 1.1'
|
15
|
+
s.homepage = %q(http://github.com/spox/pipeliner)
|
16
|
+
s.description = "Simple library to allow pipeline styled communications between objects"
|
17
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'pipeliner'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
class FilterTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
end
|
7
|
+
def teardown
|
8
|
+
end
|
9
|
+
|
10
|
+
class MyFilter < Pipeliner::Filter
|
11
|
+
def do_filter(o)
|
12
|
+
return [o.to_sym]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_basic
|
17
|
+
assert_raise(ArgumentError){ Pipeliner::Filter.new }
|
18
|
+
a = Pipeliner::Filter.new(String)
|
19
|
+
assert_kind_of(Pipeliner::Filter, a)
|
20
|
+
assert_raise(ArgumentError){ a.filter([1,2]) }
|
21
|
+
assert_raise(NoMethodError){ a.filter('test') }
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_custom
|
25
|
+
a = MyFilter.new(String)
|
26
|
+
assert_kind_of(Array, a.filter('test'))
|
27
|
+
assert_equal([:test], a.filter('test'))
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'pipeliner'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
class FilterManagerTest < Test::Unit::TestCase
|
5
|
+
class MyFilter < Pipeliner::Filter
|
6
|
+
def do_filter(o)
|
7
|
+
"#{o}: filtered"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
def setup
|
11
|
+
@fm = Pipeliner::FilterManager.new
|
12
|
+
@filter = MyFilter.new(String)
|
13
|
+
end
|
14
|
+
def teardown
|
15
|
+
@fm.clear
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_add
|
19
|
+
@fm.add(String, @filter)
|
20
|
+
assert_equal(1, @fm.filters[String][:filters].size)
|
21
|
+
@fm.add(String, @filter)
|
22
|
+
assert_equal(1, @fm.filters[String][:filters].size)
|
23
|
+
@fm.add(String){|s|s}
|
24
|
+
assert_equal(1, @fm.filters[String][:procs].size)
|
25
|
+
assert_raise(ArgumentError) do
|
26
|
+
@fm.add(String, "string")
|
27
|
+
end
|
28
|
+
assert_raise(ArgumentError) do
|
29
|
+
@fm.add(Object)
|
30
|
+
end
|
31
|
+
@fm.clear
|
32
|
+
assert(@fm.filters.empty?)
|
33
|
+
@fm.add(String, @filter){|s|s}
|
34
|
+
assert_equal(1, @fm.filters[String][:filters].size)
|
35
|
+
assert_equal(1, @fm.filters[String][:procs].size)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_remove
|
39
|
+
@fm.add(String, @filter)
|
40
|
+
assert_equal(1, @fm.filters[String][:filters].size)
|
41
|
+
@fm.remove(@filter, String)
|
42
|
+
assert(!@fm.filters.has_key?(String))
|
43
|
+
@fm.add(String, @filter)
|
44
|
+
assert_equal(1, @fm.filters[String][:filters].size)
|
45
|
+
@fm.remove(@filter)
|
46
|
+
assert(!@fm.filters.has_key?(String))
|
47
|
+
pr = @fm.add(String){|s|s}
|
48
|
+
assert_equal(1, @fm.filters[String][:procs].size)
|
49
|
+
@fm.remove(pr, String)
|
50
|
+
assert(!@fm.filters.has_key?(String))
|
51
|
+
pr = @fm.add(String){|s|s}
|
52
|
+
assert_equal(1, @fm.filters[String][:procs].size)
|
53
|
+
@fm.remove(pr)
|
54
|
+
assert(!@fm.filters.has_key?(String))
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_apply
|
58
|
+
@fm.add(String, @filter)
|
59
|
+
assert_equal("test: filtered", @fm.apply_filters("test"))
|
60
|
+
assert_equal(1, @fm.apply_filters(1))
|
61
|
+
data = "fubar"
|
62
|
+
assert_not_same(data, @fm.apply_filters(data))
|
63
|
+
data = [:fubar]
|
64
|
+
assert_same(data, @fm.apply_filters(data))
|
65
|
+
@fm.add(Fixnum){|o| "string"}
|
66
|
+
assert_kind_of(String, @fm.apply_filters(2))
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_clear
|
70
|
+
@fm.add(String, @filter)
|
71
|
+
assert_equal(1, @fm.filters[String][:filters].size)
|
72
|
+
@fm.clear
|
73
|
+
assert(@fm.filters.empty?)
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'pipeliner'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
class PipelineTest < Test::Unit::TestCase
|
5
|
+
class Tester
|
6
|
+
def initialize
|
7
|
+
@m = []
|
8
|
+
end
|
9
|
+
def method(s)
|
10
|
+
@m << s
|
11
|
+
end
|
12
|
+
def messages
|
13
|
+
@m
|
14
|
+
end
|
15
|
+
end
|
16
|
+
def setup
|
17
|
+
@pipeline = Pipeliner::Pipeline.new
|
18
|
+
end
|
19
|
+
def teardown
|
20
|
+
@pipeline.filters.clear
|
21
|
+
@pipeline.clear
|
22
|
+
@pipeline.close
|
23
|
+
end
|
24
|
+
def test_filters
|
25
|
+
assert_kind_of(Pipeliner::FilterManager, @pipeline.filters)
|
26
|
+
end
|
27
|
+
def test_set_filters
|
28
|
+
fm = Pipeliner::FilterManager.new
|
29
|
+
assert_not_same(fm, @pipeline.filters)
|
30
|
+
@pipeline.filters = fm
|
31
|
+
assert_same(fm, @pipeline.filters)
|
32
|
+
end
|
33
|
+
def test_hook_addition
|
34
|
+
assert_raise(ArgumentError){ @pipeline.hook(String) }
|
35
|
+
assert_raise(ArgumentError){ @pipeline.hook(String){ true } }
|
36
|
+
assert_raise(ArgumentError){ @pipeline.hook(String){|a,b| true} }
|
37
|
+
assert_kind_of(Proc, @pipeline.hook(String){|a| true })
|
38
|
+
assert_kind_of(Proc, @pipeline.hook(String){|a, *b| true })
|
39
|
+
assert_kind_of(Proc, @pipeline.hook(String){|*a| true })
|
40
|
+
assert_equal(1, @pipeline.hooks.size)
|
41
|
+
assert_equal(3, @pipeline.hooks[String][:procs].size)
|
42
|
+
@pipeline.clear
|
43
|
+
assert(@pipeline.hooks.empty?)
|
44
|
+
object = Tester.new
|
45
|
+
assert_nil(@pipeline.hook(String, object, :method))
|
46
|
+
assert_equal(1, @pipeline.hooks[String][object.class].size)
|
47
|
+
end
|
48
|
+
def test_hook_deletion
|
49
|
+
pr = @pipeline.hook(String){|a| true}
|
50
|
+
assert_kind_of(Proc, pr)
|
51
|
+
assert_equal(1, @pipeline.hooks[String][:procs].size)
|
52
|
+
@pipeline.unhook(pr, String)
|
53
|
+
assert(@pipeline.hooks.empty?)
|
54
|
+
object = Tester.new
|
55
|
+
@pipeline.hook(String, object, :method)
|
56
|
+
assert_equal(1, @pipeline.hooks[String].size)
|
57
|
+
@pipeline.unhook(object, String, :method)
|
58
|
+
assert(@pipeline.hooks.empty?)
|
59
|
+
@pipeline.hook(String, object, :method)
|
60
|
+
assert_equal(1, @pipeline.hooks[String].size)
|
61
|
+
@pipeline.unhook(object, String)
|
62
|
+
assert(@pipeline.hooks.empty?)
|
63
|
+
@pipeline.hook(String, object, :method)
|
64
|
+
assert_equal(1, @pipeline.hooks[String].size)
|
65
|
+
@pipeline.unhook(object)
|
66
|
+
assert(@pipeline.hooks.empty?)
|
67
|
+
@pipeline.hook(String, object, :method)
|
68
|
+
@pipeline.hook(Object){|a|true}
|
69
|
+
assert_equal(2, @pipeline.hooks.size)
|
70
|
+
@pipeline.clear
|
71
|
+
assert(@pipeline.hooks.empty?)
|
72
|
+
end
|
73
|
+
def test_flush
|
74
|
+
out = []
|
75
|
+
assert(out.empty?)
|
76
|
+
@pipeline.hook(String){|x|out << x}
|
77
|
+
assert_equal(1, @pipeline.hooks.size)
|
78
|
+
@pipeline << "Fubar"
|
79
|
+
sleep(0.01)
|
80
|
+
assert_equal(1, out.size)
|
81
|
+
assert_equal("Fubar", out.pop)
|
82
|
+
@pipeline << 100
|
83
|
+
sleep(0.01)
|
84
|
+
assert(out.empty?)
|
85
|
+
object = Tester.new
|
86
|
+
@pipeline.hook(String, object, :method)
|
87
|
+
@pipeline.hook(Fixnum, object, :method)
|
88
|
+
assert_equal(2, @pipeline.hooks[String].size)
|
89
|
+
assert_equal(2, @pipeline.hooks.size)
|
90
|
+
@pipeline << 'test'
|
91
|
+
sleep(0.01)
|
92
|
+
assert_equal(object.messages.pop, out.pop)
|
93
|
+
@pipeline << 1
|
94
|
+
sleep(0.01)
|
95
|
+
assert(out.empty?)
|
96
|
+
assert_equal(1, object.messages.pop)
|
97
|
+
end
|
98
|
+
def test_filters
|
99
|
+
out = []
|
100
|
+
@pipeline.filters.add(String){|s|10}
|
101
|
+
@pipeline.hook(Object){|s|out << s}
|
102
|
+
@pipeline << 'fubar'
|
103
|
+
assert(out.include?(10))
|
104
|
+
assert(!out.include?('fubar'))
|
105
|
+
@pipeline.filters.clear
|
106
|
+
out.clear
|
107
|
+
@pipeline.filters.add(String){|s|nil}
|
108
|
+
@pipeline << 'test'
|
109
|
+
assert(out.empty?)
|
110
|
+
end
|
111
|
+
end
|
data/tests/run_tests.rb
ADDED
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pipeliner
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "1.0"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- spox
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-12-30 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: ActionPool
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ~>
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.2.0
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: splib
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "1.1"
|
34
|
+
version:
|
35
|
+
description: Simple library to allow pipeline styled communications between objects
|
36
|
+
email: spox@modspox.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- README.rdoc
|
43
|
+
- CHANGELOG
|
44
|
+
files:
|
45
|
+
- tests/cases/Filter.rb
|
46
|
+
- tests/cases/FilterManager.rb
|
47
|
+
- tests/cases/Pipeline.rb
|
48
|
+
- tests/run_tests.rb
|
49
|
+
- lib/pipeliner/Filter.rb
|
50
|
+
- lib/pipeliner/FilterManager.rb
|
51
|
+
- lib/pipeliner/Pipeline.rb
|
52
|
+
- lib/pipeliner.rb
|
53
|
+
- CHANGELOG
|
54
|
+
- LICENSE
|
55
|
+
- README.rdoc
|
56
|
+
- pipeliner.gemspec
|
57
|
+
has_rdoc: true
|
58
|
+
homepage: http://github.com/spox/pipeliner
|
59
|
+
licenses: []
|
60
|
+
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options:
|
63
|
+
- --title
|
64
|
+
- pipeliner
|
65
|
+
- --main
|
66
|
+
- README.rdoc
|
67
|
+
- --line-numbers
|
68
|
+
require_paths:
|
69
|
+
- lib
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 1.8.6
|
75
|
+
version:
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: "0"
|
81
|
+
version:
|
82
|
+
requirements: []
|
83
|
+
|
84
|
+
rubyforge_project:
|
85
|
+
rubygems_version: 1.3.5
|
86
|
+
signing_key:
|
87
|
+
specification_version: 3
|
88
|
+
summary: Object Pipeline
|
89
|
+
test_files: []
|
90
|
+
|