kb-redstorm 0.6.4
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +74 -0
- data/LICENSE.md +13 -0
- data/README.md +375 -0
- data/Rakefile +11 -0
- data/bin/redstorm +15 -0
- data/examples/native/Gemfile +2 -0
- data/examples/native/cluster_word_count_topology.rb +25 -0
- data/examples/native/exclamation_bolt.rb +21 -0
- data/examples/native/local_exclamation_topology.rb +31 -0
- data/examples/native/local_exclamation_topology2.rb +48 -0
- data/examples/native/local_redis_word_count_topology.rb +69 -0
- data/examples/native/local_word_count_topology.rb +27 -0
- data/examples/native/random_sentence_spout.rb +30 -0
- data/examples/native/split_sentence_bolt.rb +20 -0
- data/examples/native/word_count_bolt.rb +26 -0
- data/examples/shell/resources/splitsentence.py +9 -0
- data/examples/shell/resources/storm.py +206 -0
- data/examples/shell/shell_topology.rb +41 -0
- data/examples/simple/exclamation_bolt.rb +10 -0
- data/examples/simple/exclamation_topology.rb +45 -0
- data/examples/simple/exclamation_topology2.rb +45 -0
- data/examples/simple/kafka_topology.rb +55 -0
- data/examples/simple/random_sentence_spout.rb +21 -0
- data/examples/simple/redis_word_count_topology.rb +61 -0
- data/examples/simple/ruby_version_topology.rb +32 -0
- data/examples/simple/split_sentence_bolt.rb +33 -0
- data/examples/simple/word_count_bolt.rb +19 -0
- data/examples/simple/word_count_topology.rb +38 -0
- data/ivy/settings.xml +11 -0
- data/lib/red_storm.rb +9 -0
- data/lib/red_storm/application.rb +85 -0
- data/lib/red_storm/configuration.rb +16 -0
- data/lib/red_storm/configurator.rb +26 -0
- data/lib/red_storm/environment.rb +41 -0
- data/lib/red_storm/loggable.rb +15 -0
- data/lib/red_storm/proxy/batch_spout.rb +71 -0
- data/lib/red_storm/proxy/bolt.rb +63 -0
- data/lib/red_storm/proxy/proxy_function.rb +48 -0
- data/lib/red_storm/proxy/spout.rb +87 -0
- data/lib/red_storm/simple_bolt.rb +135 -0
- data/lib/red_storm/simple_drpc_topology.rb +87 -0
- data/lib/red_storm/simple_spout.rb +184 -0
- data/lib/red_storm/simple_topology.rb +209 -0
- data/lib/red_storm/topology_launcher.rb +54 -0
- data/lib/red_storm/version.rb +3 -0
- data/lib/tasks/red_storm.rake +272 -0
- data/src/main/redstorm/storm/jruby/JRubyBatchSpout.java +89 -0
- data/src/main/redstorm/storm/jruby/JRubyBolt.java +88 -0
- data/src/main/redstorm/storm/jruby/JRubyProxyFunction.java +59 -0
- data/src/main/redstorm/storm/jruby/JRubyShellBolt.java +26 -0
- data/src/main/redstorm/storm/jruby/JRubyShellSpout.java +26 -0
- data/src/main/redstorm/storm/jruby/JRubySpout.java +107 -0
- metadata +134 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
module RedStorm
|
2
|
+
|
3
|
+
class Configurator
|
4
|
+
attr_reader :config
|
5
|
+
|
6
|
+
def initialize(defaults = {})
|
7
|
+
@config = Backtype::Config.new
|
8
|
+
defaults.each{|attribute, value| @config.put(attribute.to_s, value)}
|
9
|
+
end
|
10
|
+
|
11
|
+
def set(attribute, value)
|
12
|
+
@config.put(attribute.to_s, value)
|
13
|
+
end
|
14
|
+
|
15
|
+
def method_missing(sym, *args)
|
16
|
+
config_method = "set#{self.class.camel_case(sym)}"
|
17
|
+
@config.send(config_method, *args)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def self.camel_case(s)
|
23
|
+
s.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module RedStorm
|
2
|
+
|
3
|
+
LAUNCH_PATH = File.expand_path(File.dirname(__FILE__))
|
4
|
+
JAR_CONTEXT = !!(LAUNCH_PATH =~ /\.jar!\/red_storm$/)
|
5
|
+
|
6
|
+
if JAR_CONTEXT
|
7
|
+
BASE_PATH = File.expand_path(LAUNCH_PATH + '/..')
|
8
|
+
REDSTORM_HOME = BASE_PATH
|
9
|
+
TARGET_PATH = BASE_PATH
|
10
|
+
else
|
11
|
+
BASE_PATH = Dir.pwd
|
12
|
+
REDSTORM_HOME = File.expand_path(LAUNCH_PATH + '/../..')
|
13
|
+
TARGET_PATH = "#{BASE_PATH}/target"
|
14
|
+
end
|
15
|
+
|
16
|
+
unless defined?(SPECS_CONTEXT)
|
17
|
+
GEM_PATH = "#{TARGET_PATH}/gems/"
|
18
|
+
ENV["GEM_PATH"] = GEM_PATH
|
19
|
+
ENV["GEM_HOME"] = GEM_PATH
|
20
|
+
end
|
21
|
+
|
22
|
+
def current_ruby_mode
|
23
|
+
RUBY_VERSION =~ /(\d+\.\d+)(\.\d+)*/
|
24
|
+
raise("unknown Ruby version #{$1}") unless $1 == "1.8" || $1 == "1.9"
|
25
|
+
$1
|
26
|
+
end
|
27
|
+
|
28
|
+
def jruby_mode_token(ruby_version = nil)
|
29
|
+
version_map = {"1.8" => "RUBY1_8", "--1.8" => "RUBY1_8", "1.9" => "RUBY1_9", "--1.9" => "RUBY1_9"}
|
30
|
+
version_map[ruby_version.to_s] || version_map[RedStorm.current_ruby_mode]
|
31
|
+
end
|
32
|
+
|
33
|
+
module_function :current_ruby_mode, :jruby_mode_token
|
34
|
+
|
35
|
+
# puts("*** LAUNCH_PATH=#{LAUNCH_PATH}")
|
36
|
+
# puts("*** JAR_CONTEXT=#{JAR_CONTEXT}")
|
37
|
+
# puts("*** BASE_PATH=#{BASE_PATH}")
|
38
|
+
# puts("*** REDSTORM_HOME=#{REDSTORM_HOME}")
|
39
|
+
# puts("*** TARGET_PATH=#{TARGET_PATH}")
|
40
|
+
# puts("*** GEM_PATH=#{GEM_PATH}") if defined?(GEM_PATH)
|
41
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'java'
|
2
|
+
|
3
|
+
java_import 'storm.trident.operation.TridentCollector'
|
4
|
+
java_import 'backtype.storm.task.TopologyContext'
|
5
|
+
java_import 'storm.trident.spout.IBatchSpout'
|
6
|
+
java_import 'backtype.storm.topology.OutputFieldsDeclarer'
|
7
|
+
java_import 'backtype.storm.tuple.Tuple'
|
8
|
+
java_import 'backtype.storm.tuple.Fields'
|
9
|
+
java_import 'backtype.storm.tuple.Values'
|
10
|
+
java_import 'java.util.Map'
|
11
|
+
module Backtype
|
12
|
+
java_import 'backtype.storm.Config'
|
13
|
+
end
|
14
|
+
|
15
|
+
java_package 'redstorm.proxy'
|
16
|
+
|
17
|
+
# the BatchSpout class is a proxy to the real batch spout to avoid having to deal with all the
|
18
|
+
# Java artifacts when creating a spout.
|
19
|
+
#
|
20
|
+
# The real batch spout class implementation must define these methods:
|
21
|
+
# - open(conf, context, collector)
|
22
|
+
# - emitBatch
|
23
|
+
# - getOutputFields
|
24
|
+
# - ack(batch_id)
|
25
|
+
#
|
26
|
+
# and optionnaly:
|
27
|
+
# - close
|
28
|
+
#
|
29
|
+
|
30
|
+
class BatchSpout
|
31
|
+
java_implements IBatchSpout
|
32
|
+
|
33
|
+
java_signature 'IBatchSpout (String base_class_path, String real_spout_class_name)'
|
34
|
+
def initialize(base_class_path, real_spout_class_name)
|
35
|
+
@real_spout = Object.module_eval(real_spout_class_name).new
|
36
|
+
rescue NameError
|
37
|
+
require base_class_path
|
38
|
+
@real_spout = Object.module_eval(real_spout_class_name).new
|
39
|
+
end
|
40
|
+
|
41
|
+
java_signature 'void open(Map, TopologyContext)'
|
42
|
+
def open(conf, context)
|
43
|
+
@real_spout.open(conf, context)
|
44
|
+
end
|
45
|
+
|
46
|
+
java_signature 'void close()'
|
47
|
+
def close
|
48
|
+
@real_spout.close if @real_spout.respond_to?(:close)
|
49
|
+
end
|
50
|
+
|
51
|
+
java_signature 'void emitBatch(long, TridentCollector)'
|
52
|
+
def emitBatch(batch_id, collector)
|
53
|
+
@real_spout.emit_batch(batch_id, collector)
|
54
|
+
end
|
55
|
+
|
56
|
+
java_signature 'void ack(long)'
|
57
|
+
def ack(batch_id)
|
58
|
+
@real_spout.ack(batch_id)
|
59
|
+
end
|
60
|
+
|
61
|
+
java_signature 'Fields getOutputFields()'
|
62
|
+
def getOutputFields
|
63
|
+
@real_spout.get_output_fields()
|
64
|
+
end
|
65
|
+
|
66
|
+
java_signature 'Map<String, Object> getComponentConfiguration()'
|
67
|
+
def getComponentConfiguration
|
68
|
+
@real_spout.get_component_configuration
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'java'
|
2
|
+
|
3
|
+
java_import 'backtype.storm.task.OutputCollector'
|
4
|
+
java_import 'backtype.storm.task.TopologyContext'
|
5
|
+
java_import 'backtype.storm.topology.IRichBolt'
|
6
|
+
java_import 'backtype.storm.topology.OutputFieldsDeclarer'
|
7
|
+
java_import 'backtype.storm.tuple.Tuple'
|
8
|
+
java_import 'backtype.storm.tuple.Fields'
|
9
|
+
java_import 'backtype.storm.tuple.Values'
|
10
|
+
java_import 'java.util.Map'
|
11
|
+
module Backtype
|
12
|
+
java_import 'backtype.storm.Config'
|
13
|
+
end
|
14
|
+
|
15
|
+
java_package 'redstorm.proxy'
|
16
|
+
|
17
|
+
# the Bolt class is a proxy to the real bolt to avoid having to deal with all the
|
18
|
+
# Java artifacts when creating a bolt.
|
19
|
+
#
|
20
|
+
# The real bolt class implementation must define these methods:
|
21
|
+
# - prepare(conf, context, collector)
|
22
|
+
# - execute(tuple)
|
23
|
+
# - declare_output_fields
|
24
|
+
#
|
25
|
+
# and optionnaly:
|
26
|
+
# - cleanup
|
27
|
+
#
|
28
|
+
class Bolt
|
29
|
+
java_implements IRichBolt
|
30
|
+
|
31
|
+
java_signature 'IRichBolt (String base_class_path, String real_bolt_class_name)'
|
32
|
+
def initialize(base_class_path, real_bolt_class_name)
|
33
|
+
@real_bolt = Object.module_eval(real_bolt_class_name).new
|
34
|
+
rescue NameError
|
35
|
+
require base_class_path
|
36
|
+
@real_bolt = Object.module_eval(real_bolt_class_name).new
|
37
|
+
end
|
38
|
+
|
39
|
+
java_signature 'void prepare(Map, TopologyContext, OutputCollector)'
|
40
|
+
def prepare(conf, context, collector)
|
41
|
+
@real_bolt.prepare(conf, context, collector)
|
42
|
+
end
|
43
|
+
|
44
|
+
java_signature 'void execute(Tuple)'
|
45
|
+
def execute(tuple)
|
46
|
+
@real_bolt.execute(tuple)
|
47
|
+
end
|
48
|
+
|
49
|
+
java_signature 'void cleanup()'
|
50
|
+
def cleanup
|
51
|
+
@real_bolt.cleanup if @real_bolt.respond_to?(:cleanup)
|
52
|
+
end
|
53
|
+
|
54
|
+
java_signature 'void declareOutputFields(OutputFieldsDeclarer)'
|
55
|
+
def declareOutputFields(declarer)
|
56
|
+
@real_bolt.declare_output_fields(declarer)
|
57
|
+
end
|
58
|
+
|
59
|
+
java_signature 'Map<String, Object> getComponentConfiguration()'
|
60
|
+
def getComponentConfiguration
|
61
|
+
@real_bolt.get_component_configuration
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'java'
|
2
|
+
|
3
|
+
|
4
|
+
java_import 'storm.trident.tuple.TridentTuple'
|
5
|
+
|
6
|
+
java_import 'storm.trident.operation.TridentCollector'
|
7
|
+
|
8
|
+
java_import 'java.util.Map'
|
9
|
+
|
10
|
+
java_import 'storm.trident.operation.TridentOperationContext'
|
11
|
+
|
12
|
+
java_import 'storm.trident.operation.Function'
|
13
|
+
|
14
|
+
|
15
|
+
module Backtype
|
16
|
+
java_import 'backtype.storm.Config'
|
17
|
+
end
|
18
|
+
|
19
|
+
java_package 'redstorm.proxy'
|
20
|
+
|
21
|
+
class ProxyFunction
|
22
|
+
java_implements Function
|
23
|
+
|
24
|
+
java_signature 'Function (String base_class_path, String real_class_name)'
|
25
|
+
def initialize(base_class_path, real_class_name)
|
26
|
+
@real = Object.module_eval(real_class_name).new
|
27
|
+
rescue NameError
|
28
|
+
require base_class_path
|
29
|
+
@real = Object.module_eval(real_class_name).new
|
30
|
+
end
|
31
|
+
|
32
|
+
java_signature 'void execute(TridentTuple, TridentCollector)'
|
33
|
+
def execute(_trident_tuple, _trident_collector)
|
34
|
+
@real.execute(_trident_tuple, _trident_collector)
|
35
|
+
end
|
36
|
+
|
37
|
+
java_signature 'void cleanup()'
|
38
|
+
def cleanup()
|
39
|
+
@real.cleanup()
|
40
|
+
end
|
41
|
+
|
42
|
+
java_signature 'void prepare(Map, TridentOperationContext)'
|
43
|
+
def prepare(_map, _trident_operation_context)
|
44
|
+
@real.prepare(_map, _trident_operation_context)
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'java'
|
2
|
+
|
3
|
+
java_import 'backtype.storm.spout.SpoutOutputCollector'
|
4
|
+
java_import 'backtype.storm.task.TopologyContext'
|
5
|
+
java_import 'backtype.storm.topology.IRichSpout'
|
6
|
+
java_import 'backtype.storm.topology.OutputFieldsDeclarer'
|
7
|
+
java_import 'backtype.storm.tuple.Tuple'
|
8
|
+
java_import 'backtype.storm.tuple.Fields'
|
9
|
+
java_import 'backtype.storm.tuple.Values'
|
10
|
+
java_import 'java.util.Map'
|
11
|
+
module Backtype
|
12
|
+
java_import 'backtype.storm.Config'
|
13
|
+
end
|
14
|
+
|
15
|
+
java_package 'redstorm.proxy'
|
16
|
+
|
17
|
+
# the Spout class is a proxy to the real spout to avoid having to deal with all the
|
18
|
+
# Java artifacts when creating a spout.
|
19
|
+
#
|
20
|
+
# The real spout class implementation must define these methods:
|
21
|
+
# - open(conf, context, collector)
|
22
|
+
# - next_tuple
|
23
|
+
# - declare_output_fields
|
24
|
+
#
|
25
|
+
# and optionnaly:
|
26
|
+
# - ack(msg_id)
|
27
|
+
# - fail(msg_id)
|
28
|
+
# - close
|
29
|
+
#
|
30
|
+
|
31
|
+
class Spout
|
32
|
+
java_implements IRichSpout
|
33
|
+
|
34
|
+
java_signature 'IRichSpout (String base_class_path, String real_spout_class_name)'
|
35
|
+
def initialize(base_class_path, real_spout_class_name)
|
36
|
+
@real_spout = Object.module_eval(real_spout_class_name).new
|
37
|
+
rescue NameError
|
38
|
+
require base_class_path
|
39
|
+
@real_spout = Object.module_eval(real_spout_class_name).new
|
40
|
+
end
|
41
|
+
|
42
|
+
java_signature 'void open(Map, TopologyContext, SpoutOutputCollector)'
|
43
|
+
def open(conf, context, collector)
|
44
|
+
@real_spout.open(conf, context, collector)
|
45
|
+
end
|
46
|
+
|
47
|
+
java_signature 'void close()'
|
48
|
+
def close
|
49
|
+
@real_spout.close if @real_spout.respond_to?(:close)
|
50
|
+
end
|
51
|
+
|
52
|
+
java_signature 'void activate()'
|
53
|
+
def activate
|
54
|
+
@real_spout.activate if @real_spout.respond_to?(:activate)
|
55
|
+
end
|
56
|
+
|
57
|
+
java_signature 'void deactivate()'
|
58
|
+
def deactivate
|
59
|
+
@real_spout.deactivate if @real_spout.respond_to?(:deactivate)
|
60
|
+
end
|
61
|
+
|
62
|
+
java_signature 'void nextTuple()'
|
63
|
+
def nextTuple
|
64
|
+
@real_spout.next_tuple
|
65
|
+
end
|
66
|
+
|
67
|
+
java_signature 'void ack(Object)'
|
68
|
+
def ack(msg_id)
|
69
|
+
@real_spout.ack(msg_id) if @real_spout.respond_to?(:ack)
|
70
|
+
end
|
71
|
+
|
72
|
+
java_signature 'void fail(Object)'
|
73
|
+
def fail(msg_id)
|
74
|
+
@real_spout.fail(msg_id) if @real_spout.respond_to?(:fail)
|
75
|
+
end
|
76
|
+
|
77
|
+
java_signature 'void declareOutputFields(OutputFieldsDeclarer)'
|
78
|
+
def declareOutputFields(declarer)
|
79
|
+
@real_spout.declare_output_fields(declarer)
|
80
|
+
end
|
81
|
+
|
82
|
+
java_signature 'Map<String, Object> getComponentConfiguration()'
|
83
|
+
def getComponentConfiguration
|
84
|
+
@real_spout.get_component_configuration
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'java'
|
2
|
+
require 'red_storm/configurator'
|
3
|
+
|
4
|
+
module RedStorm
|
5
|
+
|
6
|
+
class SimpleBolt
|
7
|
+
attr_reader :collector, :context, :config
|
8
|
+
|
9
|
+
# DSL class methods
|
10
|
+
|
11
|
+
def self.log
|
12
|
+
@log ||= Java::OrgApacheLog4j::Logger.getLogger(self.name)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.output_fields(*fields)
|
16
|
+
@fields = fields.map(&:to_s)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.configure(&configure_block)
|
20
|
+
@configure_block = block_given? ? configure_block : lambda {}
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.on_receive(*args, &on_receive_block)
|
24
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
25
|
+
method_name = args.first
|
26
|
+
|
27
|
+
self.receive_options.merge!(options)
|
28
|
+
@on_receive_block = block_given? ? on_receive_block : lambda {|tuple| self.send(method_name || :on_receive, tuple)}
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.on_init(method_name = nil, &on_init_block)
|
32
|
+
@on_init_block = block_given? ? on_init_block : lambda {self.send(method_name || :on_init)}
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.on_close(method_name = nil, &close_block)
|
36
|
+
@close_block = block_given? ? close_block : lambda {self.send(method_name || :on_close)}
|
37
|
+
end
|
38
|
+
|
39
|
+
# DSL instance methods
|
40
|
+
|
41
|
+
def log
|
42
|
+
self.class.log
|
43
|
+
end
|
44
|
+
|
45
|
+
def unanchored_emit(*values)
|
46
|
+
@collector.emit(Values.new(*values))
|
47
|
+
end
|
48
|
+
|
49
|
+
def anchored_emit(tuple, *values)
|
50
|
+
@collector.emit(tuple, Values.new(*values))
|
51
|
+
end
|
52
|
+
|
53
|
+
def ack(tuple)
|
54
|
+
@collector.ack(tuple)
|
55
|
+
end
|
56
|
+
|
57
|
+
def fail(tuple)
|
58
|
+
@collector.fail(tuple)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Bolt proxy interface
|
62
|
+
|
63
|
+
def execute(tuple)
|
64
|
+
output = instance_exec(tuple, &self.class.on_receive_block)
|
65
|
+
if output && self.class.emit?
|
66
|
+
values_list = !output.is_a?(Array) ? [[output]] : !output.first.is_a?(Array) ? [output] : output
|
67
|
+
values_list.each{|values| self.class.anchor? ? anchored_emit(tuple, *values) : unanchored_emit(*values)}
|
68
|
+
@collector.ack(tuple) if self.class.ack?
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def prepare(config, context, collector)
|
73
|
+
@collector = collector
|
74
|
+
@context = context
|
75
|
+
@config = config
|
76
|
+
instance_exec(&self.class.on_init_block)
|
77
|
+
end
|
78
|
+
|
79
|
+
def cleanup
|
80
|
+
instance_exec(&self.class.close_block)
|
81
|
+
end
|
82
|
+
|
83
|
+
def declare_output_fields(declarer)
|
84
|
+
declarer.declare(Fields.new(self.class.fields))
|
85
|
+
end
|
86
|
+
|
87
|
+
def get_component_configuration
|
88
|
+
configurator = Configurator.new
|
89
|
+
configurator.instance_exec(&self.class.configure_block)
|
90
|
+
configurator.config
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
# default noop optional dsl callbacks
|
96
|
+
def on_init; end
|
97
|
+
def on_close; end
|
98
|
+
|
99
|
+
def self.fields
|
100
|
+
@fields ||= []
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.configure_block
|
104
|
+
@configure_block ||= lambda {}
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.on_receive_block
|
108
|
+
@on_receive_block ||= lambda {|tuple| self.send(:on_receive, tuple)}
|
109
|
+
end
|
110
|
+
|
111
|
+
def self.on_init_block
|
112
|
+
@on_init_block ||= lambda {self.send(:on_init)}
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.close_block
|
116
|
+
@close_block ||= lambda {self.send(:on_close)}
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.receive_options
|
120
|
+
@receive_options ||= {:emit => true, :ack => false, :anchor => false}
|
121
|
+
end
|
122
|
+
|
123
|
+
def self.emit?
|
124
|
+
!!self.receive_options[:emit]
|
125
|
+
end
|
126
|
+
|
127
|
+
def self.ack?
|
128
|
+
!!self.receive_options[:ack]
|
129
|
+
end
|
130
|
+
|
131
|
+
def self.anchor?
|
132
|
+
!!self.receive_options[:anchor]
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|