rflow 0.0.5 → 1.0.0a1

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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-gemset +1 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +21 -0
  5. data/.yardopts +1 -0
  6. data/Gemfile +5 -1
  7. data/Guardfile +8 -0
  8. data/LICENSE +190 -0
  9. data/NOTES +26 -13
  10. data/README.md +448 -0
  11. data/Rakefile +5 -12
  12. data/bin/rflow +23 -20
  13. data/example/basic_config.rb +2 -2
  14. data/example/basic_extensions.rb +8 -8
  15. data/example/http_config.rb +1 -1
  16. data/example/http_extensions.rb +15 -15
  17. data/lib/rflow.rb +15 -387
  18. data/lib/rflow/component.rb +105 -50
  19. data/lib/rflow/component/port.rb +25 -24
  20. data/lib/rflow/components/raw.rb +4 -4
  21. data/lib/rflow/components/raw/extensions.rb +2 -2
  22. data/lib/rflow/configuration.rb +54 -36
  23. data/lib/rflow/configuration/component.rb +2 -3
  24. data/lib/rflow/configuration/connection.rb +9 -10
  25. data/lib/rflow/configuration/migrations/{20010101000001_create_settings.rb → 20010101000000_create_settings.rb} +2 -2
  26. data/lib/rflow/configuration/migrations/20010101000001_create_shards.rb +21 -0
  27. data/lib/rflow/configuration/migrations/20010101000002_create_components.rb +7 -2
  28. data/lib/rflow/configuration/migrations/20010101000003_create_ports.rb +3 -3
  29. data/lib/rflow/configuration/migrations/20010101000004_create_connections.rb +2 -2
  30. data/lib/rflow/configuration/port.rb +3 -4
  31. data/lib/rflow/configuration/ruby_dsl.rb +59 -35
  32. data/lib/rflow/configuration/setting.rb +8 -7
  33. data/lib/rflow/configuration/shard.rb +24 -0
  34. data/lib/rflow/configuration/uuid_keyed.rb +3 -3
  35. data/lib/rflow/connection.rb +21 -10
  36. data/lib/rflow/connections/zmq_connection.rb +45 -44
  37. data/lib/rflow/logger.rb +67 -0
  38. data/lib/rflow/master.rb +127 -0
  39. data/lib/rflow/message.rb +14 -14
  40. data/lib/rflow/pid_file.rb +84 -0
  41. data/lib/rflow/shard.rb +148 -0
  42. data/lib/rflow/version.rb +1 -1
  43. data/rflow.gemspec +22 -28
  44. data/schema/message.avsc +8 -8
  45. data/spec/fixtures/config_ints.rb +4 -4
  46. data/spec/fixtures/config_shards.rb +30 -0
  47. data/spec/fixtures/extensions_ints.rb +8 -8
  48. data/spec/rflow_component_port_spec.rb +58 -0
  49. data/spec/rflow_configuration_ruby_dsl_spec.rb +148 -0
  50. data/spec/rflow_configuration_spec.rb +4 -4
  51. data/spec/rflow_message_data_raw.rb +2 -2
  52. data/spec/rflow_message_data_spec.rb +6 -6
  53. data/spec/rflow_message_spec.rb +13 -13
  54. data/spec/rflow_spec.rb +294 -71
  55. data/spec/schema_spec.rb +2 -2
  56. data/spec/spec_helper.rb +6 -4
  57. data/temp.rb +21 -21
  58. metadata +56 -65
  59. data/.rvmrc +0 -1
  60. data/README +0 -0
@@ -1,3 +1,5 @@
1
+ require 'ostruct'
2
+
1
3
  require 'rflow/message'
2
4
  require 'rflow/component/port'
3
5
 
@@ -8,7 +10,7 @@ class RFlow
8
10
  RFlow::Configuration.add_available_component(subclass)
9
11
  end
10
12
 
11
-
13
+
12
14
  # The Component class methods used in the creation of a component
13
15
  class << self
