smith 0.6.12 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f8f041091471b55b270ce604ec0da4536547c9ae
4
- data.tar.gz: 69b0a8bc99dd6d0f27c7e6b7bec3aa4859498eec
3
+ metadata.gz: 8f1a64e720f6d09734b66a3bf1f39b56ec81aed8
4
+ data.tar.gz: baafcf0aa8e1f048ea9ef250adc8475d8aa53408
5
5
  SHA512:
6
- metadata.gz: 7cdd21628c98edff2dc2441b0d9346916b4db23f6a95ddd147c933d98796f695e1000f72aba258cfd8ca357e027bec4512ebb38ebe1b1827a21c550de847730a
7
- data.tar.gz: 11ca393aab7304966e121bd4343a476d3fbdd414a9e2f255c4c6af33b7e98f88f19940e2b5966391eed578110de6ac35a490abca2d901003c81e4b235d9e5cf1
6
+ metadata.gz: afca0d267739a592b25876133fa70fd0f2f03f962e22a3a4af35efa67b28cb9b47068526142e707db7afa7a496734c8f396c8b884ec0d3295c1706a5f7ab7f94
7
+ data.tar.gz: 0365407a67ab65dc32d41bbf91b316db1dde05851f6191df1a49bfcba611246efc09291f06abba2286c499f60f0807c484226ec5314b6fc315d773b712279a41
@@ -0,0 +1,62 @@
1
+ [smith]
2
+ timeout = 8
3
+ namespace = "smith"
4
+
5
+ [agent]
6
+ monitor = false
7
+ singleton = true
8
+ metadata = ""
9
+ prefetch = 1
10
+
11
+ # Specify the VM that an agent should use.
12
+ [vm]
13
+ agent_default = "ruby"
14
+
15
+ [eventmachine]
16
+ epoll = true
17
+ kqueue = true
18
+
19
+ [agency]
20
+ # The the agency pid path. This can be overriden on the command line.
21
+ pid_directory = "~/.smith/run"
22
+ cache_directory = "~/.smith/cache"
23
+
24
+ # It would be better to use inline tables here but there is a bug in
25
+ # toml-rb: https://github.com/emancu/toml-rb/issues/57.
26
+ # TODO: Change these to inline tables when this bug is fixed.
27
+ [amqp.exchange]
28
+ durable = true
29
+ auto_delete = false
30
+
31
+ [amqp.queue]
32
+ durable = true
33
+ auto_delete = false
34
+
35
+ [amqp.pop]
36
+ ack = true
37
+
38
+ [amqp.publish.headers]
39
+ # headers = {}},
40
+
41
+ [amqp.subscribe]
42
+ ack = true
43
+
44
+ # Change according to your local broker.
45
+ [amqp.broker]
46
+ host = "localhost"
47
+ port = 5672
48
+ user = "guest"
49
+ password = "guest"
50
+ vhost = "/"
51
+
52
+ [logging]
53
+ trace = true
54
+ level = "debug"
55
+
56
+ # Log pattern. Note you need to escape backslashes.
57
+ default_pattern = "%d [%5p] %7l - %34c:%-3L - %m\n"
58
+ default_date_pattern = "%Y/%m/%d %H:%M:%S.%3N"
59
+
60
+ # This can be either: stderr, stdout, file, rollingfile or syslog
61
+ [logging.appender]
62
+ type = "stderr"
@@ -1,9 +1,4 @@
1
1
  # -*- encoding: utf-8 -*-
2
-
3
- $LOAD_PATH.delete_if { |p| p.to_s =~ /eventmachine-\d/ }
4
-
5
- require 'eventmachine-le'
6
-
7
2
  require 'amqp'
8
3
  require 'tmpdir'
9
4
  require "socket"
@@ -17,9 +12,10 @@ require 'extlib/string'
17
12
  require 'extlib/inflection'
18
13
  require 'daemons/pidfile'
19
14
 
20
- require_relative 'smith/config'
21
- require_relative 'smith/logger'
22
- require_relative 'smith/acl_compiler'
15
+ require 'smith/config'
16
+ require 'smith/utils'
17
+ require 'smith/logger'
18
+ require 'smith/acl_compiler'
23
19
 
24
20
  MultiJson.use(:oj)
25
21
 
@@ -49,8 +45,8 @@ module Smith
49
45
  Pathname.new(__FILE__).dirname.parent.expand_path
50
46
  end
51
47
 
52
- def agent_paths
53
- path_to_pathnames(config.agency.agent_path)
48
+ def agent_directories
49
+ config.agency.agent_directories
54
50
  end
55
51
 
56
52
  # Convenience method to get the hostname
@@ -58,17 +54,17 @@ module Smith
58
54
  Socket.gethostname
