rflow 0.0.5 → 1.0.0a1

Sign up to get free protection for your applications and to get access to all the features.
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'