14
16
  def defined_input_ports
@@ -23,7 +25,7 @@ class RFlow
23
25
  # Port defintions only have names
24
26
 
25
27
  # TODO: consider class-based UUIDs to identify component types
26
-
28
+
27
29
  # Define an input port with a given name
28
30
  def input_port(port_name)
29
31
  define_port(defined_input_ports, port_name)
@@ -41,89 +43,142 @@ class RFlow
41
43
 
42
44
  # Create the port accessor method based on the port name
43
45
  define_method port_name.to_s.to_sym do
44
- port = ports.by_name[port_name.to_s]
46
+ port = ports.by_name[port_name.to_s]
45
47
  return port if port
46
-
48
+
47
49
  # If the port was not connected, return a port-like object
48
50
  # that can respond/log but doesn't send any data. Note,
49
51
  # it won't be available in the 'by_uuid' collection, as it
50
- # doesn't have a configured instance_uuid
52
+ # doesn't have a configured uuid
51
53
  RFlow.logger.debug "'#{self.name}##{port_name}' not connected, creating a disconnected port"
52
- disconnected_port = DisconnectedPort.new(port_name, 0)
54
+
55
+ disconnected_port = DisconnectedPort.new(OpenStruct.new(:name => port_name, :uuid => 0))
53
56
  ports << disconnected_port
54
57
  disconnected_port
55
58
  end
56
59
  end
60
+
61
+
62
+ # Attempt to instantiate a component described by the config
63
+ # specification. This assumes that the specification of a
64
+ # component is a fully qualified Ruby class that has already
65
+ # been loaded. It will first attempt to find subclasses of
66
+ # RFlow::Component (in the available_components hash) and then
67
+ # attempt to constantize the specification into a different
68
+ # class. Future releases will support external (i.e. non-managed
69
+ # components), but the current stuff only supports Ruby classes
70
+ def build(config)
71
+ if config.managed?
72
+ RFlow.logger.debug "Instantiating component '#{config.name}' as '#{config.specification}' (#{config.uuid})"
73
+ begin
74
+ RFlow.logger.debug RFlow.configuration.available_components.inspect
75
+ instantiated_component = if RFlow.configuration.available_components.include? config.specification
76
+ RFlow.logger.debug "Component found in configuration.available_components['#{config.specification}']"
77
+ RFlow.configuration.available_components[config.specification].new(config)
78
+ else
79
+ RFlow.logger.debug "Component not found in configuration.available_components, constantizing component '#{config.specification}'"
80
+ config.specification.constantize.new(config)
81
+ end
82
+ rescue NameError => e
83
+ error_message = "Could not instantiate component '#{config.name}' as '#{config.specification}' (#{config.uuid}): the class '#{config.specification}' was not found"
84
+ RFlow.logger.error error_message
85
+ raise RuntimeError, error_message
86
+ rescue Exception => e
87
+ error_message = "Could not instantiate component '#{config.name}' as '#{config.specification}' (#{config.uuid}): #{e.class} #{e.message}"
88
+ RFlow.logger.error error_message
89
+ raise RuntimeError, error_message
90
+ end
91
+ else
92
+ error_message = "Non-managed components not yet implemented for component '#{config.name}' as '#{config.specification}' (#{config.uuid})"
93
+ RFlow.logger.error error_message
94
+ raise NotImplementedError, error_message
95
+ end
96
+
97
+ instantiated_component
98
+ end
57
99
  end
58
100
 
59
- attr_reader :instance_uuid
60
- attr_reader :name
61
- attr_reader :configuration
62
- attr_reader :ports
63
-
64
- def initialize(uuid, name=nil, configuration=nil)
65
- @instance_uuid = uuid
66
- @name = name
101
+ attr_reader :config, :uuid, :name, :ports
102
+
103
+ def initialize(config)
104
+ @config = config
105
+ @uuid = config.uuid
106
+ @name = config.name
67
107
  @ports = PortCollection.new
