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