59
55
  end
60
56
 
61
- def acl_path
62
- path_to_pathnames(config.agency.acl_path)
57
+ def acl_directories
58
+ config.agency.acl_directories
63
59
  end
64
60
 
65
- def cache_path
66
- Pathname.new(config.agency.cache_path).expand_path
61
+ def cache_directory
62
+ Utils.check_and_create_directory(config.agency.cache_directory)
67
63
  end
68
64
 
69
65
  # Return the acl cache path.
70
- def acl_cache_path
71
- @acl_cache_path = Pathname.new(Smith.config.agency.acl_cache_path).tap do |path|
66
+ def acl_cache_directory
67
+ cache_directory.join('acl').tap do |path|
72
68
  check_path(path, true)
73
69
  end
74
70
  end
@@ -94,8 +90,6 @@ module Smith
94
90
  EM.kqueue
95
91
  end
96
92
 
97
- EM.set_descriptor_table_size(Smith.config.eventmachine.file_descriptors)
98
-
99
93
  connection_settings = config.amqp.broker.merge(
100
94
  :on_tcp_connection_failure => method(:tcp_connection_failure_handler),
101
95
  :on_possible_authentication_failure => method(:authentication_failure_handler))
@@ -185,14 +179,6 @@ module Smith
185
179
  EM.stop
186
180
  end
187
181
 
188
- def path_to_pathnames(path)
189
- path ||= []
190
- path.split(':').map do |path|
191
- p = Pathname.new(path)
192
- ((p.absolute?) ? p : root_path.join(p)).tap { |path| check_path(path) }
193
- end
194
- end
195
-
196
182
  def broker_identifier(connection)
197
183
  broker = connection.broker.properties
198
184
  "#{connection.broker_endpoint}, (#{broker['product']}/v#{broker['version']})"
@@ -202,10 +188,10 @@ module Smith
202
188
  unless path.exist?
203
189
  error_message = "Path does not exist: #{path}"
204
190
  if create
205
- logger.info { "Path does not exist: #{path}. Creating" }
191
+ logger.info { "#{error_message}. Creating" }
206
192
  path.mkpath
207
193
  else
208
- logger.warn { "Path does not exist: #{path}" }
194
+ logger.warn { error_message }
209
195
  end
210
196
  end
211
197
  end
@@ -213,7 +199,6 @@ module Smith
213
199
  end
214
200
  end
215
201
 
216
- require 'smith/utils'
217
202
  require 'smith/self_pipe'
218
203
  require 'smith/amqp_errors'
219
204
  require 'smith/object_count'
@@ -15,9 +15,9 @@ module Smith
15
15
  end
16
16
 
17
17
  def compile
18
- $LOAD_PATH << Smith.acl_cache_path
18
+ $LOAD_PATH << Smith.acl_cache_directory
19
19
 
20
- Smith.acl_path.each do |path|
20
+ Smith.acl_directories.each do |path|
21
21
  $LOAD_PATH << path
22
22
 
23
23
  acl_files = path_glob(path)
@@ -41,7 +41,7 @@ module Smith
41
41
 
42
42
  unless acls.empty?
43
43
  Dir.chdir(path) do
44
- cmd = %Q{sh -c 'protoc --ruby_out=#{Smith.acl_cache_path} -I #{path} #{out_of_date_acls.map(&:to_s).join(' ')} 2>&1'}
44
+ cmd = %Q{sh -c 'protoc --ruby_out=#{Smith.acl_cache_directory} -I #{path} #{out_of_date_acls.map(&:to_s).join(' ')} 2>&1'}
45
45
  protoc = IO.popen(cmd)
46
46
  output = protoc.read
47
47
  protoc.close
@@ -62,7 +62,7 @@ module Smith
62
62
 
63
63
  # Returns true if the .proto file is newer that the .pb.rb file
64
64
  def should_compile?(file)
65
- cached_file = Smith.acl_cache_path.join(file.basename).sub_ext(".pb.rb")
65
+ cached_file = Smith.acl_cache_directory.join(file.basename).sub_ext(".pb.rb")
66
66
  if cached_file.exist?
67
67
  file.mtime > cached_file.mtime
68
68
  else
@@ -109,7 +109,7 @@ module Smith
109
109
  end
110
110
 
111
111
  def acl_compiled_path(path)
112
- "#{Smith.acl_cache_path.join(path.basename('.proto'))}.pb.rb"
112
+ "#{Smith.acl_cache_directory.join(path.basename('.proto'))}.pb.rb"
113
113
  end
114
114
  end
115
115
  end
@@ -116,7 +116,7 @@ module Smith
116
116
  # multiple agents are allowed.
117
117
  def options(opts)
118
118
  opts.each do |k, v|