68
- @configuration = configuration
108
+
109
+ configure_ports!
110
+ configure_connections!
111
+ configure!(config.options)
69
112
  end
70
113
 
71
-
114
+
72
115
  # Returns a list of connected input ports. Each port will have
73
116
  # one or more keys associated with a particular connection.
74
117
  def input_ports
75
118
  ports.by_type["RFlow::Component::InputPort"]
76
119
  end
77
120
 
78
-
121
+
79
122
  # Returns a list of connected output ports. Each port will have
80
123
  # one or more keys associated with the particular connection.
81
124
  def output_ports
82
125
  ports.by_type["RFlow::Component::OutputPort"]
83
126
  end
84
127
 
85
-
128
+
86
129
  # Returns a list of disconnected output ports.
87
130
  def disconnected_ports
88
131
  ports.by_type["RFlow::Component::DisconnectedPort"]
89
132
  end
90
-
91
133
 
92
- # TODO: DRY up the following two methods by factoring out into a meta-method
93
-
94
- def configure_input_port!(port_name, port_instance_uuid, port_options={})
95
- unless self.class.defined_input_ports.include? port_name
96
- raise ArgumentError, "Input port '#{port_name}' not defined on component '#{self.class}'"
134
+
135
+ def configure_ports!
136
+ # Send the port configuration to each component
137
+ config.input_ports.each do |input_port_config|
138
+ RFlow.logger.debug "Configuring component '#{name}' (#{uuid}) with input port '#{input_port_config.name}' (#{input_port_config.uuid})"
139
+ configure_input_port!(input_port_config)
97
140
  end
98
- ports << InputPort.new(port_name, port_instance_uuid, port_options)
141
+
142
+ config.output_ports.each do |output_port_config|
143
+ RFlow.logger.debug "Configuring component '#{name}' (#{uuid}) with output port '#{output_port_config.name}' (#{output_port_config.uuid})"
144
+ configure_output_port!(output_port_config)
145
+ end
146
+ end
147
+
148
+
149
+ def configure_input_port!(port_config)
150
+ unless self.class.defined_input_ports.include? port_config.name
151
+ raise ArgumentError, "Input port '#{port_config.name}' not defined on component '#{self.class}'"
152
+ end
153
+ ports << InputPort.new(port_config)
99
154
  end
100
155
 
101
-
102
- def configure_output_port!(port_name, port_instance_uuid, port_options={})
103
- unless self.class.defined_output_ports.include? port_name
104
- raise ArgumentError, "Output port '#{port_name}' not defined on component '#{self.class}'"
156
+
157
+ def configure_output_port!(port_config)
158
+ unless self.class.defined_output_ports.include? port_config.name
159
+ raise ArgumentError, "Output port '#{port_config.name}' not defined on component '#{self.class}'"
105
160
  end
106
- ports << OutputPort.new(port_name, port_instance_uuid, port_options)
161
+ ports << OutputPort.new(port_config)
107
162
  end
108
163
 
109
164
 
110
- # Only supports Ruby types.
111
- # TODO: figure out how to dynamically load the built-in
112
- # connections, or require them at the top of the file and not rely
113
- # on rflow.rb requiring 'rflow/connections'
114
- def configure_connection!(port_instance_uuid, port_key, connection_type, connection_uuid, connection_name=nil, connection_options={})
115
- case connection_type
116
- when 'RFlow::Configuration::ZMQConnection'
117
- connection = RFlow::Connections::ZMQConnection.new(connection_uuid, connection_name, connection_options)
118
- else
119
- raise ArgumentError, "Only ZMQConnections currently supported"
165
+ def configure_connections!
166
+ config.input_ports.each do |input_port_config|
167
+ input_port_config.input_connections.each do |input_connection_config|
168
+ RFlow.logger.debug "Configuring input port '#{input_port_config.name}' (#{input_port_config.uuid}) key '#{input_connection_config.input_port_key}' with #{input_connection_config.type.to_s} connection '#{input_connection_config.name}' (#{input_connection_config.uuid})"
169
+ ports.by_uuid[input_port_config.uuid].add_connection(input_connection_config.input_port_key, Connection.build(input_connection_config))
170
+ end
120
171
  end
