rflow 1.3.0 → 1.3.1

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.
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