119
- Smith.config.agent.send("#{k}=", v)
119
+ Smith.config.agent[k] = v
120
120
  end
121
121
  end
122
122
  end
@@ -1,5 +1,12 @@
1
1
  # -*- encoding: utf-8 -*-
2
- require 'gdbm'
2
+
3
+ begin
4
+ require 'gdbm'
5
+ rescue LoadError => e
6
+ STDERR.puts "\nYou instance of ruby wasn't compiled with gdbm support.\nSee: https://github.com/filterfish/smith2/wiki/gdbm\n\n"
7
+ raise
8
+ end
9
+
3
10
  require 'securerandom'
4
11
 
5
12
  module Smith
@@ -10,7 +17,7 @@ module Smith
10
17
  attr_accessor :path
11
18
 
12
19
  def initialize(opts={})
13
- @db = GDBM.new(Smith.cache_path.join('agent_state.gdbm').to_s, 0600, GDBM::WRCREAT | GDBM::SYNC)
20
+ @db = GDBM.new(Smith.cache_directory.join('agent_state.gdbm').to_s, 0600, GDBM::WRCREAT | GDBM::SYNC)
14
21
  end
15
22
 
16
23
  def create(name)
@@ -133,7 +133,7 @@ module Smith
133
133
  end
134
134
 
135
135
  def exists?
136
- agent_path(name)
136
+ agent_directories(name)
137
137
  end
138
138
 
139
139
  def to_s
@@ -190,7 +190,7 @@ module Smith
190
190
 
191
191
  bootstrapper = Pathname.new(__FILE__).dirname.join('bootstrap.rb').expand_path
192
192
 
193
- binary = Smith.config.ruby[agent_process.name]
193
+ binary = Smith.config.vm[agent_process.name.snake_case.to_sym] || Smith.config.vm.agent_default
194
194
  logger.debug { "Launching #{agent_process.name} with: #{binary}" }
195
195
  exec(binary, bootstrapper.to_s, agent_process.name, agent_process.uuid)
196
196
  end
@@ -40,7 +40,7 @@ module Smith
40
40
  end
41
41
 
42
42
  def load_agent
43
- path = agent_path(@agent_name)
43
+ path = agent_directories(@agent_name)
44
44
  logger.debug { "Loading #{@agent_name} from: #{path.dirname}" }
45
45
  add_agent_load_path(path)
46
46
  load path
@@ -92,6 +92,7 @@ module Smith
92
92
 
93
93
  private
94
94
 
95
+ # FIXME This really should be using Smith::Daemon
95
96
  def write_pid_file
96
97
  @pid = Daemons::PidFile.new(Daemons::Pid.dir(:normal, Dir::tmpdir, nil), ".smith-#{@agent_uuid}", true)
97
98
  @pid.pid = Process.pid
@@ -10,10 +10,10 @@ module Smith
10
10
  # FIXME make sure that if the path doesn't exist don't blow up.
11
11
  separator = (options[:one_column]) ? "\n" : " "
12
12
 
13
- Smith.agent_paths.inject([]) do |path_acc,path|
13
+ Smith.agent_directories.inject([]) do |path_acc, path|
14
14
  path_acc.tap do |a|
15
15
  if path.exist?
16
- a << path.each_child.inject([]) do |agent_acc,p|
16
+ a << path.each_child.inject([]) do |agent_acc, p|
17
17
  agent_acc.tap do |b|
18
18
  b << Extlib::Inflection.camelize(p.basename('.rb')) if p.file? && p.basename('.rb').to_s.end_with?("agent")
19
19
  end
@@ -29,8 +29,8 @@ module Smith
29
29
  a = (target.empty?) ? a : a.select {|z| target.detect {|y| z.name == y } }.flatten
30
30
  if options[:long_given]
31
31
  tabulate(long_format(a), :header => "total #{a.count}")
32
- elsif options[:one_column_given]
33
- short_format(a, "\n")
32
+ elsif options[:name_only]
33
+ name_only(a, "\n")
34
34
  else
35
35
  short_format(a)
36
36
  end
@@ -46,6 +46,11 @@ module Smith
46
46
  a.map { |a| [a.uuid] }.join(sep)
47
47
  end
48
48
 
49
+
50
+ def name_only(a, sep=' ')
51
+ a.map { |a| [a.name] }.join(sep)
52
+ end
53
+
49
54
  def format_time(t)
50
55
  (t) ? t.strftime("%Y/%m/%d %H:%M:%S") : ''
51
56
  end
@@ -61,12 +66,12 @@ module Smith
61
66
  def options_spec
62
67
  banner "List the running agents."
63
68
 
