rflow 1.3.0 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4f99f280cff598c00599545d4f57e27ee6589b62
4
- data.tar.gz: 2b5933fc4baef7b4ae520f820bb0fa48e5e79b1d
3
+ metadata.gz: a854e3c0cf56feae660eb061c29d9cbaad028a34
4
+ data.tar.gz: ac21f337ed623cb7a0a96c8612dc17f5e1e63bd2
5
5
  SHA512:
6
- metadata.gz: 5c436a2293827ff13ffc5f9b5b7732549e9e3948c261d0f9d19d83c565eb694d93129ab8e0133e6afe99deace85c4f7053ca2de8bc3d8e120139e0adca1960ba
7
- data.tar.gz: 3ab7e7aa970cf600adbdef69bbab0934cf402741c2baf610dcb5b039edddf32b9576e58b49c421d1a8b67cf6529dc0434fcfb1014769f97fe103a921905dcb84
6
+ metadata.gz: d580e43a06b900a9cfcb8cd7372d933b698f5dc28c40909a1a2ed4211c342af497829f44e3699b520499dfd7007e70fc6925c88b8354a63278d6402cd504f5bc
7
+ data.tar.gz: b5d50e712d85913f0052e3a3c3cd5d2c73a4e69280af1cc0d3f92aa1fcc8917181db8a7d5288b1cab17787161543945023f6b170a47cc9d883428197d35173a0
data/.gitignore CHANGED
@@ -7,3 +7,5 @@ pkg/*
7
7
  spec/tmp/
8
8
  *.swp
9
9
  /.vagrant
10
+ .yardoc
11
+ doc/
data/.yardopts CHANGED
@@ -1 +1 @@
1
- --output ./doc --main README.md --files schema/*.avsc lib/**/*.rb bin/*.rb - README.md LICENSE
1
+ --output ./doc --main README.md --files schema/*.avsc lib/**/*.rb bin/*.rb --exclude lib/rflow/configuration/migrations --exclude lib/rflow/configuration/ruby_dsl.rb - README.md LICENSE
@@ -3,14 +3,17 @@
3
3
  VAGRANTFILE_API_VERSION = '2'
4
4
 
5
5
  Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
6
- config.vm.define 'centos62' do |c|
7
- c.vm.box = 'jstoneham/rflow-centos62'
8
- end
9
- config.vm.define 'centos64' do |c|
10
- c.vm.box = 'box-cutter/centos64'
11
- end
12
- config.vm.define 'centos65' do |c|
13
- c.vm.box = 'chef/centos-6.5'
6
+ # config.vm.define 'centos62' do |c|
7
+ # c.vm.box = 'jstoneham/rflow-centos62'
8
+ # end
9
+ # config.vm.define 'centos64' do |c|
10
+ # c.vm.box = 'box-cutter/centos64'
11
+ # end
12
+ # config.vm.define 'centos65' do |c|
13
+ # c.vm.box = 'chef/centos-6.5'
14
+ # end
15
+ config.vm.define 'centos67' do |c|
16
+ c.vm.box = 'boxcutter/centos67'
14
17
  end
15
18
 
16
19
  config.vm.synced_folder '.', '/rflow'
@@ -25,14 +28,14 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
25
28
 
26
29
  # install RPM dependencies for rflow and zeromq
27
30
  config.vm.provision 'shell', privileged: true, inline: <<-EOS
28
- curl -O https://dl.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm
31
+ curl -OL https://dl.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm
29
32
  rpm -ivh epel-release-6-8.noarch.rpm
30
33
  yum -y install libyaml-devel patch libffi-devel glibc-headers autoconf gcc-c++ glibc-devel readline-devel zlib-devel openssl-devel automake libtool bison git sqlite-devel rpm-build libuuid-devel vim
31
34
  EOS
32
35
 
33
36
  # build zeromq as vagrant user
34
37
  config.vm.provision 'shell', privileged: false, inline: <<-EOS
35
- curl -O http://download.zeromq.org/zeromq-3.2.4.tar.gz
38
+ curl -OL https://archive.org/download/zeromq_3.2.4/zeromq-3.2.4.tar.gz
36
39
  rpmbuild -tb zeromq-3.2.4.tar.gz
37
40
  EOS
38
41
 
@@ -44,6 +47,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
44
47
  # set up RVM and bundler
45
48
  config.vm.provision 'shell', privileged: false, inline: <<-EOS
46
49
  rm -f .profile
50
+ gpg2 --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
47
51
  curl -sSL https://get.rvm.io | bash -s stable
48
52
  source .rvm/scripts/rvm
49
53
  rvm install `cat /rflow/.ruby-version`
@@ -11,16 +11,31 @@ require 'rflow/components'
11
11
  require 'rflow/connections'
12
12
  require 'rflow/logger'
13
13
 
14
+ # The RFlow application.
14
15
  class RFlow
15
16
  include Log4r
16
17
 
17
18
  class << self
19
+ # RFlow's logger, whose message will be routed to logs as specified in the configuration.
20
+ # @return [RFlow::Logger]
18
21
  attr_accessor :logger
19
- attr_reader :configuration, :master
22
+
23
+ # RFlow's configuration.
24
+ # @return [RFlow::Configuration]
25
+ attr_reader :configuration
26
+
27
+ # RFlow's master node which oversees all the others.
28
+ # @return [RFlow::Master]
29
+ attr_reader :master
20
30
 
21
31
  RFlow.logger = RFlow::Logger.new({})
22
32
  end
23
33
 
34
+ # Start RFlow running. This is the main programmatic entry point to the application.
35
+ # Pulls in the configuration, sets up logging, and starts the master note.
36
+ #
37
+ # @param config_database_path [String] the path to the SQLite configuration database
38
+ # @param daemonize [boolean] true to fork and daemonize; false to run in the foreground
24
39
  def self.run!(config_database_path = nil, daemonize = false)
25
40
  @config_database_path = config_database_path
26
41
  @daemonize = daemonize
@@ -1,10 +1,14 @@
1
1
  require 'rflow/child_process'
2
2
 
3
3
  class RFlow
4
- # A message broker to mediate messages along a connection.
5
- # The broker runs in a child process and will not return from spawn!.
4
+ # A message broker process to mediate messages along a connection.
5
+ # The broker runs in a child process and will not return from {spawn!}.
6
6
  class Broker < ChildProcess
7
7
  class << self
8
+ # Build the broker from the connection configuration.
9
+ # Only supports {RFlow::Configuration::ZMQStreamer} configurations.
10
+ # @param config [RFlow::Configuration::ZMQStreamer] the connection configuration
11
+ # @return [RFlow::Connections::ZMQStreamer]
8
12
  def build(config)
9
13
  case config.class.name
10
14
  when 'RFlow::Configuration::ZMQStreamer'
@@ -1,11 +1,20 @@
1
1
  require 'fcntl'
2
2
 
3
3
  class RFlow
4
+ # Encapsulates a child process being managed by RFlow.
4
5
  class ChildProcess
5
- attr_reader :pid, :name
6
-
6
+ # The PID of the child process.
7
+ # @return [Fixnum]
8
+ attr_reader :pid
9
+ # The name of the child process.
10
+ # @return [String]
11
+ attr_reader :name
12
+
13
+ # Symbolic constant for SIGINFO as this is only defined on BSD and not in Ruby.
7
14
  SIGINFO = 29
8
15
 
16
+ # @param name [String] process name
17
+ # @param role [String] role to be played by the process, for logging (Master, Broker, etc.)
9
18
  def initialize(name, role = name)
10
19
  @name = name
11
20
  @role = role
@@ -13,7 +22,12 @@ class RFlow
13
22
 
14
23
  # Launch another process to execute the child. The parent
15
24
  # process retains the original worker object (with pid and IPC
16
- # pipe) to allow for process management
25
+ # pipe) to allow for process management. Parent will
26
+ # return once the child starts; child will update its process
27
+ # name, detach from the process group, set up signal handlers, and
28
+ # execute {run_child_process}; when that returns, it will
29
+ # exit with return code 0.
30
+ # @return [void]
17
31
  def spawn!
18
32
  establish_child_pipe
19
33
  drop_database_connections
@@ -26,6 +40,7 @@ class RFlow
26
40
  end
27
41
  end
28
42
 
43
+ protected
29
44
  def run_child_process
30
45
  @child_pipe_w.close
31
46
  register_logging_context
@@ -42,6 +57,11 @@ class RFlow
42
57
 
43
58
  def run_process; end
44
59
 
60
+ public
61
+ # Called when the child process needs to be shut down, before it dies.
62
+ # Clears signal handlers.
63
+ # @param signal [String] SIG*, whichever signal caused the shutdown
64
+ # @return [void]
45
65
  def shutdown!(signal)
46
66
  RFlow.logger.info "Shutting down due to #{signal}"
47
67
  unhandle_signals
@@ -3,22 +3,37 @@ require 'rflow/message'
3
3
  require 'rflow/component/port'
4
4
 
5
5
  class RFlow
6
+ # Parent class for all RFlow components.
6
7
  class Component
7
8
  class << self
8
- # Keep track of available component subclasses
9
+ # Keep track of available component subclasses.
10
+ # @!visibility private
9
11
  def inherited(subclass)
10
12
  RFlow::Configuration.add_available_component(subclass)
11
13
  end
12
14
 
13
- # Define an input port with a given name
15
+ # When declaring your component class, defines an input port with a given
16
+ # name. Will also define a port accessor method named after the port for
17
+ # retrieving it.
18
+ #
19
+ # @param name [String]
20
+ # @return [void]
14
21
  def input_port(name); define_port(defined_input_ports, name); end
15
22
 
16
- # Define an output port with a given name
23
+ # When declaring your component class, defines an output port with a
24
+ # given name. Will also define a port accessor method named after the
25
+ # port for retrieving it.
26
+ #
27
+ # @param name [String]
28
+ # @return [void]
17
29
  def output_port(name); define_port(defined_output_ports, name); end
18
30
 
31
+ # @!visibility private
19
32
  def defined_input_ports; @defined_input_ports ||= {}; end
33
+ # @!visibility private
20
34
  def defined_output_ports; @defined_output_ports ||= {}; end
21
35
 
36
+ # @!visibility private
22
37
  def define_port(collection, name)
23
38
  collection[name.to_s] = true
24
39
 
@@ -32,10 +47,14 @@ class RFlow
32
47
  # specification. This assumes that the specification of a
33
48
  # component is a fully qualified Ruby class that has already
34
49
  # been loaded. It will first attempt to find subclasses of
35
- # RFlow::Component (in the available_components hash) and then
50
+ # {RFlow::Component} (in {Configuration#available_components}) and then
36
51
  # attempt to constantize the specification into a different
37
52
  # class. Future releases will support external (i.e. non-managed
38
- # components), but the current stuff only supports Ruby classes
53
+ # components), but the current stuff only supports Ruby classes.
54
+ #
55
+ # @param worker [Shard::Worker] the worker process for the component to run in
56
+ # @param config [Configuration::Component] the component configuration
57
+ # @return [RFlow::Component] an instance of the component class
39
58
  def build(worker, config)
40
59
  raise NotImplementedError, "Non-managed components not yet implemented for component '#{config.name}' as '#{config.specification}' (#{config.uuid})" unless config.managed?
41
60
 
@@ -74,9 +93,20 @@ class RFlow
74
93
  end
75
94
  end
76
95
 
77
- attr_accessor :uuid, :name
78
- attr_reader :ports, :worker
79
-
96
+ # The UUID of the component.
97
+ # @return [String]
98
+ attr_accessor :uuid
99
+ # The name of the component.
100
+ # @return [String]
101
+ attr_accessor :name
102
+ # Collection of the component's input and output ports.
103
+ # @return [PortCollection]
104
+ attr_reader :ports
105
+ # Reference to the worker process in which this instance of the component is running.
106
+ # @return [Shard::Worker]
107
+ attr_reader :worker
108
+
109
+ # @param args [Hash] supported args are +:name+, +:uuid+, +:worker+
80
110
  def initialize(args = {})
81
111
  @name = args[:name]
82
112
  @uuid = args[:uuid]
@@ -87,16 +117,22 @@ class RFlow
87
117
  self.class.defined_output_ports.each {|name, _| ports << OutputPort.new(self, name: name) }
88
118
  end
89
119
 
120
+ # @!attribute shard [r]
121
+ # Reference to the component's worker process's {Shard}.
122
+ # @return [Shard]
90
123
  def shard; worker.shard if worker; end
91
124
 
92
125
  # Returns a list of connected input ports. Each port will have
93
126
  # one or more keys associated with a particular connection.
127
+ # @return [Array<InputPort>]
94
128
  def input_ports; ports.by_type['RFlow::Component::InputPort']; end
95
129
 
96
130
  # Returns a list of connected output ports. Each port will have
97
131
  # one or more keys associated with the particular connection.
132
+ # @return [Array<OutputPort>]
98
133
  def output_ports; ports.by_type['RFlow::Component::OutputPort']; end
99
134
 
135
+ # @!visibility private
100
136
  def configure_input_port!(port_name, options = {})
101
137
  RFlow.logger.debug "Configuring component '#{name}' (#{uuid}) input port '#{port_name}' (#{options[:uuid]})"
102
138
  unless self.class.defined_input_ports.include? port_name
@@ -105,6 +141,7 @@ class RFlow
105
141
  ports.by_name[port_name].uuid = options[:uuid]
106
142
  end
107
143
 
144
+ # @!visibility private
108
145
  def configure_output_port!(port_name, options = {})
109
146
  RFlow.logger.debug "Configuring component '#{name}' (#{uuid}) output port '#{port_name}' (#{options[:uuid]})"
110
147
  unless self.class.defined_output_ports.include? port_name
@@ -113,6 +150,7 @@ class RFlow
113
150
  ports.by_name[port_name].uuid = options[:uuid]
114
151
  end
115
152
 
153
+ # @!visibility private
116
154
  # Tell the component to establish its ports' connections, i.e. make
117
155
  # the connection. Uses the underlying connection object. Also
118
156
  # establishes the callbacks for each of the input ports
@@ -121,12 +159,16 @@ class RFlow
121
159
  input_ports.each(&:connect!)
122
160
  end
123
161
 
162
+ # @!visibility private
124
163
  # Tell the component to establish its ports' connections, i.e. make
125
164
  # the connection. Uses the underlying connection object.
165
+ # @!visibility private
126
166
  def connect_outputs!
127
167
  output_ports.each(&:connect!)
128
168
  end
129
169
 
170
+ # Pretty-printed version of the component, its ports, their keys, and their connections.
171
+ # @return [String]
130
172
  def to_s
131
173
  string = "Component '#{name}' (#{uuid})\n"
132
174
  ports.each do |port|
@@ -141,29 +183,37 @@ class RFlow
141
183
 
142
184
  # Method that should be overridden by a subclass to provide for
143
185
  # component-specific configuration. The subclass should use the
144
- # self.configuration attribute (@configuration) to store its
145
- # particular configuration. The incoming deserialized_configuration
146
- # parameter is from the RFlow configuration database and is (most
147
- # likely) a hash. Don't assume that the keys are symbols
186
+ # {configuration} attribute (+@configuration+) to store its
187
+ # particular configuration.
188
+ # @param deserialized_configuration [Hash] from the RFlow configuration database; most likely a Hash. Don't assume that the keys are symbols!
189
+ # @return [void]
148
190
  def configure!(deserialized_configuration); end
149
191
 
150
192
  # Main component running method. Subclasses should implement if
151
193
  # they want to set up any EventMachine stuffs (servers, clients,
152
- # etc)
194
+ # etc.).
195
+ # @return [void]
153
196
  def run!; end
154
197
 
155
198
  # Method called when a message is received on an input port.
156
- # Subclasses should implement if they want to receive messages
199
+ # Subclasses should implement if they want to receive messages.
200
+ # @param input_port [RFlow::Component::InputPort] the input port the message was received on
201
+ # @param input_port_key [String] if the message was received on a keyed subport, this is the key
202
+ # @param connection [RFlow::Connection] the connection the message was received on
203
+ # @param message [RFlow::Message] the message itself
204
+ # @return [void]
157
205
  def process_message(input_port, input_port_key, connection, message); end
158
206
 
159
207
  # Method called when RFlow is shutting down. Subclasses should
160
- # implment to terminate any servers/clients (or let them finish)
161
- # and stop sending new data through the flow
208
+ # implement to terminate any servers/clients (or let them finish)
209
+ # and stop sending new data through the flow.
210
+ # @return [void]
162
211
  def shutdown!; end
163
212
 
164
213
  # Method called after all components have been shutdown! and just
165
- # before the global RFlow exit. Sublcasses should implement to
166
- # cleanup any leftover state, e.g. flush file handles, etc
214
+ # before the global RFlow exit. Sublcasses should implement to
215
+ # cleanup any leftover state, e.g. flush file handles, etc.
216
+ # @return [void]
167
217
  def cleanup!; end
168
218
  end
169
219
  end
@@ -3,7 +3,9 @@ class RFlow
3
3
  # TODO: make this into a class to limit the amount of extensions
4
4
  # that we have to do when operating on these 'Arrays', i.e. when
5
5
  # adding two together
6
+ # @!visibility private
6
7
  module ConnectionCollection
8
+ # @!visibility private
7
9
  def send_message(message)
8
10
  each {|connection| connection.send_message(message) }
9
11
  end
@@ -12,7 +14,15 @@ class RFlow
12
14
  # Collection class to make it easier to index by both names
13
15
  # and types.
14
16
  class PortCollection
15
- attr_reader :ports, :by_name, :by_type
17
+ # All the ports in the collection.
18
+ # @return [Array<Port>]
19
+ attr_reader :ports
20
+ # All the ports in the collection, indexed by name.
21
+ # @return [Hash<String, Port>]
22
+ attr_reader :by_name
23
+ # All the ports in the collection, indexed by type ({InputPort}, {OutputPort}).
24
+ # @return [Hash<String, Array<Port>>]
25
+ attr_reader :by_type
16
26
 
17
27
  def initialize
18
28
  @ports = []
@@ -20,6 +30,9 @@ class RFlow
20
30
  @by_type = Hash.new {|hash, key| hash[key.to_s] = []}
21
31
  end
22
32
 
33
+ # Add a port to the collection.
34
+ # @param port [Port] port to add
35
+ # @return [PortCollection] self
23
36
  def <<(port)
24
37
  by_name[port.name.to_s] = port
25
38
  by_type[port.class.to_s] << port
@@ -27,42 +40,65 @@ class RFlow
27
40
  self
28
41
  end
29
42
 
30
- # Enumerate through each port
43
+ # Enumerate through each port, +yield+ing each.
31
44
  # TODO: simplify with enumerators and procs
45
+ # @return [Array<Port>]
32
46
  def each
33
47
  ports.each {|port| yield port }
34
48
  end
35
49
  end
36
50
 
51
+ # An input or output port on a {Component}.
37
52
  class Port
38
- attr_reader :connected, :component
53
+ # True if there are connections to the port.
54
+ # @return [boolean]
55
+ attr_reader :connected
56
+ # The {Component} this port belongs to.
57
+ # @return [Component]
58
+ attr_reader :component
39
59
 
40
60
  def initialize(component)
41
61
  @component = component
42
62
  end
43
63
 
64
+ # Synonym for {connected}.
65
+ # @return [boolean]
44
66
  def connected?; connected; end
45
67
  end
46
68
 
47
- # Stateless class to help with a nicer API
69
+ # Represents a keyed subport on a {Component} - that is, an input or output port
70
+ # that has been subscripted with a port name for subdividing the messages being
71
+ # received or output.
48
72
  class HashSubPort
73
+ # @param hash_port [HashPort] the port to which this subport belongs
74
+ # @param key [String] the key subscript
49
75
  def initialize(hash_port, key)
50
76
  @hash_port = hash_port
51
77
  @key = key
52
78
  end
53
79
 
80
+ # Send a {Message} down all the connections to this subport.
81
+ # @param message [Message]
82
+ # @return [void]
54
83
  def send_message(message)
55
84
  connections.each {|connection| connection.send_message(message) }
56
85
  end
57
86
 
87
+ # Retrieve all the connections for this subport.
88
+ # @return [Array<Connection>]
58
89
  def connections
59
90
  @hash_port.connections_for(@key)
60
91
  end
61
92
 
93
+ # Directly connect this subport to another port.
94
+ # @param other_port [Port] the other port to connect to
95
+ # @return [void]
62
96
  def direct_connect(other_port)
63
97
  @hash_port.direct_connect(@key, other_port)
64
98
  end
65
99
 
100
+ # Enumerate the connections to this subport, +yield+ing each.
101
+ # @return [Array<Connection>]
66
102
  def each
67
103
  connections.each {|connection| yield connection }
68
104
  end
@@ -75,9 +111,16 @@ class RFlow
75
111
  # result in the same message being sent to all indexed
76
112
  # connections.
77
113
  class HashPort < Port
78
- attr_accessor :name, :uuid
114
+ # The name of the port.
115
+ # @return [String]
116
+ attr_accessor :name
117
+ # The UUID of the port.
118
+ # @return [String]
119
+ attr_accessor :uuid
79
120
 
80
121
  public
122
+ # @param component [Component] the component the port belongs to
123
+ # @param args [Hash] supported args are +:uuid+ and +:name+
81
124
  def initialize(component, args = {})
82
125
  super(component)
83
126
  self.uuid = args[:uuid]
@@ -86,17 +129,21 @@ class RFlow
86
129
  end
87
130
 
88
131
  # Get the subport for a given key, which can be used to send messages
89
- # or direct connection
132
+ # or direct connection.
133
+ # @param key [String] the key to subscript with
134
+ # @return [HashSubPort]
90
135
  def [](key)
91
136
  HashSubPort.new(self, key)
92
137
  end
93
138
 
94
- # Returns an Array of all the connections that should
95
- # be sent/received on this subport. Merges the nil-keyed port
139
+ # Returns all the connections that should
140
+ # be sent/received on this subport. Merges the +nil+-keyed port
96
141
  # (i.e. any connections for a port without a key) to those
97
142
  # specific for the key, so should only be used to read a list of
98
- # connections, not to add new ones. Use add_connection to add a
143
+ # connections, not to add new ones. Use {add_connection} to add a
99
144
  # new connection for a given key.
145
+ # @param key [String] the key to subscript with
146
+ # @return [Array<Connection>]
100
147
  def connections_for(key)
101
148
  case key
102
149
  when nil; @connections_for[nil]
@@ -104,20 +151,32 @@ class RFlow
104
151
  end
105
152
  end
106
153
 
107
- # Adds a connection for a given key
154
+ # Adds a connection for a given key.
155
+ # @param key [String] the key to subscript with
156
+ # @param connection [Connection] the connection to add
157
+ # @return [void]
108
158
  def add_connection(key, connection)
109
159
  RFlow.logger.debug "Attaching #{connection.class.name} connection '#{connection.name}' (#{connection.uuid}) to port '#{name}' (#{uuid}), key '#{connection.input_port_key}'"
110
160
  @connections_for[key] << connection
111
161
  @all_connections = nil
112
162
  end
113
163
 
114
- # Removes a connection from a given key
164
+ # Removes a connection from a given key.
165
+ # @param key [String] the key to subscript with
166
+ # @param connection [Connection] the connection to remove
167
+ # @return [void]
115
168
  def remove_connection(key, connection)
116
169
  RFlow.logger.debug "Removing #{connection.class.name} connection '#{connection.name}' (#{connection.uuid}) from port '#{name}' (#{uuid}), key '#{connection.input_port_key}'"
117
170
  @connections_for[key].delete(connection)
118
171
  @all_connections = nil
119
172
  end
120
173
 
174
+ # Collect messages being sent to this port in a {MessageCollectingConnection}
175
+ # for retrieval later, usually for unit testing purposes. +yield+s after
176
+ # establishing the new connection.
177
+ # @param key [String] the key to subscript with
178
+ # @param receiver [Array] array in which to place arriving messages
179
+ # @return [MessageCollectingConnection]
121
180
  def collect_messages(key, receiver)
122
181
  begin
123
182
  connection = RFlow::MessageCollectingConnection.new.tap do |c|
@@ -132,6 +191,12 @@ class RFlow
132
191
  end
133
192
  end
134
193
 
194
+ # Directly connect this port to another port. If it's an input port,
195
+ # forward messages to that input port; if it's an output port,
196
+ # forward messages so they appear to come from that output port.
197
+ # @param key [String] the key to subscript with
198
+ # @param other_port [Port] the port to forward to
199
+ # @return [void]
135
200
  def direct_connect(key = nil, other_port)
136
201
  case other_port
137
202
  when InputPort; add_connection key, ForwardToInputPort.new(other_port)
@@ -140,39 +205,62 @@ class RFlow
140
205
  end
141
206
  end
142
207
 
143
- # Return a list of connected keys
208
+ # A list of connected keys.
209
+ # @return [Array<String>]
144
210
  def keys
145
211
  @connections_for.keys
146
212
  end
147
213
 
214
+ # Enumerate all connections, +yield+ing each.
215
+ # @return [Array<Connection>]
148
216
  def each
149
217
  @connections_for.values.each {|connections| yield connections }
150
218
  end
151
219
 
220
+ # Override in subclasses to actually send messages places.
221
+ # @param message [Message] the message to send
222
+ # @return [void]
152
223
  def send_message(message)
153
224
  raise NotImplementedError, 'Raw ports do not know how to send messages'
154
225
  end
155
226
 
156
- # Should be overridden. Called when it is time to actually
157
- # establish the connection
227
+ # Override in subclasses to handle establishing the connection.
228
+ # @return [void]
158
229
  def connect!; raise NotImplementedError, 'Raw ports do not know which direction to connect'; end
159
230
 
231
+ # Retrieve all connections to the port, regardless of key. The resulting +Array+
232
+ # also supports +send_message(message)+ which will deliver the message on all
233
+ # connections.
234
+ # @return [Array<Connection>]
160
235
  def all_connections
161
236
  @all_connections ||= @connections_for.values.flatten.uniq.extend(ConnectionCollection)
162
237
  end
163
238
  end
164
239
 
240
+ # An actual {Component} input port.
165
241
  class InputPort < HashPort
242
+ # Connect all the input connections, once everything's been set up.
243
+ # @return [void]
166
244
  def connect!
167
245
  @connections_for.each {|key, conns| conns.each {|c| c.connect_input! } }
168
246
  @connected = true
169
247
  end
170
248
 
249
+ # Add and start up a new {Connection}.
250
+ # @param key [String] the key to subscript with
251
+ # @param connection [Connection] the connection to add
252
+ # @return [void]
171
253
  def add_connection(key, connection)
172
254
  super
173
255
  connection.connect_input! if connected?
174
256
  end
175
257
 
258
+ # Once things have been set up, registering the receive callback
259
+ # will set it on all connections, so that when messages are received,
260
+ # they are delivered on all connections with appropriate key and connection
261
+ # information from the context of the connection.
262
+ # @param callback [Proc] the receive callback
263
+ # @return [void]
176
264
  def recv_callback=(callback)
177
265
  @connections_for.each do |key, connections|
178
266
  connections.each do |connection|
@@ -184,12 +272,19 @@ class RFlow
184
272
  end
185
273
  end
186
274
 
275
+ # An actual {Component} output port.
187
276
  class OutputPort < HashPort
277
+ # Connect all the output connections, once everything's been set up.
278
+ # @return [void]
188
279
  def connect!
189
280
  @connections_for.each {|key, conns| conns.each {|c| c.connect_output! } }
190
281
  @connected = true
191
282
  end
192
283
 
284
+ # Add and start up a new {Connection}.
285
+ # @param key [String] the key to subscript with
286
+ # @param connection [Connection] the connection to add
287
+ # @return [void]
193
288
  def add_connection(key, connection)
194
289
  super
195
290
  connection.connect_output! if connected?
@@ -197,6 +292,8 @@ class RFlow
197
292
 
198
293
  # Send a message to all connections on all keys for this port,
199
294
  # but only once per connection.
295
+ # @param message [RFlow::Message] the message to send
296
+ # @return [void]
200
297
  def send_message(message)
201
298
  all_connections.send_message(message)
202
299
  end