121
172
 
122
- ports.by_uuid[port_instance_uuid.to_s].add_connection(port_key, connection)
123
- connection
173
+ config.output_ports.each do |output_port_config|
174
+ output_port_config.output_connections.each do |output_connection_config|
175
+ RFlow.logger.debug "Configuring output port '#{output_port_config.name}' (#{output_port_config.uuid}) key '#{output_connection_config.output_port_key}' with #{output_connection_config.type.to_s} connection '#{output_connection_config.name}' (#{output_connection_config.uuid})"
176
+ ports.by_uuid[output_port_config.uuid].add_connection(output_connection_config.output_port_key, Connection.build(output_connection_config))
177
+ end
178
+ end
124
179
  end
125
180
 
126
-
181
+
127
182
  # Tell the component to establish it's ports' connections, i.e. make
128
183
  # the connection. Uses the underlying connection object. Also
129
184
  # establishes the callbacks for each of the input ports
@@ -141,26 +196,26 @@ class RFlow
141
196
  end
142
197
  end
143
198
  end
144
-
199
+
145
200
  output_ports.each do |output_port|
146
201
  output_port.connect!
147
202
  end
148
203
  end
149
204
 
150
-
205
+
151
206
  def to_s
152
- string = "Component '#{name}' (#{instance_uuid})\n"
207
+ string = "Component '#{name}' (#{uuid})\n"
153
208
  ports.each do |port|
154
209
  port.keys.each do |port_key|
155
210
  port[port_key].each do |connection|
156
- string << "\t#{port.class.to_s} '#{port.name}' (#{port.instance_uuid}) key '#{port_key}' connection '#{connection.name}' (#{connection.instance_uuid})\n"
211
+ string << "\t#{port.class.to_s} '#{port.name}' (#{port.uuid}) key '#{port_key}' connection '#{connection.name}' (#{connection.uuid})\n"
157
212
  end
158
213
  end
159
214
  end
160
215
  string
161
216
  end
162
217
 
163
-
218
+
164
219
  # Method that should be overridden by a subclass to provide for
165
220
  # component-specific configuration. The subclass should use the
166
221
  # self.configuration attribute (@configuration) to store its
@@ -168,7 +223,7 @@ class RFlow
168
223
  # parameter is from the RFlow configuration database and is (most
169
224
  # likely) a hash. Don't assume that the keys are symbols
170
225
  def configure!(deserialized_configuration); end
171
-
226
+
172
227
  # Main component running method. Subclasses should implement if
173
228
  # they want to set up any EventMachine stuffs (servers, clients,
174
229
  # etc)
@@ -187,6 +242,6 @@ class RFlow
187
242
  # before the global RFlow exit. Sublcasses should implement to
188
243
  # cleanup any leftover state, e.g. flush file handles, etc
189
244
  def cleanup!; end
190
-
245
+
191
246
  end # class Component
192
247
  end # class RFlow
@@ -10,7 +10,7 @@ class RFlow
10
10
  connection.send_message(message)
11
11
  end
12
12
  end
13
- end
13
+ end
14
14
 
15
15
  # Collection class to make it easier to index by both names,
16
16
  # UUIDs, and types.
@@ -25,7 +25,7 @@ class RFlow
25
25
  end
26
26
 
27
27
  def <<(port)
28
- by_uuid[port.instance_uuid.to_s] = port
28
+ by_uuid[port.uuid.to_s] = port
29
29
  by_name[port.name.to_s] = port
30
30
  by_type[port.class.to_s] << port
31
31
  ports << port
@@ -42,12 +42,14 @@ class RFlow
42
42
  end
43
43
  end
44
44
  end
45
-
46
45
 
47
- # Bare superclass for (potential) later methods. Currently empty
48
- class Port; end
49
46
 