64
- opt :long, "the number of times to send the message", :short => :l
69
+ opt :long, "shows full details of running agents", :short => :l
65
70
  opt :group, "list only agents in this group", :type => :string, :short => :g
66
- opt :one_column, "the number of times to send the message", :short => :s
67
- opt :all, "show all agents in all states", :short => :a
71
+ opt :name_only, "list on the agents' name", :short => :n
72
+ opt :all, "list all agents in all states", :short => :a
68
73
 
69
- conflicts :one_column, :long
74
+ conflicts :name_only, :long
70
75
  end
71
76
  end
72
77
  end
@@ -3,7 +3,7 @@ module Smith
3
3
  module Commands
4
4
  module Common
5
5
  def agent_group(group)
6
- agents = Smith.agent_paths.map do |path|
6
+ agents = Smith.agent_directories.map do |path|
7
7
  group_dir = path.join(group)
8
8
  if group_dir.exist? && group_dir.directory?
9
9
  agents = Pathname.glob(group_dir.join("*_agent.rb"))
@@ -17,9 +17,9 @@ module Smith
17
17
  else
18
18
  target.map do |acl|
19
19
  if options[:source_given]
20
- acls = find_acl(Smith.acl_cache_path, acl, 'pb.rb')
20
+ acls = find_acl(Smith.acl_cache_directory, acl, 'pb.rb')
21
21
  else
22
- acls = find_acl(Smith.acl_path, acl, 'proto')
22
+ acls = find_acl(Smith.acl_directories, acl, 'proto')
23
23
  end
24
24
 
25
25
  case acls.length
@@ -37,7 +37,7 @@ module Smith
37
37
  end.join("\n")
38
38
  end
39
39
  elsif options[:clean_given]
40
- Pathname.glob(Smith.acl_cache_path.join("*.pb.rb")).each {|p| p.unlink}
40
+ Pathname.glob(Smith.acl_cache_directory.join("*.pb.rb")).each {|p| p.unlink}
41
41
  ""
42
42
  elsif options[:compile_given]
43
43
  Pathname.glob(Smith.compile_acls)
@@ -46,7 +46,7 @@ module Smith
46
46
  join_string = (options[:long]) ? "\n" : " "
47
47
  acl_type_cache.dump_types.keys.map(&:to_s).sort.join(join_string)
48
48
 
49
- # Pathname.glob(Smith.acl_path.map {|p| "#{p}#{File::SEPARATOR}*"}).map do |p|
49
+ # Pathname.glob(Smith.acl_directories.map {|p| "#{p}#{File::SEPARATOR}*"}).map do |p|
50
50
  # p.basename(".proto")
51
51
  # end.sort.join(join_string)
52
52
  end
@@ -54,10 +54,10 @@ module Smith
54
54
 
55
55
  private
56
56
 
57
- def find_acl(path, acl, ext)
58
- [path].flatten.inject([]) do |a,path|
57
+ def find_acl(directories, acl, ext)
58
+ [directories].flatten.inject([]) do |a, directory|
59
59
  a.tap do |acc|
60
- acl_file = path.join("#{acl.snake_case}.#{ext}")
60
+ acl_file = directory.join("#{acl.snake_case}.#{ext}")
61
61
  acc << acl_file if acl_file.exist?
62
62
  end
63
63
  end
@@ -1,44 +1,22 @@
1
1
  #!/usr/bin/env ruby
2
2
  # -*- encoding: utf-8 -*-
3
3
 
4
+ require 'toml'
5
+ require 'fileutils'
4
6
  require 'pathname'
7
+ require 'hashie/extensions/coercion'
8
+ require 'hashie/extensions/deep_merge'
9
+ require 'hashie/extensions/method_access'
10
+ require 'hashie/extensions/merge_initializer'
5
11
 
6
12
  module Smith
7
13
 
8
14
  class ConfigNotFoundError < IOError; end
15
+ class MissingConfigItemError < StandardError; end
9
16
 
10
17
  class Config
11
18
 
