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
@@ -6,12 +6,13 @@ class RFlow
6
6
  class Component < ConfigDB
7
7
  include UUIDKeyed
8
8
  include ActiveModel::Validations
9
-
9
+
10
10
  class ComponentInvalid < StandardError; end
11
11
  class ComponentNotFound < StandardError; end
12
12
 
13
13
  serialize :options, Hash
14
14
 
15
+ belongs_to :shard, :primary_key => 'uuid', :foreign_key => 'shard_uuid'
15
16
  has_many :input_ports, :primary_key => 'uuid', :foreign_key => 'component_uuid'
16
17
  has_many :output_ports, :primary_key => 'uuid', :foreign_key => 'component_uuid'
17
18
 
@@ -19,9 +20,7 @@ class RFlow
19
20
  #has_many :input_connections, :through => :input_ports, :source => :input_connections
20
21
  #has_many :output_connections, :through => :output_ports, :source => :output_connection
21
22
 
22
-
23
23
  validates_uniqueness_of :name
24
-
25
24
  end
26
25
  end
27
26
  end
@@ -10,18 +10,18 @@ class RFlow
10
10
  include ActiveModel::Validations
11
11
 
12
12
  serialize :options, Hash
13
-
13
+
14
14
  belongs_to :input_port, :primary_key => 'uuid', :foreign_key => 'input_port_uuid'
15
15
  belongs_to :output_port,:primary_key => 'uuid', :foreign_key => 'output_port_uuid'
16
16
 
17
17
  before_create :merge_default_options!
18
-
18
+
19
19
  validates_uniqueness_of :uuid
20
20
  validates_presence_of :output_port_uuid, :input_port_uuid
21
21
 
22
22
  validate :all_required_options_present
23
23
 
24
-
24
+
25
25
  def all_required_options_present
26
26
  self.class.required_options.each do |option_name|
27
27
  unless self.options.include? option_name.to_s
@@ -30,7 +30,7 @@ class RFlow
30
30
  end
31
31
  end
32
32
 
33
-
33
+
34
34
  def merge_default_options!
35
35
  self.options ||= {}
36
36
  self.class.default_options.each do |option_name, default_value_or_proc|
@@ -43,7 +43,7 @@ class RFlow
43
43
  # used in validations. To be overridden.
44
44
  def self.required_options; []; end
45
45
 
46
-
46
+
47
47
  # Should return a hash of default options, where the keys are
48
48
  # the option names and the values are either default option
49
49
  # values or Procs that take a single connection argument. This
@@ -53,7 +53,7 @@ class RFlow
53
53
 
54
54
  end
55
55
 
56
-
56
+
57
57
  # STI Subclass for ZMQ connections and their required options
58
58
  class ZMQConnection < Connection
59
59
 
@@ -61,15 +61,15 @@ class RFlow
61
61
  {
62
62
  'output_socket_type' => 'PUSH',
63
63
  'output_address' => lambda{|conn| "ipc://rflow.#{conn.uuid}"},
64
- 'output_responsibility' => 'bind',
64
+ 'output_responsibility' => 'connect',
65
65
  'input_socket_type' => 'PULL',
66
66
  'input_address' => lambda{|conn| "ipc://rflow.#{conn.uuid}"},
67
- 'input_responsibility' => 'connect',
67
+ 'input_responsibility' => 'bind',
68
68
  }
69
69
  end
70
70
  end
71
71
 
72
-
72
+
73
73
  # STI Subclass for AMQP connections and their required options
74
74
  class AMQPConnection < Connection
75
75
 
@@ -95,4 +95,3 @@ class RFlow
95
95
  end
96
96
  end
97
97
  end
98
-
@@ -3,11 +3,11 @@ class CreateSettings < ActiveRecord::Migration
3
3
  create_table(:settings, :id => false) do |t|
4
4
  t.string :name, :primary => true
5
5
  t.text :value
6
-
6
+
7
7
  t.timestamps
8
8
  end
9
9
  end
10
-
10
+
11
11
  def self.down
12
12
  drop_table :settings
13
13
  end
@@ -0,0 +1,21 @@
1
+ class CreateShards < ActiveRecord::Migration
2
+ def self.up
3
+ create_table(:shards, :id => false) do |t|
4
+ t.string :uuid, :limit => 36, :primary => true
5
+ t.string :name
6
+ t.integer :count
7
+
8
+ # STI
9
+ t.string :type
10
+
11
+ t.timestamps
12
+ end
13
+
14
+ add_index :shards, :uuid, :unique => true
15
+ add_index :shards, :name, :unique => true
16
+ end
17
+
18
+ def self.down
19
+ drop_table :shards
20
+ end
21
+ end
@@ -6,13 +6,18 @@ class CreateComponents < ActiveRecord::Migration
6
6
  t.boolean :managed, :default => true