50
-
47
+ class Port
48
+ attr_reader :connected
49
+ def connected?; @connected; end
50
+ end
51
+
52
+
51
53
  # Allows for a list of connections to be assigned to each port/key
52
54
  # combination. Note that binding an input port to an un-indexed
53
55
  # output port will result in messages from all indexed connections
@@ -55,14 +57,16 @@ class RFlow
55
57
  # result in the same message being sent to all indexed
56
58
  # connections.
57
59
  class HashPort < Port
58
- attr_reader :name, :instance_uuid, :options, :connections_for
59
-
60
- def initialize(name, instance_uuid, options={})
61
- @name = name
62
- @instance_uuid = instance_uuid
60
+ attr_reader :config, :name, :uuid, :connections_for
61
+
62
+ def initialize(config)
63
+ @config = config
64
+ @name = config.name
65
+ @uuid = config.uuid
63
66
  @connections_for = Hash.new {|hash, key| hash[key] = Array.new.extend(ConnectionCollection)}
64
67
  end
65
68
 
69
+
66
70
  # Returns an extended Array of all the connections that should
67
71
  # be sent/received on this port. Merges the nil-keyed port
68
72
  # (i.e. any connections for a port without a key) to those
@@ -79,7 +83,7 @@ class RFlow
79
83
  connections_for[key] << connection
80
84
  end
81
85
 
82
-
86
+
83
87
  # Return a list of connected keys
84
88
  def keys
85
89
  connections_for.keys
@@ -94,13 +98,13 @@ class RFlow
94
98
  end
95
99
  end
96
100
 
97
-
101
+
98
102
  # Send a message to all connections on all keys for this port,
99
103
  # but only once per connection.
100
104
  def send_message(message)
101
105
  all_connections.send_message(message)
102
106
  end
103
-
107
+
104
108
 
105
109
  # Should be overridden. Called when it is time to actually
106
110
  # establish the connection
@@ -113,38 +117,35 @@ class RFlow
113
117
  connections
114
118
  end.flatten.uniq.extend(ConnectionCollection)
115
119
  end
116
-
120
+
117
121
  end
118
122
 
119
-
123
+
120
124
  class InputPort < HashPort
121
125
  def connect!
122
126
  connections_for.each do |port_key, connections|
123
127
  connections.each do |connection|
124
128
  connection.connect_input!
129
+ @connected = true
125
130
  end
126
131
  end
127
132
  end
128
133
  end
129
134
 
130
-
135
+
131
136
  class OutputPort < HashPort
132
137
  def connect!
133
138
  connections_for.each do |port_key, keyed_connections|
134
139
  keyed_connections.each do |connection|
135
140
  connection.connect_output!
141
+ @connected = true
136
142
  end
137
143
  end
138
144
  end
139
145
  end
140
146
 
147
+
141
148
  class DisconnectedPort < HashPort; end
142
-
149
+
143
150
  end
144
151
  end
145
-
146
- __END__
147
-
148
- out[even] -> a
149
- out[odd] -> b
150
- out[nil] -> c
@@ -3,14 +3,14 @@ require 'rflow/components/raw/extensions'
3
3
  class RFlow
4
4
  module Components
5
5
  module Raw
6
-
6
+
7
7
  # Load the schemas
8
8
  SCHEMA_DIRECTORY = ::File.expand_path(::File.join(::File.dirname(__FILE__), '..', '..', '..', 'schema'))
9
-
9
+
10
10
  SCHEMA_FILES = {
11
11
  'raw.avsc' => 'RFlow::Message::Data::Raw',
12
12
  }
13
-
13
+
14
14
  SCHEMA_FILES.each do |file_name, data_type_name|
15
15
  schema_string = ::File.read(::File.join(SCHEMA_DIRECTORY, file_name))
16
16
  RFlow::Configuration.add_available_data_type data_type_name, 'avro', schema_string