12
- CONFIG_FILENAME = '.smithrc'
13
-
14
- attr_accessor :agent, :agency, :amqp, :logging, :smith, :eventmachine, :smith, :ruby
15
-
16
- to_hash = proc do
17
- def to_hash
18
- Hash[*members.zip(values).flatten]
19
- end
20
-
21
- def merge(h)
22
- to_hash.merge(h)
23
- end
24
-
25
- def has_key?(k)
26
- to_hash.has_key?(k)
27
- end
28
- end
29
-
30
- Struct.new("Agent", :monitor, :singleton, :metadata, :prefetch, &to_hash)
31
- Struct.new("Agency", :cache_path, :agent_path, :acl_path, :acl_cache_path, :pid_dir, &to_hash)
32
- Struct.new("AmqpOpts", :durable, :auto_delete, &to_hash)
33
- Struct.new("Broker", :host, :port, :user, :password, :vhost, &to_hash)
34
- Struct.new("Subscribe", :ack, &to_hash)
35
- Struct.new("Pop", :ack, &to_hash)
36
- Struct.new("Publish", :headers, &to_hash)
37
- Struct.new("Amqp", :broker, :exchange, :queue, :publish, :subscribe, :pop, &to_hash)
38
- Struct.new("Appender", :type, :filename, &to_hash)
39
- Struct.new("Logging", :trace, :level, :default_pattern, :default_date_pattern, :appender, :filetype, :vhost, &to_hash)
40
- Struct.new("Smith", :namespace, :timeout, &to_hash)
41
- Struct.new("Eventmachine", :file_descriptors, :epoll, :kqueue, &to_hash)
19
+ CONFIG_FILENAME = 'smithrc'
42
20
 
43
21
  def initialize
44
22
  load_config
@@ -48,123 +26,151 @@ module Smith
48
26
  @config = Config.new
49
27
  end
50
28
 
51
- def to_hash
52
- {:agent => @agent, :agency => @agency, :amqp => @amqp, :eventmachine => @eventmachine, :logging => @logging, :smith => @smith, :ruby => @ruby}
53
- end
54
-
55
29
  def path
56
30
  @config_file
57
31
  end
58
32
 
33
+ def method_missing(method, *args)
34
+ @config.send(method, *args)
35
+ end
36
+
59
37
  def self.get
60
38
  @config ||= Config.new
61
39
  end
62
40
 
63
41
  private
64
42
 
65
- def set_as_boolean(config, k, default=nil)
66
- v = config[k]
67
- if v.nil? && default
68
- default
69
- else
70
- v = v.downcase
71
- if v == 'true'
72
- true
73
- elsif v == 'false'
74
- false
75
- else
76
- raise ArgumentError, "#{k} must be true or false, #{v} given."
77
- end
78
- end
43
+ class ConfigHash < Hash
44
+ include Hashie::Extensions::Coercion
45
+ include Hashie::Extensions::DeepMerge
46
+ include Hashie::Extensions::MethodReader
47
+ include Hashie::Extensions::MergeInitializer
48
+
49
+ coerce_value Hash, ConfigHash
79
50
  end
80
51
 
81
- def set_as_string(config, k, default=nil)
82
- config[k]
52
+ def load_config
53
+ @config_file = find_config_file
54
+ @config = load_tomls(default_config_file, @config_file)
55
+ coerce_directories!
83
56
  end
84
57
 
85
- def set_as_integer(config, k, default=nil)
86
- v = config[k]
87
- if v.nil? && default
88
- default
89
- else
90
- begin
91
- Integer(v)
92
- rescue
93
- raise ArgumentError, "#{k} must be an integer. #{v} given."
94
- end
58
+ # Make sure the non-default direcotires are set.
59
+ # @raise [MissingConfigItemError<Array<String>>] the config items that are not set.
60
+ def check_directories
61
+ errors = []
62
+ errors << "agncy.acl_directories" if @config.agency[:acl_directories].empty?
63
+ errors << "agncy.agent_directories" if @config.agency[:agent_directories].empty?
64
+
65
+ unless errors.empty?
66
+ raise MissingConfigItemError, errors
95
67
  end
96
68
  end
97
69
 