7
7
  t.text :specification
8
8
  t.text :options
9
-
9
+
10
+ # UUID version of belongs_to :shard
11
+ t.string :shard_uuid
12
+
10
13
  t.timestamps
11
14
  end
12
15
 
13
16
  add_index :components, :uuid, :unique => true
17
+ add_index :components, :name, :unique => true
18
+ add_index :components, :shard_uuid
14
19
  end
15
-
20
+
16
21
  def self.down
17
22
  drop_table :components
18
23
  end
@@ -7,9 +7,9 @@ class CreatePorts < ActiveRecord::Migration
7
7
  # For STI
8
8
  t.text :type
9
9
 
10
- # UUID version of belongs_to :component
10
+ # UUID version of belongs_to :component
11
11
  t.string :component_uuid
12
-
12
+
13
13
  t.timestamps
14
14
  end
15
15
 
@@ -17,7 +17,7 @@ class CreatePorts < ActiveRecord::Migration
17
17
  add_index :ports, :component_uuid
18
18
  add_index :ports, [:component_uuid, :name], :unique => true
19
19
  end
20
-
20
+
21
21
  def self.down
22
22
  drop_table :ports
23
23
  end
@@ -14,13 +14,13 @@ class CreateConnections < ActiveRecord::Migration
14
14
  t.string :input_port_key, :default => '0'
15
15
 
16
16
  t.text :options
17
-
17
+
18
18
  t.timestamps
19
19
  end
20
20
 
21
21
  add_index :connections, :uuid, :unique => true
22
22
  end
23
-
23
+
24
24
  def self.down
25
25
  drop_table :connections
26
26
  end
@@ -6,7 +6,7 @@ class RFlow
6
6
  class Port < ConfigDB
7
7
  include UUIDKeyed
8
8
  include ActiveModel::Validations
9
-
9
+
10
10
  class PortInvalid < StandardError; end
11
11
 
12
12
  belongs_to :component, :primary_key => 'uuid', :foreign_key => 'component_uuid'
@@ -18,13 +18,12 @@ class RFlow
18
18
  # STI-based classes
19
19
  class InputPort < Port;
20
20
  has_many :input_connections, :class_name => 'RFlow::Configuration::Connection', :primary_key => 'uuid', :foreign_key => 'input_port_uuid'
21
- has_many :output_ports, :through => :connections
21
+ has_many :connections, :class_name => 'RFlow::Configuration::Connection', :primary_key => 'uuid', :foreign_key => 'input_port_uuid'
22
22
  end
23
23
 
24
24
  class OutputPort < Port;
25
25
  has_many :output_connections, :class_name => 'RFlow::Configuration::Connection', :primary_key => 'uuid', :foreign_key => 'output_port_uuid'
26
- has_many :input_ports, :through => :connections
26
+ has_many :connections, :class_name => 'RFlow::Configuration::Connection', :primary_key => 'uuid', :foreign_key => 'output_port_uuid'
27
27
  end
28
28
  end
29
29
  end
30
-
@@ -6,12 +6,14 @@ class RFlow
6
6
  # Ruby DSL config file controller.
7
7
  # TODO: more docs and examples
8
8
  class RubyDSL
9
- attr_accessor :setting_specs, :component_specs, :connection_specs, :allocated_system_ports
10
-
9
+ attr_accessor :setting_specs, :shard_specs, :connection_specs, :allocated_system_ports
10
+
11
11
  def initialize
12
12
  @setting_specs = []
13
- @component_specs = []
13
+ @shard_specs = [{:name => "DEFAULT", :type => :process, :count => 1, :components => []}]
14
14
  @connection_specs = []
15
+
16
+ @current_shard = @shard_specs.first
15
17
  end
16
18
 
17
19
  # Helper function to extract the line of the config that
@@ -19,7 +21,7 @@ class RFlow
19
21
  def get_config_line(call_history)
20
22
  call_history.first.split(':in').first
21
23
  end
22
-
24
+
23
25
  # DSL method to specify a name/value pair. RFlow core uses the
24
26
  # 'rflow.' prefix on all of its settings. Custom settings
25
27
  # should use a custom (unique) prefix
@@ -27,11 +29,28 @@ class RFlow
27
29
  setting_specs << {:name => setting_name.to_s, :value => setting_value.to_s, :config_line => get_config_line(caller)}
28
30
  end
29
31
 