@@ -20,7 +20,7 @@ class RFlow
20
20
  RFlow::Configuration.add_available_data_extension('RFlow::Message::Data::Raw',
21
21
  RFlow::Components::Raw::Extensions::RawExtension)
22
22
 
23
-
23
+
24
24
  end
25
25
  end
26
26
  end
@@ -1,9 +1,9 @@
1
1
  class RFlow
2
2
  module Components
3
3
  module Raw
4
-
4
+
5
5
  module Extensions
6
-
6
+
7
7
  module RawExtension
8
8
  def self.extended(base_data)
9
9
  base_data.data_object ||= {'raw' => ''}
@@ -17,13 +17,13 @@ class RFlow
17
17
  # An exception class
18
18
  class ConfigurationInvalid < StandardError; end
19
19
 
20
-
20
+
21
21
  # A class to hold DB config and connection information
22
22
  class ConfigDB < ActiveRecord::Base
23
23
  self.abstract_class = true
24
24
  end
25
-
26
-
25
+
26
+
27
27
  # A collection class for data extensions that supports a naive
28
28
  # prefix-based 'inheritance' on lookup. When looking up a key
29
29
  # with [] all existing keys will be examined to determine if the
@@ -55,10 +55,10 @@ class RFlow
55
55
  def clear
56
56
  @hash.clear
57
57
  end
58
-
58
+
59
59
  end
60
60
 
61
-
61
+
62
62
  class << self
63
63
 
64
64
  # A collection of data types (schemas) indexed by their name and
@@ -119,7 +119,7 @@ class RFlow
119
119
  available_data_extensions.add data_type_name, data_extension
120
120
  end
121
121
 
122
-
122
+
123
123
  # Used when RFlow::Component is subclassed to add another
124
124
  # available component to the list.
125
125
  def self.add_available_component(component)
@@ -141,8 +141,8 @@ class RFlow
141
141
  ConfigDB.establish_connection(:adapter => "sqlite3",
142
142
  :database => config_database_path)
143
143
  end
144
-
145
-
144
+
145
+
146
146
  # Using default ActiveRecord migrations, attempt to migrate the
147
147
  # database to the latest version.
148
148
  def self.migrate_database
@@ -160,7 +160,7 @@ class RFlow
160
160
  load config_file_path
161
161
  end
162
162
 
163
-
163
+
164
164
  # Connect to the configuration database, migrate it to the latest
165
165
  # version, and process a config file if provided.
166
166
  def self.initialize_database(config_database_path, config_file_path=nil)
@@ -172,7 +172,7 @@ class RFlow
172
172
  :database => config_database_path)
173
173
 
174
174
  migrate_database
175
-
175
+
176
176
  expanded_config_file_path = File.expand_path config_file_path if config_file_path
177
177
 
178
178
  working_dir = Dir.getwd
@@ -183,12 +183,14 @@ class RFlow
183
183
  end
184
184
 
185
185
  RFlow.logger.debug "Defaulting non-existing config values"
186
- merge_defaults!
186
+ merge_defaults!
187
187
 
188
188
  Dir.chdir working_dir
189
+
190
+ self.new(config_database_path)
189
191
  end
190
192
 
191
-
193
+
192
194
  # Make sure that the configuration has all the necessary values set.
193
195
  def self.merge_defaults!
194
196
  Setting::DEFAULTS.each do |name, default_value_or_proc|
@@ -202,8 +204,8 @@ class RFlow
202
204
  end
203
205
  end
204
206
  end
205
-
206
-
207
+
208
+
207
209
  attr_accessor :config_database_path
208
210
  attr_accessor :cached_settings
209
211
  attr_accessor :cached_components
@@ -211,19 +213,24 @@ class RFlow
211
213
  attr_accessor :cached_connections
212
214
 
213
215
 
214
- def initialize(config_database_path)
216
+ def initialize(config_database_path=nil)
215
217
  @cached_settings = Hash.new
216
218
  @cached_components = Hash.new
217
219
  @cached_ports = []
218
220
  @cached_connections = []