98
- def load_config
99
- config = read_config_file(find_config_file)
100
-
101
- amqp_opts = Struct::AmqpOpts.new(true, false)
102
- cache_path = Pathname.new(config[:agency_cache_path]).expand_path
103
- local_acl_path = Pathname.new(__FILE__).dirname.join('messaging').join('acl').expand_path
104
- acl_path = "#{local_acl_path}#{File::PATH_SEPARATOR}#{config[:acl_path]}"
105
- broker = Struct::Broker.new(config[:broker_host], set_as_integer(config, :broker_port), config[:broker_user], config[:broker_password], config[:broker_vhost] || '/')
106
- appender = Struct::Appender.new(config[:logging_appender_type], config[:logging_appender_filename])
107
-
108
- @agent = Struct::Agent.new(set_as_boolean(config, :agent_monitor), set_as_boolean(config, :agent_singleton), '', set_as_integer(config, :agent_prefetch))
109
- @agency = Struct::Agency.new(cache_path, config[:agent_path], acl_path, cache_path.join('acl'), config[:agency_pid_dir])
110
- @amqp = Struct::Amqp.new(broker, amqp_opts, amqp_opts, Struct::Publish.new({}), Struct::Subscribe.new(true), Struct::Pop.new(true))
111
- @eventmachine = Struct::Eventmachine.new(set_as_integer(config, :file_descriptors, 1024), set_as_boolean(config, :epoll, true), set_as_boolean(config, :kqueue, true))
112
- @logging = Struct::Logging.new(config[:logging_trace], config[:logging_level], config[:logging_pattern], config[:logging_date_pattern], appender)
113
- @smith = Struct::Smith.new(config[:smith_namespace], set_as_integer(config, :smith_timeout))
114
-
115
- # Set the default ruby runtime. This will use the ruby that is in the path.
116
- @ruby = Hash.new(config[:default_vm] || 'ruby')
117
-
118
- config[:agent_vm] && config[:agent_vm].split(/\s+/).each do |vm_spec|
119
- agent, vm = vm_spec.split(/:/)
120
- @ruby[agent] = vm
70
+ # Check appropriate env vars and convert the string representation to Pathname
71
+ # @return [ConfigHash] the config with coerced paths.
72
+ def coerce_directories!
73
+ @config.tap do |c|
74
+ c.agency[:pid_directory] = path_from_env('SMITH_PID_DIRECTORY', c.agency[:pid_directory])
75
+ c.agency[:cache_directory] = path_from_env('SMITH_CACHE_DIRECTORY', c.agency[:cache_directory])
76
+ c.agency[:acl_directories] = paths_from_env('SMITH_ACL_DIRECTORIES', c.agency[:acl_directories])
77
+ c.agency[:agent_directories] = paths_from_env('SMITH_AGENT_DIRECTORIES', c.agency[:agent_directories])
121
78
  end
122
79
 
123
- find_config_file
80
+ check_directories
81
+ @config.agency[:acl_directories] = @config.agency[:acl_directories] + [smith_acl_directory]
124
82
  end
125
83
 
