andromeda 0.1 → 0.1.2
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.
- data/.gitignore +1 -0
- data/.rvmrc +1 -0
- data/CHANGELOG.md +49 -0
- data/Gemfile +17 -9
- data/Gemfile.lock +5 -0
- data/LICENSE.txt +1 -2
- data/README.md +150 -15
- data/ROADMAP.md +73 -0
- data/Rakefile +4 -6
- data/andromeda.gemspec +2 -2
- data/lib/andromeda.rb +52 -9
- data/lib/andromeda/atom.rb +105 -0
- data/lib/andromeda/cmd.rb +242 -0
- data/lib/andromeda/common.rb +0 -0
- data/lib/andromeda/copy_clone.rb +44 -0
- data/lib/andromeda/error.rb +42 -0
- data/lib/andromeda/guide.rb +50 -0
- data/lib/andromeda/guide_track.rb +98 -0
- data/lib/andromeda/id.rb +10 -83
- data/lib/andromeda/impl/atom.rb +47 -0
- data/lib/andromeda/impl/class_attr.rb +31 -0
- data/lib/andromeda/impl/proto_plan.rb +219 -0
- data/lib/andromeda/impl/to_s.rb +48 -0
- data/lib/andromeda/impl/xor_id.rb +89 -0
- data/lib/andromeda/kit.rb +172 -0
- data/lib/andromeda/map_reduce.rb +3 -0
- data/lib/andromeda/plan.rb +130 -0
- data/lib/andromeda/pool_guide.rb +70 -0
- data/lib/andromeda/spot.rb +132 -0
- data/lib/andromeda/sugar.rb +41 -0
- data/lib/andromeda/sync.rb +68 -0
- data/lib/andromeda/version.rb +1 -1
- data/yard_extensions/andromeda.rb +28 -0
- metadata +30 -13
- data/lib/andromeda/andromeda.rb +0 -225
- data/lib/andromeda/commando.rb +0 -106
- data/lib/andromeda/helpers.rb +0 -134
- data/lib/andromeda/join.rb +0 -48
- data/lib/andromeda/pools.rb +0 -69
- data/lib/andromeda/scope.rb +0 -38
@@ -0,0 +1,132 @@
|
|
1
|
+
module Andromeda
|
2
|
+
|
3
|
+
# A spot is a reachable destination to which data may be sent for processing.
|
4
|
+
# It encapsulates addressing the processing logic of a Plan into a separate,
|
5
|
+
# immutable Object that may be passed around freely.
|
6
|
+
#
|
7
|
+
# It is somewhat similiar/a mixture of notions such as actor address,
|
8
|
+
# RPC endpoint, and stack frame in other frameworks
|
9
|
+
#
|
10
|
+
# You MUST not inherit from this class
|
11
|
+
#
|
12
|
+
class Spot < Impl::ConnectorBase
|
13
|
+
include Impl::To_S
|
14
|
+
|
15
|
+
# @return [Plan] Plan to which this spot will deliver data events
|
16
|
+
attr_reader :plan
|
17
|
+
|
18
|
+
# @return [Symbol] Name of spot attribute in plan that corresponds to this spot
|
19
|
+
attr_reader :name
|
20
|
+
|
21
|
+
# @return [Plan, nil] Plan of calling spot if any, nil otherwise
|
22
|
+
attr_reader :here
|
23
|
+
|
24
|
+
# @return [Symbol, nil] Spot's destination name, or nil for plan.dest, returned by >>
|
25
|
+
def dest_name ; @dest end
|
26
|
+
|
27
|
+
# @param [Plan] plan Plan to which this spot will deliver data events
|
28
|
+
# @param [Symbol] name Name of spot attribute in plan that corresponds to this spot
|
29
|
+
# @param [Plan, nil] here Plan of calling Spot if any, nil otherwise
|
30
|
+
# @param [Symbol, nil] dest destination name use to obtain return value for >>
|
31
|
+
def initialize(plan, name, here, dest = nil)
|
32
|
+
raise ArgumentError, "#{plan} is not a Plan" unless plan.is_a? Plan
|
33
|
+
raise ArgumentError, "#{name} is not a Symbol" unless name.is_a? Symbol
|
34
|
+
unless plan.meth_spot_name?(name)
|
35
|
+
raise ArgumentError, "#{name} is not a known method spot name of #{plan}"
|
36
|
+
end
|
37
|
+
unless dest.nil? || dest.is_a?(Symbol)
|
38
|
+
raise ArgumentError, "#{dest} is neither nil nor a Symbol"
|
39
|
+
end
|
40
|
+
if !here || here.is_a?(Plan)
|
41
|
+
@plan = plan
|
42
|
+
@name = name
|
43
|
+
@here = here
|
44
|
+
@dest = dest
|
45
|
+
else
|
46
|
+
raise ArgumentError, "#{here} is neither nil nor a Plan"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def cloneable? ; true end
|
51
|
+
def clone_to_copy? ; false end
|
52
|
+
def identical_copy ; self end
|
53
|
+
|
54
|
+
# Spots compare attribute-wise and do not accept subclasses
|
55
|
+
#
|
56
|
+
# @retrun [TrueClass, FalseClass] self == other
|
57
|
+
def ==(other)
|
58
|
+
return true if self.equal? other
|
59
|
+
return false unless other.class.equal? Spot
|
60
|
+
name.eq(other.name) && plan.eq(other.plan) && here.eq(other.here) && dest.eq(other.dest)
|
61
|
+
end
|
62
|
+
|
63
|
+
def hash ; plan.hash ^ name.hash ^ here.hash ^ dest.hash end
|
64
|
+
|
65
|
+
def to_short_s
|
66
|
+
dest_ = dest_name
|
67
|
+
dest_ = if dest_ then " dest=:#{dest_name}" else '' end
|
68
|
+
here_ = here
|
69
|
+
if here_
|
70
|
+
then " plan=#{plan} name=:#{name} here=#{here_}#{dest_}"
|
71
|
+
else " plan=#{plan} name=:#{name}#{dest_}" end
|
72
|
+
end
|
73
|
+
alias_method :inspect, :to_s
|
74
|
+
|
75
|
+
# Post data with the associated tags_in to this's spot's plan's method spot with name name
|
76
|
+
# and hint that the caller requested the spot activation to be executed on track tack
|
77
|
+
#
|
78
|
+
# @param [Track] track requested target track
|
79
|
+
# @param [Any] data any data event
|
80
|
+
# @param [Hash] tags to be passed along
|
81
|
+
#
|
82
|
+
# @return [self]
|
83
|
+
def post_to(track, data, tags_in = {})
|
84
|
+
tags_in = (here.tags.identical_copy.update(tags_in) rescue tags_in) if here
|
85
|
+
plan.post_data self, track, data, tags_in
|
86
|
+
self
|
87
|
+
end
|
88
|
+
|
89
|
+
# @return [Spot] a fresh copy of self for which dest_name == spot_name holds
|
90
|
+
def via(spot_name)
|
91
|
+
raise ArgumentError unless spot_name.nil? || spot_name.is_a?(Symbol)
|
92
|
+
Spot.new plan, name, here, spot_name
|
93
|
+
end
|
94
|
+
|
95
|
+
# Call the spot method associated with this spot with the provided key and val
|
96
|
+
#
|
97
|
+
# Precondition is that here == plan, i.e. the caller already executes in the scope
|
98
|
+
# of this spot's plan
|
99
|
+
#
|
100
|
+
# @return [Any] plan.call_local name, key, val
|
101
|
+
def call_local(key, val)
|
102
|
+
plan.call_local name, key, val
|
103
|
+
end
|
104
|
+
|
105
|
+
# @return [self] for compatibility with plan's API
|
106
|
+
def entry ; self end
|
107
|
+
|
108
|
+
# @return [Spot] plan.public_spot(dest_name) if that exists, plan.dest otherwise
|
109
|
+
def dest
|
110
|
+
if dest_name
|
111
|
+
then plan.public_spot(dest_name)
|
112
|
+
else plan.dest end
|
113
|
+
end
|
114
|
+
|
115
|
+
def >>(target)
|
116
|
+
return (plan >> target) unless dest_name
|
117
|
+
unless plan.attr_spot_name?(dest_name)
|
118
|
+
raise ArgumentError, "#{dest_name} is not an attr_spot_name"
|
119
|
+
end
|
120
|
+
plan.send :"#{dest_name}=", target.entry
|
121
|
+
plan.public_spot(dest_name)
|
122
|
+
end
|
123
|
+
|
124
|
+
# @return [Spot] this spot or a modified copy of it such that here == new_calling_plan holds
|
125
|
+
def intern(new_calling_plan)
|
126
|
+
if here.equal? new_calling_plan
|
127
|
+
then self
|
128
|
+
else Spot.new plan, name, new_calling_plan, dest_name end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Andromeda
|
2
|
+
|
3
|
+
class Plan
|
4
|
+
def pool ; guide.pool_track.pool rescue nil end
|
5
|
+
end
|
6
|
+
|
7
|
+
module Guides
|
8
|
+
|
9
|
+
def self.default ; DefaultGuide.instance end
|
10
|
+
def self.single ; SinglePoolGuide.new end
|
11
|
+
def self.shared_pool ; SharedPoolGuide end
|
12
|
+
def self.pool ; SharedPoolGuide.new end
|
13
|
+
def self.local ; LocalGuide end
|
14
|
+
def self.shared_single ; SharedSinglePoolGuide end
|
15
|
+
|
16
|
+
class DefaultGuide < SimpleDelegator
|
17
|
+
include Singleton
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
super LocalGuide.instance
|
21
|
+
end
|
22
|
+
|
23
|
+
def instance=(new_instance)
|
24
|
+
if new_instance.is_a?(Class) && new_instance.include?(Singleton)
|
25
|
+
new_instance = new_instance.instance
|
26
|
+
end
|
27
|
+
instance.__setobj__ new_instance
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
class DefaultLogger < SimpleDelegator
|
34
|
+
include Singleton
|
35
|
+
|
36
|
+
def initialize
|
37
|
+
super Logger.new(STDERR)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Andromeda
|
2
|
+
|
3
|
+
module Sync
|
4
|
+
|
5
|
+
# Comparable to a join in join calculus, called Sync here to reserve the name Join
|
6
|
+
# for map_reduce.rb
|
7
|
+
#
|
8
|
+
class Sync < Plan
|
9
|
+
|
10
|
+
def initialize(config = {})
|
11
|
+
super config
|
12
|
+
@mutex = Mutex.new
|
13
|
+
@cv = ConditionVariable.new
|
14
|
+
# box value to keep ref after clone
|
15
|
+
@state = [ state_init ]
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def state_init ; {} end
|
21
|
+
|
22
|
+
def state_ready?(state)
|
23
|
+
raise RuntimeException, 'Not implemented'
|
24
|
+
end
|
25
|
+
|
26
|
+
def state_empty?(state, k, chunk) ; state[k].nil? end
|
27
|
+
def state_updated(state, k, chunk) ; state[k] = chunk; state end
|
28
|
+
|
29
|
+
def state_chunk_key(name, state) ; chunk_key(name, state) end
|
30
|
+
|
31
|
+
def run_chunk(pool, scope, name, meth, k, chunk, &thunk)
|
32
|
+
@mutex.synchronize do
|
33
|
+
state = @state[0]
|
34
|
+
while true
|
35
|
+
if state_empty?(state, k, chunk)
|
36
|
+
@state[0] = (state = state_updated(state, k, chunk))
|
37
|
+
if state_ready?(state)
|
38
|
+
@state[0] = state_init
|
39
|
+
cv.signal
|
40
|
+
new_k = state_chunk_key(name, state)
|
41
|
+
return super pool, scope, name, meth, new_k, state, &thunk
|
42
|
+
else
|
43
|
+
cv.signal
|
44
|
+
return self
|
45
|
+
end
|
46
|
+
else
|
47
|
+
cv.wait @mutex
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Passes all input and waits for the associated scope to return to the start value
|
55
|
+
# (will only work if there is no concurrent modification to the associated scope)
|
56
|
+
class ScopeWaiter < Plan
|
57
|
+
|
58
|
+
def on_enter(k, v)
|
59
|
+
scope_ = current_scope
|
60
|
+
value_ = scope_.value
|
61
|
+
super k, v
|
62
|
+
scope_.wait_until_eq value_
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
data/lib/andromeda/version.rb
CHANGED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'yard'
|
2
|
+
|
3
|
+
class MethSpotHandler < YARD::Handlers::Ruby::AttributeHandler
|
4
|
+
handles method_call(:meth_spot)
|
5
|
+
namespace_only
|
6
|
+
|
7
|
+
def process
|
8
|
+
push_state(:scope => :class) { super }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class AttrSpotHandler < YARD::Handlers::Ruby::AttributeHandler
|
13
|
+
handles method_call(:attr_spot)
|
14
|
+
namespace_only
|
15
|
+
|
16
|
+
def process
|
17
|
+
push_state(:scope => :class) { super }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class SignalSpotHandler < YARD::Handlers::Ruby::AttributeHandler
|
22
|
+
handles method_call(:signal_spot)
|
23
|
+
namespace_only
|
24
|
+
|
25
|
+
def process
|
26
|
+
push_state(:scope => :class) { super }
|
27
|
+
end
|
28
|
+
end
|
metadata
CHANGED
@@ -1,18 +1,20 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: andromeda
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.1'
|
5
4
|
prerelease:
|
5
|
+
version: 0.1.2
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Stefan Plantikow
|
9
9
|
autorequire:
|
10
10
|
bindir: script
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-05-01 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
|
-
description:
|
15
|
-
|
14
|
+
description: Andromeda is a light weight framework for complex event processing on
|
15
|
+
multicore architectures. Andromeda users construct networks of plans that are interconnected
|
16
|
+
via endpoint spots, describe how plans are scheduled onto threads, and process data
|
17
|
+
by feeding data events to the resulting structure.
|
16
18
|
email: stefanp@moviepilot.com
|
17
19
|
executables: []
|
18
20
|
extensions: []
|
@@ -21,22 +23,38 @@ files:
|
|
21
23
|
- .gitignore
|
22
24
|
- .rvmrc
|
23
25
|
- AUTHORS
|
26
|
+
- CHANGELOG.md
|
24
27
|
- Gemfile
|
25
28
|
- Gemfile.lock
|
26
29
|
- LICENSE.txt
|
27
30
|
- README.md
|
31
|
+
- ROADMAP.md
|
28
32
|
- Rakefile
|
29
33
|
- andromeda.gemspec
|
30
34
|
- lib/andromeda.rb
|
31
|
-
- lib/andromeda/
|
32
|
-
- lib/andromeda/
|
33
|
-
- lib/andromeda/
|
35
|
+
- lib/andromeda/atom.rb
|
36
|
+
- lib/andromeda/cmd.rb
|
37
|
+
- lib/andromeda/common.rb
|
38
|
+
- lib/andromeda/copy_clone.rb
|
39
|
+
- lib/andromeda/error.rb
|
40
|
+
- lib/andromeda/guide.rb
|
41
|
+
- lib/andromeda/guide_track.rb
|
34
42
|
- lib/andromeda/id.rb
|
35
|
-
- lib/andromeda/
|
36
|
-
- lib/andromeda/
|
37
|
-
- lib/andromeda/
|
43
|
+
- lib/andromeda/impl/atom.rb
|
44
|
+
- lib/andromeda/impl/class_attr.rb
|
45
|
+
- lib/andromeda/impl/proto_plan.rb
|
46
|
+
- lib/andromeda/impl/to_s.rb
|
47
|
+
- lib/andromeda/impl/xor_id.rb
|
48
|
+
- lib/andromeda/kit.rb
|
49
|
+
- lib/andromeda/map_reduce.rb
|
50
|
+
- lib/andromeda/plan.rb
|
51
|
+
- lib/andromeda/pool_guide.rb
|
52
|
+
- lib/andromeda/spot.rb
|
53
|
+
- lib/andromeda/sugar.rb
|
54
|
+
- lib/andromeda/sync.rb
|
38
55
|
- lib/andromeda/version.rb
|
39
56
|
- spec/spec_helper.rb
|
57
|
+
- yard_extensions/andromeda.rb
|
40
58
|
homepage: https://github.com/moviepilot/andromeda
|
41
59
|
licenses:
|
42
60
|
- PUBLIC DOMAIN WITHOUT ANY WARRANTY
|
@@ -58,11 +76,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
58
76
|
version: '0'
|
59
77
|
requirements: []
|
60
78
|
rubyforge_project: andromeda
|
61
|
-
rubygems_version: 1.8.
|
79
|
+
rubygems_version: 1.8.12
|
62
80
|
signing_key:
|
63
81
|
specification_version: 3
|
64
|
-
summary:
|
65
|
-
DSL
|
82
|
+
summary: light weght framework for complex event processing based on a dataflow DSL
|
66
83
|
test_files:
|
67
84
|
- spec/spec_helper.rb
|
68
85
|
has_rdoc:
|
data/lib/andromeda/andromeda.rb
DELETED
@@ -1,225 +0,0 @@
|
|
1
|
-
# TODO
|
2
|
-
# - Turn into separate gem
|
3
|
-
# - Write Tests
|
4
|
-
# - Write fusor for synchronization (extra class that blocks until ready to submit)
|
5
|
-
# - Write docs, add yard support for documenting bases, attrs, etc.
|
6
|
-
# - Make nice slideshow and become very famous and rich. yay!
|
7
|
-
module Andromeda
|
8
|
-
|
9
|
-
module Internal
|
10
|
-
class Transplanting
|
11
|
-
attr_reader :orig
|
12
|
-
|
13
|
-
def initialize(init_opts = nil)
|
14
|
-
@opts = init_opts
|
15
|
-
@orig = self
|
16
|
-
end
|
17
|
-
|
18
|
-
protected
|
19
|
-
|
20
|
-
def transplant(new_opts = nil)
|
21
|
-
return self if @opts == new_opts
|
22
|
-
obj = self.clone
|
23
|
-
obj.instance_variable_set '@opts', new_opts
|
24
|
-
obj
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
class Dest < Internal::Transplanting
|
30
|
-
attr_reader :base
|
31
|
-
attr_reader :meth
|
32
|
-
|
33
|
-
def initialize(base, meth, init_opts = nil)
|
34
|
-
super init_opts
|
35
|
-
raise ArgumentError, "'#{meth}' is not a symbol" unless meth.kind_of?(Symbol)
|
36
|
-
raise ArgumentError, "'#{base}' is not a base" unless base.kind_of?(Base)
|
37
|
-
raise NoMethodError, "'#{base}' does not respond to '#{meth}'" unless base.respond_to?(meth)
|
38
|
-
@base = base
|
39
|
-
@meth = meth
|
40
|
-
end
|
41
|
-
|
42
|
-
def <<(chunk, opts = {}) ; submit chunk, opts ; self end
|
43
|
-
def submit(chunk, opts = {}) ; submit_to nil, chunk, opts end
|
44
|
-
def submit_now(chunk, opts = {}) ; submit_to :local, chunk, opts end
|
45
|
-
|
46
|
-
def submit_to(target_pool, chunk, opts = {})
|
47
|
-
if @opts
|
48
|
-
new_opts = @opts.clone
|
49
|
-
opts.each { |k, v| new_opts[k] = v }
|
50
|
-
else
|
51
|
-
new_opts = opts
|
52
|
-
end
|
53
|
-
new_opts[:scope] ||= Scope.new
|
54
|
-
new_opts[:mark] ||= Id.zero
|
55
|
-
base.transplant(new_opts).process target_pool, new_opts[:scope], self.meth, chunk
|
56
|
-
new_opts
|
57
|
-
end
|
58
|
-
|
59
|
-
def entry ; self end
|
60
|
-
end
|
61
|
-
|
62
|
-
class Base < Internal::Transplanting
|
63
|
-
attr_reader :id
|
64
|
-
attr_reader :opts
|
65
|
-
|
66
|
-
attr_accessor :log
|
67
|
-
attr_accessor :mark
|
68
|
-
attr_accessor :emit
|
69
|
-
attr_accessor :pool
|
70
|
-
|
71
|
-
attr_reader :trace_enter
|
72
|
-
attr_reader :trace_exit
|
73
|
-
|
74
|
-
def initialize(config = {})
|
75
|
-
super config[:init_opts]
|
76
|
-
@id = Id.gen
|
77
|
-
set_from_config init_from_config, config
|
78
|
-
@trace_enter ||= init_trace_hash :enter
|
79
|
-
@trace_exit ||= init_trace_hash :emit
|
80
|
-
@pool ||= init_pool_config
|
81
|
-
end
|
82
|
-
|
83
|
-
def trace=(new_trace)
|
84
|
-
raise ArgumentError, "'#{new_trace}' is not a Hash" unless new_trace.kind_of?(Hash)
|
85
|
-
@trace = new_trace
|
86
|
-
end
|
87
|
-
|
88
|
-
def init_trace_hash(kind) ; {} end
|
89
|
-
def init_pool_config ; nil end
|
90
|
-
def init_from_config ; [:readers, :writers] end
|
91
|
-
|
92
|
-
def log ; @log = Logger.new(STDERR) unless @log ; @log end
|
93
|
-
def mark ; @mark = Id.zero unless @mark ; @mark end
|
94
|
-
|
95
|
-
def on_enter(c)
|
96
|
-
emit << c rescue nil
|
97
|
-
end
|
98
|
-
|
99
|
-
# @param [nil, :local, :spawn, :single, :fifo, :default, :global, #process, #process_base] pool_descr
|
100
|
-
# If nil, uses self.pool as pool_descr and continues. If that is nil, too, defaults to :local.
|
101
|
-
# If #process_base, uses target_pool.process_base(meth) as pool_descr and continues.
|
102
|
-
# If pool_descr is :spawn, uses SpawnPool.default_pool
|
103
|
-
# If pool_descr is :single, uses PoolSupport.new_single_pool
|
104
|
-
# If pool_descr is :fifo, uses PoolSupport.new_fifo_pool
|
105
|
-
# If pool_descr is :global, uses the globally shared PoolSupport.global_pool
|
106
|
-
# If pool_descr is :default uses PoolSupport.new_default_pool
|
107
|
-
# Finally, if #process, runs by calling #process. If :local, runs in current thread.
|
108
|
-
# Otherwise, the behaviour is undefined.
|
109
|
-
def process(pool_descr, scope, meth, chunk)
|
110
|
-
this = self
|
111
|
-
run target_pool(pool_descr, meth), scope, meth, chunk do
|
112
|
-
begin
|
113
|
-
enter_level = trace_enter[meth]
|
114
|
-
exit_level = trace_exit[meth]
|
115
|
-
trace :enter, enter_level, meth, chunk if enter_level
|
116
|
-
send meth, chunk
|
117
|
-
trace :exit, exit_level, meth, chunk if exit_level
|
118
|
-
rescue Exception => e
|
119
|
-
handle_exception meth, chunk, e
|
120
|
-
ensure
|
121
|
-
scope.leave if scope
|
122
|
-
end
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
def check_mark
|
127
|
-
mark = @opts[:mark]
|
128
|
-
raise RuntimeError, 'invalid mark' if mark && !mark.zero?
|
129
|
-
end
|
130
|
-
|
131
|
-
def intern(dest) ; dest.transplant(opts) end
|
132
|
-
|
133
|
-
def dest(name)
|
134
|
-
result = if self.respond_to?(name)
|
135
|
-
then intern self.send(name)
|
136
|
-
else Dest.new self, "on_#{name}".to_sym, opts end
|
137
|
-
raise ArgumentError, "unknown or invalid dest: '#{name}'" unless result.kind_of?(Dest)
|
138
|
-
result
|
139
|
-
end
|
140
|
-
|
141
|
-
def trace(kind, level, method, chunk)
|
142
|
-
log_ = log
|
143
|
-
log_.send level, "TRACE #{id.to_s} :#{kind} :#{method} chunk: '#{chunk}'" if log_
|
144
|
-
end
|
145
|
-
|
146
|
-
protected
|
147
|
-
|
148
|
-
def set_from_config(what, config = {})
|
149
|
-
init_readers = what.include? :readers
|
150
|
-
init_writers = what.include? :writers
|
151
|
-
config.each_pair do |k, v|
|
152
|
-
k = k.to_sym rescue nil
|
153
|
-
if init_writers
|
154
|
-
writer = "#{k}=".to_sym rescue nil
|
155
|
-
if writer && self.respond_to?(writer)
|
156
|
-
then self.send writer, v
|
157
|
-
else instance_variable_set "@#{k}", v if init_readers && self.respond_to?(k) end
|
158
|
-
else
|
159
|
-
instance_variable_set "@#{k}", v if init_readers && self.respond_to?(k)
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
def mark_opts
|
165
|
-
if @mark && !@mark.zero?
|
166
|
-
if @opts[:mark]
|
167
|
-
then @opts[:mark] = @opts[:mark].xor(mark)
|
168
|
-
else @opts[:mark] = mark end
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
def run(pool, scope, meth, chunk, &thunk)
|
173
|
-
scope.enter
|
174
|
-
begin
|
175
|
-
if pool && pool.respond_to?(:process)
|
176
|
-
then pool.process(&thunk)
|
177
|
-
else thunk.call end
|
178
|
-
rescue Exception => e
|
179
|
-
handle_exception meth, chunk, e
|
180
|
-
scope.leave if scope
|
181
|
-
end
|
182
|
-
self
|
183
|
-
end
|
184
|
-
|
185
|
-
def target_pool(pool_descr, meth)
|
186
|
-
pool_descr = pool unless pool_descr
|
187
|
-
case pool_descr
|
188
|
-
when nil then :local
|
189
|
-
when :local then :local
|
190
|
-
when :spawn then SpawnPool.default_pool
|
191
|
-
when :global then PoolSupport.global_pool
|
192
|
-
when :default then PoolSupport.new_default_pool
|
193
|
-
when :single then PoolSupport.new_single_pool
|
194
|
-
when :fifo then PoolSupport.new_fifo_pool
|
195
|
-
else
|
196
|
-
if pool_descr.respond_to(:process_base)
|
197
|
-
then pool_descr.process_base(meth)
|
198
|
-
else pool_descr end
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
def handle_exception(meth, chunk, e)
|
203
|
-
if log
|
204
|
-
trace = ''
|
205
|
-
e.backtrace.each { |s| trace << "\n #{s}" }
|
206
|
-
log.error "Caught '#{e}' when processing chunk: '#{chunk}' via meth: '#{meth}' with backtrace: '#{trace}'"
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
public
|
211
|
-
|
212
|
-
def >>(dest) ; self.emit = dest.entry end
|
213
|
-
|
214
|
-
def drop ; self.emit = nil end
|
215
|
-
|
216
|
-
def entry ; dest(:enter) end
|
217
|
-
alias_method :exit, :emit
|
218
|
-
|
219
|
-
def <<(chunk, opts = {}) ; entry.<< chunk, opts end
|
220
|
-
def submit(chunk, opts = {}) ; entry.submit chunk, opts end
|
221
|
-
def submit_now(chunk, opts = {}) ; entry.submit_now chunk, opts end
|
222
|
-
def submit_to(target_pool, chunk, opts = {}) ; entry.submit_to target_pool, chunk, opts end
|
223
|
-
end
|
224
|
-
end
|
225
|
-
|