219
221
 
220
- @config_database_path = config_database_path
221
- self.class.establish_config_database_connection(config_database_path)
222
+ # If there is not a config DB path, assume that an AR
223
+ # conncection has already been established
224
+ if config_database_path
225
+ @config_database_path = config_database_path
226
+ self.class.establish_config_database_connection(config_database_path)
227
+ end
222
228
 
223
229
  # Validate the connected database. TODO: make this more
224
230
  # complete, i.e. validate the various columns
225
231
  begin
226
232
  Setting.first
233
+ Shard.first
227
234
  Component.first
228
235
  Port.first
229
236
  Connection.first
@@ -234,44 +241,54 @@ class RFlow
234
241
  end
235
242
  end
236
243
 
237
-
244
+
238
245
  def to_s
239
246
  string = "Configuration:\n"
247
+
240
248
  settings.each do |setting|
241
249
  string << "Setting: '#{setting.name}' = '#{setting.value}'\n"
242
250
  end
243
- components.each do |component|
244
- string << "Component '#{component.name}' as #{component.specification} (#{component.uuid})\n"
245
- component.output_ports.each do |output_port|
246
- output_port.output_connections.each do |output_connection|
247
- input_port = output_connection.input_port
248
- string << "\tOutputPort '#{output_port.name}' key '#{output_connection.output_port_key}' (#{output_port.uuid}) =>\n"
249
- string << "\t\tConnection '#{output_connection.name}' as #{output_connection.type} (#{output_connection.uuid}) =>\n"
250
- string << "\t\tInputPort '#{input_port.name}' key '#{output_connection.input_port_key}' (#{input_port.uuid}) Component '#{input_port.component.name}' (#{input_port.component.uuid})\n"
251
+
252
+ shards.each do |shard|
253
+ string << "Shard #{shard.name} (#{shard.uuid}), type #{shard.class.name}, count #{shard.count}\n"
254
+ shard.components.each do |component|
255
+ string << " Component '#{component.name}' as #{component.specification} (#{component.uuid})\n"
256
+ component.output_ports.each do |output_port|
257
+ output_port.output_connections.each do |output_connection|
258
+ input_port = output_connection.input_port
259
+ string << " OutputPort '#{output_port.name}' key '#{output_connection.output_port_key}' (#{output_port.uuid}) =>\n"
260
+ string << " Connection '#{output_connection.name}' as #{output_connection.type} (#{output_connection.uuid}) =>\n"
261
+ string << " InputPort '#{input_port.name}' key '#{output_connection.input_port_key}' (#{input_port.uuid}) Component '#{input_port.component.name}' (#{input_port.component.uuid})\n"
262
+ end
251
263
  end
252
264
  end
253
265
  end
254
266
  string
255
267
  end
256
-
268
+
257
269
  # Helper method to access settings with minimal syntax
258
270
  def [](setting_name)
259
271
  Setting.find_by_name(setting_name).value rescue nil
260
272
  end
261
273
 
262
-
274
+ def settings
275
+ Setting.all
276
+ end
277
+
278
+ def shards
279
+ Shard.all
280
+ end
281
+
282
+ def shard(shard_uuid)
283
+ Shard.find_by_uuid shard_uuid
284
+ end
285
+
263
286
  def components
264
287
  Component.all
265
288
  end
266
289
 
267
-
268
- def component(component_instance_uuid)
269
- Component.find_by_uuid component_instance_uuid
270
- end
271
-
272
-
273
- def settings
274
- Setting.all
290
+ def component(component_uuid)
291
+ Component.find_by_uuid component_uuid
275
292
  end
276
293
 
277
294
  def available_components
@@ -282,6 +299,7 @@ end
282
299
 
283
300
  # Load the models
284
301
  require 'rflow/configuration/setting'
302
+ require 'rflow/configuration/shard'
285
303
  require 'rflow/configuration/component'
286
304
  require 'rflow/configuration/port'
287
305
  require 'rflow/configuration/connection'