126
- # Read the config file
127
- def read_config_file(config_file)
128
- @config_file = config_file
129
- config_file.readlines.inject({}) do |a, line|
130
- a.tap do |acc|
131
- parameters = line.gsub(/#.*$/, '').strip
132
- unless parameters.empty?
133
- key, value = parameters.split(/\s+/, 2)
134
- a[key.to_sym] = value
135
- end
84
+ # Find the config file. This checks the following paths before raising an
85
+ # exception:
86
+ #
87
+ # * ./.smithrc
88
+ # * $HOME/.smithrc
89
+ # * /etc/smithrc
90
+ # * /etc/smith/smithrc
91
+ #
92
+ # @return the config file path
93
+ def find_config_file
94
+ if ENV["SMITH_CONFIG"]
95
+ to_pathname(ENV["SMITH_CONFIG"]).tap do |path|
96
+ raise ConfigNotFoundError, "Cannot find a config file name: #{path}" unless path.exist?
136
97
  end
98
+ else
99
+ user = ["./.#{CONFIG_FILENAME}", "#{ENV['HOME']}/.#{CONFIG_FILENAME}"].map { |p| to_pathname(p) }
100
+ system = ["/etc/#{CONFIG_FILENAME}", "/etc/smith/#{CONFIG_FILENAME}"].map { |p| to_pathname(p) }
101
+ default = [default_config_file]
102
+
103
+ (user + system + default).detect { |path| path.exist? }
137
104
  end
138
105
  end
139
106
 
140
- # Find the config file. If it isn't in the CWD recurse up the file path
141
- # until it reaches the user home directory. If it gets to the home
142
- # directory without finding a config file it will read /etc/smithrc and
143
- # then /etc/smith/config. If that fails give up and raise a
144
- # ConfigNotFoundError exception.
107
+ # Convert a string to a path
145
108
  #
146
- # path: the pathname to find the config file. Defaults to CWD.
147
- # recursive: rucures up the path. Defaults to true.
148
- def find_config_file(path=Pathname.new(".").expand_path, recursive=true)
149
- conf = path.join(CONFIG_FILENAME)
150
- if conf.exist?
151
- return conf
152
- else
153
- if path == Pathname.new(ENV['HOME'])
154
- p = Pathname.new("/etc/smithrc")
155
- if p.exist?
156
- return p
157
- else
158
- p = Pathname.new("/etc/smith/config")
159
- if p.exist?
160
- return p
161
- else
162
- raise ConfigNotFoundError, "Cannot find a usable config file."
163
- end
164
- end
165
- end
166
- find_config_file(path.dirname)
167
- end
109
+ # @param path [String] the string to convert
110
+ # @return [Pathname]
111
+ def to_pathname(path)
112
+ Pathname.new(path).expand_path
113
+ end
114
+
115
+ # Returns a path from the environment variable passed in. If the
116
+ # environment variable is not set it returns nil.
117
+ #
118
+ # @param env_var [String] the name of the environment variable
119
+ # @param default [String] the value to use if the environment variable is not set
120
+ # @return [Pathname]
121
+ def path_from_env(env_var, default)
122
+ to_pathname(ENV.fetch(env_var, default))
123
+ end
124
+
125
+ # Returns an array of path from the environment variable passed in. If the
126
+ # environment variable is not set it returns an empty array.
127
+ #
128
+ # @param env_var [String] the name of the environment variable
129
+ # @param default [String] the value to use if the environment variable is not set
130
+ # @return [Array<Pathnmae>]
131
+ def paths_from_env(env_var, default)
132
+ split_paths(ENV.fetch(env_var, default))
133
+ end
134
+
135
+ # Splits a string using PATH_SEPARATOR.
136
+ #
137
+ # @param paths to split
138
+ # @return [Array<Pathnmae>]
139
+ def split_paths(paths)
140
+ (paths || '').split(File::PATH_SEPARATOR).map { |p| to_pathname(p) }
141
+ end
142
+
143
+ # @return [Pathnmae]
144
+ def smith_acl_directory
145
+ Pathname.new(__FILE__).dirname.join('messaging').join('acl').expand_path
146
+ end
147
+
148
+ # @return [Pathnmae]
149
+ def default_config_file
150
+ gem_root.join('config', 'smithrc.toml')
151
+ end
152
+
153
+ # Loads and merges multiple toml files.
154
+ #
155
+ # @params [String] the default toml file
156
+ # @params [String] the user supplied toml file
157
+ # @return [ConfigHash] the merge toml files.
158
+ def load_tomls(default, main)
159
+ load_toml(default).deep_merge(load_toml(main))
160
+ end
161
+
162
+ # Load the toml file specified
163
+ #
164
+ # @param [Pathname] the path of the toml file
165
+ # @return [ConfigHash] the toml file
166
+ def load_toml(path)
167
+ ConfigHash.new(TOML.parse(path.read, :symbolize_keys => true))
168
+ end
169
+
170
+ # Returns the gem root. We can't use Smith.root_path here as it hasn't
171
+ # been initialised yet.
172
+ def gem_root
173
+ Pathname.new(__FILE__).dirname.parent.parent.expand_path
168
174
  end
169
175
  end
170
176
  end
@@ -3,6 +3,8 @@
3
3
  require 'daemons/daemonize'
4
4
  require 'daemons/pidfile'
5
5
 
6
+ require 'smith/utils'
7
+
6
8
  module Smith
7
9
  class Daemon
8
10
 
@@ -11,7 +13,7 @@ module Smith
11
13
  def initialize(name, daemonise, dir=nil)
12
14
  @name = name
13
15
  @daemonise = daemonise
14
- @pid = Daemons::PidFile.new(pid_dir(dir), @name)
16
+ @pid = Daemons::PidFile.new(pid_directory(dir), @name)
15
17
  end
16
18
 
17
19
  # Daemonise the process if the daemonise option is true, otherwise do nothing.
@@ -57,7 +59,7 @@ module Smith
57
59
  def unlink_pid_file
58
60
  p = Pathname.new(@pid.filename)
59
61
  if p.exist?
60
- logger.verbose { "Removing pid file." }
62
+ logger.verbose { "Removing pid file: #{p.to_s}" }
61
63
  p.unlink
62
64
  end
63
65
  end
@@ -66,16 +68,8 @@ module Smith
66
68
 
67
69
  # Get the pid directory. This checks for the command line option,
68
70
  # then the config and finally use the tmp directory.
69
- def pid_dir(dir)
70
- if dir
71
- dir
72
- else
73
- if Smith.config.agency.to_hash.has_key?(:pid_dir)
74
- Smith.config.agency.pid_dir
75
- else
76
- Dir.tmpdir
77
- end
78
- end
71
+ def pid_directory(dir)
72
+ dir || Utils.check_and_create_directory(Smith.config.agency.pid_directory)
79
73
  end
80
74
  end
81
75
  end
@@ -81,7 +81,10 @@ module Smith
81
81
  # Subscribes to a queue and passes the headers and payload into the
82
82
  # block. +subscribe+ will automatically acknowledge the message unless
83
83
  # the options sets :ack to false.
84
- def subscribe(&blk)
84
+ def subscribe(handler=nil, &blk)
85
+
86
+ blk = handler || blk
87
+
85
88
  @queue_completion.completion do |queue|
86
89
  @requeue_options_completion.completion do |requeue_options|
87
90
  if !queue.subscribed?
@@ -253,6 +256,18 @@ module Smith
253
256
  def ack(multiple=false)
254
257
  @metadata.ack(multiple)
255
258
  end
259
+ alias :call :ack
260
+
261
+ # Make #call invoke ack. This makes the following idiom possible:
262
+ #
263
+ # receiver('queue').subscribe do |payload, receiver|
264
+ # blah(payload, &receiver)
265
+ # end
266
+ #
267
+ # which will ensure that #ack is called properly.
268
+ def to_proc
269
+ proc { |obj| ack(obj) }
270
+ end
256
271
 
257
272
  # reject the message. Optionally requeuing it.
258
273
  def reject(opts={})
@@ -6,8 +6,8 @@ module Smith
6
6
  #
7
7
  # @param name [String] the name of the agent.
8
8
  # @return [Pathname] the path of the agent.
9
- def agent_path(name)
10
- Smith.agent_paths.each do |path|
9
+ def agent_directories(name)
10
+ Smith.agent_directories.each do |path|
11
11
  p = path_from_class(path, name)
12
12
  return p if p.exist?
13
13
  end
@@ -38,5 +38,12 @@ module Smith
38
38
  def class_from_name(name)
39
39
  name.to_s.split(/::/).inject(Kernel) { |acc, t| acc.const_get(t) }
40
40
  end
41
+
42
+ def check_and_create_directory(dir)
43
+ dir.tap do
44
+ dir.exist? || dir.mkpath
45
+ end
46
+ end
47
+ module_function :check_and_create_directory
41
48
  end
42
49
  end
@@ -1,3 +1,3 @@
1
1
  module Smith
2
- VERSION = "0.6.12"
2
+ VERSION = "0.7.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smith
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.12
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Heycock
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-21 00:00:00.000000000 Z
11
+ date: 2015-04-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: oj
@@ -65,19 +65,19 @@ dependencies:
65
65
  - !ruby/object:Gem::Version
66
66
  version: '1.1'
67
67
  - !ruby/object:Gem::Dependency
68
- name: eventmachine-le
68
+ name: eventmachine
69
69
  requirement: !ruby/object:Gem::Requirement
70
70
  requirements:
71
71
  - - "~>"
72
72
  - !ruby/object:Gem::Version
73
- version: '1.1'
73
+ version: '1.0'
74
74
  type: :runtime
75
75
  prerelease: false
76
76
  version_requirements: !ruby/object:Gem::Requirement
77
77
  requirements:
78
78
  - - "~>"
79
79
  - !ruby/object:Gem::Version
80
- version: '1.1'
80
+ version: '1.0'
81
81
  - !ruby/object:Gem::Dependency
82
82
  name: extlib
83
83
  requirement: !ruby/object:Gem::Requirement
@@ -191,33 +191,33 @@ dependencies:
191
191
  - !ruby/object:Gem::Version
192
192
  version: 0.1.4
193
193
  - !ruby/object:Gem::Dependency
194
- name: hashie
194
+ name: curses
195
195
  requirement: !ruby/object:Gem::Requirement
196
196
  requirements:
197
- - - "~>"
197
+ - - '='
198
198
  - !ruby/object:Gem::Version
199
- version: '2.1'
199
+ version: 1.0.1
200
200
  type: :runtime
201
201
  prerelease: false
202
202
  version_requirements: !ruby/object:Gem::Requirement
203
203
  requirements:
204
- - - "~>"
204
+ - - '='
205
205
  - !ruby/object:Gem::Version
206
- version: '2.1'
206
+ version: 1.0.1
207
207
  - !ruby/object:Gem::Dependency
208
- name: curses
208
+ name: toml-rb
209
209
  requirement: !ruby/object:Gem::Requirement
210
210
  requirements:
211
- - - '='
211
+ - - "~>"
212
212
  - !ruby/object:Gem::Version
213
- version: 1.0.1
213
+ version: '0.3'
214
214
  type: :runtime
215
215
  prerelease: false
216
216
  version_requirements: !ruby/object:Gem::Requirement
217
217
  requirements:
218
- - - '='
218
+ - - "~>"
219
219
  - !ruby/object:Gem::Version
220
- version: 1.0.1
220
+ version: '0.3'
221
221
  description: Simple multi-agent framework. It uses AMQP for it's messaging layer.
222
222
  email: rgh@filterfish.org
223
223
  executables:
@@ -230,6 +230,7 @@ files:
230
230
  - bin/agency
231
231
  - bin/pry-smith
232
232
  - bin/smithctl
233
+ - config/smithrc.toml
233
234
  - lib/smith.rb
234
235
  - lib/smith/acl_compiler.rb
235
236
  - lib/smith/acl_parser.rb
@@ -317,7 +318,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
317
318
  version: '0'
318
319
  requirements: []
319
320
  rubyforge_project: smith
320
- rubygems_version: 2.2.2
321
+ rubygems_version: 2.4.5
321
322
  signing_key:
322
323
  specification_version: 4
323
324
  summary: Multi-agent framework