32
+ # DSL method to specify a shard block for either a process or thread
33
+ def shard(shard_name, shard_options={})
34
+ raise ArgumentError, "Cannot use DEFAULT as a shard name" if shard_name == 'DEFAULT'
35
+ shard_type = if shard_options[:thread] || shard_options[:type] == :thread
36
+ :thread
37
+ else
38
+ :process
39
+ end
40
+
41
+ shard_count = shard_options[shard_type] || shard_options[:count] || 1
42
+
43
+ @current_shard = {:name => shard_name, :type => shard_type, :count => shard_count, :components => [], :config_line => get_config_line(caller)}
44
+ @shard_specs << @current_shard
45
+ yield self
46
+ @current_shard = @shard_specs.first
47
+ end
48
+
30
49
  # DSL method to specify a component. Expects a name,
31
50
  # specification, and set of component specific options, that
32
51
  # must be marshallable into the database (i.e. should all be strings)
33
52
  def component(component_name, component_specification, component_options={})
34
- component_specs << {
53
+ @current_shard[:components] << {
35
54
  :name => component_name,
36
55
  :specification => component_specification.to_s, :options => component_options,
37
56
  :config_line => get_config_line(caller)
@@ -55,9 +74,9 @@ class RFlow
55
74
  input_component_name, input_port_name, input_port_key = parse_connection_string(input_string)
56
75
 
57
76
  connection_specs << {
58
- :name => output_string + '=>' + input_string,
77
+ :name => output_string + '=>' + input_string,
59
78
  :output_component_name => output_component_name,
60
- :output_port_name => output_port_name, :output_port_key => output_port_key,
79
+ :output_port_name => output_port_name, :output_port_key => output_port_key,
61
80
  :output_string => output_string,
62
81
  :input_component_name => input_component_name,
63
82
  :input_port_name => input_port_name, :input_port_key => input_port_key,
@@ -76,16 +95,16 @@ class RFlow
76
95
  [matched[1], matched[2], (matched[3] || nil)]
77
96
  end
78
97
 
79
-
98
+
80
99
  # Method to process the 'DSL' objects into the config database
81
100
  # via ActiveRecord
82
101
  def process
83
102
  process_setting_specs
84
- process_component_specs
103
+ process_shard_specs
85
104
  process_connection_specs
86
105
  end
87
106
 
88
-
107
+
89
108
  # Iterates through each setting specified in the DSL and
90
109
  # creates rows in the database corresponding to the setting
91
110
  def process_setting_specs
@@ -95,17 +114,36 @@ class RFlow
95
114
  end
96
115
  end
97
116
 
98
-
99
- # Iterates through each component specified in the DSL and
100
- # creates rows in the database corresponding to the component.
101
- def process_component_specs
102
- component_specs.each do |component_spec|
103
- RFlow.logger.debug "Found component '#{component_spec[:name]}', creating"
104
- RFlow::Configuration::Component.create! :name => component_spec[:name], :specification => component_spec[:specification], :options => component_spec[:options]
117
+
118
+ # Iterates through each shard specified in the DSL and creates
119
+ # rows in the database corresponding to the shard and included
120
+ # components
121
+ def process_shard_specs
122
+ @shard_specs.each do |shard_spec|
123
+ RFlow.logger.debug "Found #{shard_spec[:type]} shard '#{shard_spec[:name]}', creating"
124
+
125
+ shard_class = case shard_spec[:type]
126
+ when :process
127
+ RFlow::Configuration::ProcessShard
128
+ when :thread
129
+ RFlow::Configuration::ThreadShard
130
+ else
131
+ raise RFlow::Configuration::Shard::ShardInvalid, "Invalid shard: #{shard_spec.inspect}"
132
+ end
133
+
134
+ shard = shard_class.create! :name => shard_spec[:name], :count => shard_spec[:count]
135
+
136
+ shard_spec[:components].each do |component_spec|
137
+ RFlow.logger.debug "Shard '#{shard_spec[:name]}' found component '#{component_spec[:name]}', creating"
138
+ RFlow::Configuration::Component.create!(:shard => shard,
139
+ :name => component_spec[:name],
140
+ :specification => component_spec[:specification],
141
+ :options => component_spec[:options])
142
+ end
105
143
  end
106
144
  end
107
145
 
108
-
146
+
109
147
  # Iterates through each component specified in the DSL and uses
110
148
  # 'process_connection' to insert all the parts of the connection
111
149
  # into the database
@@ -121,36 +159,22 @@ class RFlow
121
159
  # ZeroMQ ipc sockets
122
160
  def process_connection_spec(connection_spec)
123
161
  RFlow.logger.debug "Found connection from '#{connection_spec[:output_string]}' to '#{connection_spec[:input_string]}', creating"
124
-
162
+
125
163
  # an input port can be associated with multiple outputs, but
126
164
  # an output port can only be associated with one input
127
165
  output_component = RFlow::Configuration::Component.find_by_name connection_spec[:output_component_name]
128
166
  raise RFlow::Configuration::Component::ComponentNotFound, "#{connection_spec[:output_component_name]}" unless output_component
129
167
  output_port = output_component.output_ports.find_or_initialize_by_name :name => connection_spec[:output_port_name]
130
168
  output_port.save!
131
-
169
+
132
170
  input_component = RFlow::Configuration::Component.find_by_name connection_spec[:input_component_name]
133
171
  raise RFlow::Configuration::Component::ComponentNotFound, "#{connection_spec[:input_component_name]}" unless input_component
134
172
  input_port = input_component.input_ports.find_or_initialize_by_name :name => connection_spec[:input_port_name]
135
173
  input_port.save!
136
174
 
137
- # Create a unique ZMQ address
138
- # zmq_address = "ipc://run/rflow.#{output_component.uuid}.#{output_port.uuid}"
139
- # if connection_spec[:output_port_key]
140
- # zmq_address << ".#{connection_spec[:output_port_key].gsub(/[^\w]/, '').downcase}"
141
- # end
142
-
143
175
  connection = RFlow::Configuration::ZMQConnection.new(:name => connection_spec[:name],
144
176
  :output_port_key => connection_spec[:output_port_key],
145
177
  :input_port_key => connection_spec[:input_port_key])
146
- # :options => {
147
- # 'output_socket_type' => "PUSH",
148
- # 'output_address' => zmq_address,
149
- # 'output_responsibility' => "bind",
150
- # 'input_socket_type' => "PULL",
151
- # 'input_address' => zmq_address,
152
- # 'input_responsibility' => "connect",
153
- # })
154
178
 
155
179
  connection.output_port = output_port
156
180
  connection.input_port = input_port
@@ -170,7 +194,7 @@ class RFlow
170
194
  raise RFlow::Configuration::Connection::ConnectionInvalid, error_message
171
195
  end
172
196
 
173
-
197
+
174
198
  # Method called within the config file itself
175
199
  def self.configure
176
200
  config_file = self.new
@@ -8,19 +8,20 @@ class RFlow
8
8
 
9
9
  include ActiveModel::Validations
10
10
 
11
- set_primary_key :name
11
+ self.primary_key = 'name'
12
+
12
13
  attr_accessible :name, :value
13
-
14
+
14
15
  DEFAULTS = {
15
16
  'rflow.application_name' => 'rflow',
16
-
17
+
17
18
  'rflow.application_directory_path' => '.',
18
19
  'rflow.pid_directory_path' => 'run', #lambda {File.join(Setting['rflow.application_directory_path'], 'run')},
19
20
  'rflow.log_directory_path' => 'log', #lambda {File.join(Setting['rflow.application_directory_path'], 'log')},
20
21
 
21
22
  'rflow.log_file_path' => lambda {File.join(Setting['rflow.log_directory_path'], Setting['rflow.application_name'] + '.log')},
22
23
  'rflow.pid_file_path' => lambda {File.join(Setting['rflow.pid_directory_path'], Setting['rflow.application_name'] + '.pid')},
23
-
24
+
24
25
  'rflow.log_level' => 'INFO',
25
26
  }
26
27
 
@@ -41,7 +42,7 @@ class RFlow
41
42
  #validate :valid_writable_path, :if => :directory_path?
42
43
 
43
44
  # TODO: Think about making this a regex check to pull in other,
44
- # externally-defined settings
45
+ # externally-defined settings
45
46
  def directory_path?
46
47
  DIRECTORY_PATHS.include? self.name
47
48
  end
@@ -51,7 +52,7 @@ class RFlow
51
52
  errors.add :value, "setting '#{self.name}' is not a directory ('#{File.expand_path self.value}')"
52
53
  end
53
54
  end
54
-
55
+
55
56
  def valid_writable_path
56
57
  unless File.writable? self.value
57
58
  errors.add :value, "setting '#{self.name}' is not writable ('#{File.expand_path self.value}')"
@@ -61,7 +62,7 @@ class RFlow
61
62
  def self.[](setting_name)
62
63
  Setting.find(setting_name).value rescue nil
63
64
  end
64
-
65
+
65
66
  end
66
67
  end
67
68
  end
@@ -0,0 +1,24 @@
1
+ require 'active_record'
2
+ require 'rflow/configuration/uuid_keyed'
3
+
4
+ class RFlow
5
+ class Configuration
6
+
7
+ class Shard < ConfigDB
8
+ include UUIDKeyed
9
+ include ActiveModel::Validations
10
+
11
+ class ShardInvalid < StandardError; end
12
+
13
+ has_many :components, :primary_key => 'uuid', :foreign_key => 'shard_uuid'
14
+
15
+ validates_presence_of :name
16
+ validates_uniqueness_of :name
17
+ validates_numericality_of :count, :only_integer => true, :greater_than => 0
18
+ end
19
+
20
+ # STI-based classes
21
+ class ProcessShard < Shard; end
22
+ class ThreadShard < Shard; end
23
+ end
24
+ end