smith 0.6.12 → 0.7.1

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