auser-poolparty 0.2.9 → 0.2.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. data/Manifest.txt +33 -4
  2. data/PostInstall.txt +3 -1
  3. data/bin/cloud-provision +8 -4
  4. data/bin/cloud-start +2 -0
  5. data/bin/server-get-load +29 -0
  6. data/bin/server-list-active +1 -1
  7. data/bin/server-rerun +24 -0
  8. data/config/requirements.rb +1 -1
  9. data/lib/erlang/messenger/Makefile +15 -0
  10. data/lib/erlang/messenger/README +5 -0
  11. data/lib/erlang/messenger/Rakefile +39 -0
  12. data/lib/erlang/messenger/control +11 -0
  13. data/lib/erlang/messenger/ebin/erl_crash.dump +12138 -0
  14. data/lib/erlang/messenger/ebin/load_app.beam +0 -0
  15. data/lib/erlang/messenger/ebin/pm_master.beam +0 -0
  16. data/lib/erlang/messenger/ebin/pm_master_supervisor.beam +0 -0
  17. data/lib/erlang/messenger/ebin/pm_node.beam +0 -0
  18. data/lib/erlang/messenger/ebin/pm_node_supervisor.beam +0 -0
  19. data/lib/erlang/messenger/lib/load_app.app +18 -0
  20. data/lib/erlang/messenger/src/load_app.erl +26 -0
  21. data/lib/erlang/messenger/src/pm_cluster.erl +31 -0
  22. data/lib/erlang/messenger/src/pm_master.erl +27 -0
  23. data/lib/erlang/messenger/src/pm_master_supervisor.erl +8 -0
  24. data/lib/erlang/messenger/src/pm_node.erl +79 -0
  25. data/lib/erlang/messenger/src/pm_node_supervisor.erl +33 -0
  26. data/lib/poolparty.rb +9 -6
  27. data/lib/poolparty/base_packages/haproxy.rb +9 -2
  28. data/lib/poolparty/base_packages/heartbeat.rb +31 -30
  29. data/lib/poolparty/base_packages/poolparty.rb +30 -17
  30. data/lib/poolparty/base_packages/ruby.rb +1 -2
  31. data/lib/poolparty/core/array.rb +2 -2
  32. data/lib/poolparty/core/hash.rb +16 -2
  33. data/lib/poolparty/core/string.rb +1 -1
  34. data/lib/poolparty/core/symbol.rb +2 -2
  35. data/lib/poolparty/dependency_resolutions/base.rb +12 -0
  36. data/lib/poolparty/dependency_resolutions/puppet.rb +49 -0
  37. data/lib/poolparty/helpers/display.rb +3 -3
  38. data/lib/poolparty/helpers/provisioner_base.rb +6 -6
  39. data/lib/poolparty/helpers/provisioners/master.rb +21 -21
  40. data/lib/poolparty/helpers/provisioners/slave.rb +4 -3
  41. data/lib/poolparty/modules/definable_resource.rb +1 -0
  42. data/lib/poolparty/modules/file_writer.rb +11 -18
  43. data/lib/poolparty/modules/method_missing_sugar.rb +1 -1
  44. data/lib/poolparty/modules/pretty_printer.rb +11 -11
  45. data/lib/poolparty/modules/resourcing_dsl.rb +57 -0
  46. data/lib/poolparty/monitors/base_monitor.rb +17 -3
  47. data/lib/poolparty/monitors/monitors/cpu_monitor.rb +15 -0
  48. data/lib/poolparty/monitors/monitors/memory_monitor.rb +23 -0
  49. data/lib/poolparty/net/remote_instance.rb +5 -0
  50. data/lib/poolparty/net/remoter.rb +12 -2
  51. data/lib/poolparty/net/remoter_base.rb +5 -1
  52. data/lib/poolparty/plugins/git.rb +24 -15
  53. data/lib/poolparty/pool/base.rb +1 -2
  54. data/lib/poolparty/pool/cloud.rb +14 -9
  55. data/lib/poolparty/pool/custom_resource.rb +6 -6
  56. data/lib/poolparty/pool/resource.rb +53 -104
  57. data/lib/poolparty/pool/resources/class_package.rb +4 -4
  58. data/lib/poolparty/pool/resources/conditional.rb +5 -1
  59. data/lib/poolparty/pool/resources/gem.rb +22 -8
  60. data/lib/poolparty/pool/resources/symlink.rb +10 -6
  61. data/lib/poolparty/pool/resources/variable.rb +1 -1
  62. data/lib/poolparty/templates/ha.cf +3 -3
  63. data/lib/poolparty/templates/haproxy.conf +3 -3
  64. data/lib/poolparty/templates/puppet.conf +1 -1
  65. data/lib/poolparty/version.rb +1 -1
  66. data/poolparty.gemspec +7 -5
  67. data/setup.rb +3 -3
  68. data/spec/poolparty/core/hash_spec.rb +19 -1
  69. data/spec/poolparty/dependency_resolutions/base_spec.rb +11 -0
  70. data/spec/poolparty/modules/file_writer_spec.rb +9 -0
  71. data/spec/poolparty/monitors/base_monitor_spec.rb +19 -0
  72. data/spec/poolparty/monitors/monitors/cpu_monitor_spec.rb +17 -0
  73. data/spec/poolparty/net/remote_instance_spec.rb +6 -1
  74. data/spec/poolparty/net/remoter_spec.rb +2 -1
  75. data/spec/poolparty/pool/cloud_spec.rb +15 -1
  76. data/spec/poolparty/pool/custom_resource_spec.rb +2 -2
  77. data/spec/poolparty/pool/plugin_spec.rb +4 -4
  78. data/spec/poolparty/pool/resource_spec.rb +39 -1
  79. data/spec/poolparty/pool/resources/gem_spec.rb +30 -3
  80. data/spec/poolparty/pool/resources/symlink_spec.rb +4 -1
  81. data/spec/poolparty/spec_helper.rb +1 -0
  82. data/website/index.html +1 -1
  83. metadata +40 -7
  84. data/lib/erlang/eb_server.erl +0 -27
  85. data/lib/poolparty/plugins/gem_package.rb +0 -30
  86. data/lib/poolparty/puppet_plugins/poolparty/plugins/puppet/parser/functions/activenodeips.rb +0 -11
  87. data/lib/poolparty/puppet_plugins/poolparty/plugins/puppet/parser/functions/activenodenames.rb +0 -11
@@ -0,0 +1,18 @@
1
+ {application, load_app,
2
+ [
3
+ % Quick description of the server
4
+ {description, "Load Server"},
5
+ % Version
6
+ {vsn, "0.1"},
7
+ % All modules used by the application.
8
+ {modules, [load_supervisor, load_server, load_app, load_client]},
9
+ % All the registered names in the application
10
+ {registered, [load_server]},
11
+ % These must be started for application to run
12
+ {applications, [kernel, stdlib]},
13
+ % Environment vars
14
+ {env, []},
15
+ % Module and Args used to start
16
+ {mod, {load_app, []}}
17
+ ]
18
+ }.
@@ -0,0 +1,26 @@
1
+ -module (load_app).
2
+
3
+ -ifdef(EUNIT).
4
+ -include_lib("eunit/include/eunit.hrl").
5
+ -endif.
6
+
7
+ -behaviour (application).
8
+
9
+ -export ([start/0, start/2, stop/1]).
10
+
11
+ start() ->
12
+ io:format("application:start/1 starting...~n"),
13
+ application:start(?MODULE).
14
+
15
+ start(_Type, _) ->
16
+ io:format("Starting load_app~n"),
17
+ case load_supervisor:start_link() of
18
+ {ok, Pid} ->
19
+ {ok, Pid};
20
+ Error ->
21
+ Error
22
+ end.
23
+
24
+ stop(State) ->
25
+ io:format("Error: ~p~n", State),
26
+ exit(whereis(load_supervisor), shutdown).
@@ -0,0 +1,31 @@
1
+ -module(cluster).
2
+
3
+ -ifdef(EUNIT).
4
+ -include_lib("eunit/include/eunit.hrl").
5
+ -endif.
6
+
7
+ -export([slaves/1]).
8
+ %% Argument:
9
+ %% Hosts: List of hostname (string)
10
+ slaves([]) ->
11
+ ok;
12
+
13
+ slaves([Host|Hosts]) ->
14
+ Args = erl_system_args(),
15
+ NodeName = "cluster",
16
+ {ok, Node} = slave:start_link(Host, NodeName, Args),
17
+ io:format("Erlang node started = [~p]~n", [Node]),
18
+ slaves(Hosts).
19
+
20
+ erl_system_args()->
21
+ Shared = case init:get_argument(shared) of
22
+ error -> " ";
23
+ {ok,[[]]} -> " -shared "
24
+ end,
25
+ lists:append(
26
+ ["-rsh ssh -setcookie",
27
+ atom_to_list(erlang:get_cookie()),
28
+ Shared, " +Mea r10b "
29
+ ]).
30
+ %% Do not forget to start erlang with a command like:
31
+ %% erl -rsh ssh -sname clustmaster
@@ -0,0 +1,27 @@
1
+ % The client is a running process that will run on the master node
2
+ % and spawn requests to the pm_nodes and compile the responses
3
+ % for use within the poolparty network
4
+
5
+ -module (pm_master).
6
+
7
+ -ifdef(EUNIT).
8
+ -include_lib("eunit/include/eunit.hrl").
9
+ -endif.
10
+
11
+ % Client function definitions
12
+ -export ([get_load/2]).
13
+
14
+ % pm_master:get_load("0", "cpu").
15
+ get_load(Index, Type) ->
16
+ Pid = get_pid_from_index(Index),
17
+ gen_server:call(Pid, get_load_for_type, [Type]).
18
+
19
+ % Private methods
20
+ get_pid_from_index(Index) ->
21
+ String = lists:append(["pm_node", Index]),
22
+ io:format("Looking for ~p~n", [String]),
23
+ whereis(str).
24
+
25
+ % Tests
26
+ basic_test_() ->
27
+ ?_assert(get_pid_from_index == 0).
@@ -0,0 +1,8 @@
1
+ % This supervisor is responsible for monitoring the
2
+ % client service
3
+
4
+ -module (pm_master_supervisor).
5
+
6
+ -ifdef(EUNIT).
7
+ -include_lib("eunit/include/eunit.hrl").
8
+ -endif.
@@ -0,0 +1,79 @@
1
+ %%%***************************************
2
+ %%%
3
+ %%% PoolParty node-server
4
+ %%% Author: Ari Lerner <ari.the.lerner@gmail.com>
5
+ %%%
6
+ %%% Description: This server runs on the poolparty nodes
7
+ %%%
8
+ %%%***************************************
9
+
10
+ % The name of our module
11
+ -module (pm_node).
12
+
13
+ -ifdef(EUNIT).
14
+ -include_lib("eunit/include/eunit.hrl").
15
+ -endif.
16
+
17
+ % We are using the gen_server behaviour
18
+ -behaviour (gen_server).
19
+ -export ([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
20
+
21
+ % Client function definitions
22
+ -export ([start_link/1, stop/0]).
23
+ -export ([get_load_for_type/2]).
24
+
25
+ % Client Function API Calls
26
+ get_load_for_type(From, Type) ->
27
+ io:format("Getting load for "++Type++" on ~p~n", [From]),
28
+ String = string:concat("server-get-load -m ",Type),
29
+ gen_server:reply(From, {load, os:cmd(String)}).
30
+
31
+ start_link(Index) ->
32
+ io:format("Starting pm_node~p...~n", [Index]),
33
+ String = list_to_atom(string:concat("pm_node", Index)),
34
+ gen_server:start_link({global, String}, String, [], []).
35
+
36
+ stop() ->
37
+ gen_server:cast(?MODULE, stop).
38
+
39
+ % Load monitor methods
40
+ % TODO: Make this dynamic
41
+ % get_system_load() ->
42
+
43
+ % Gen server callbacks
44
+ % Sends the response and state back
45
+ init([]) ->
46
+ process_flag(trap_exit, true),
47
+ {ok, []}.
48
+
49
+ % Handle synchronous messages
50
+ % Signature:
51
+ % handle_call(_Request, _From, State) ->
52
+ % {reply, ignored, State}
53
+ %
54
+ % Gets the load on the server
55
+ handle_call({get_load, Type}, From, State) ->
56
+ spawn(?MODULE, get_load_for_type, [From, Type]),
57
+ {noreply, State};
58
+ handle_call(_Request, _From, State) -> % The catch-all
59
+ {reply, ignored, State}.
60
+
61
+ % Handle asynchronous messages
62
+ handle_cast(stop, State) ->
63
+ {stop, normal, State};
64
+ handle_cast(_Msg, State) ->
65
+ {noreply, State}.
66
+
67
+ % Handle other messages
68
+ handle_info(_Info, State) ->
69
+ io:format("Info message received from: ~p~n", [_Info]),
70
+ {noreply, State}.
71
+
72
+ % Exit
73
+ terminate(_Reason, State) ->
74
+ io:format("Server is stopping...~n"),
75
+ {ok, State}.
76
+
77
+ % If the code changes
78
+ code_change(_OldVsn, State, _Extra) ->
79
+ {ok, State}.
@@ -0,0 +1,33 @@
1
+ % Supervisor for the poolparty_messenger server
2
+ %
3
+ % Ari Lerner
4
+ % CitrusByte
5
+
6
+ -module (pm_node_supervisor).
7
+ -behaviour (supervisor).
8
+
9
+ -ifdef(EUNIT).
10
+ -include_lib("eunit/include/eunit.hrl").
11
+ -endif.
12
+
13
+ -define (SERVER, ?MODULE).
14
+
15
+ -export ([start_link/0, init/1]).
16
+
17
+ start_link() ->
18
+ io:format("Starting load_supervisor...~n"),
19
+ supervisor:start_link({local, ?SERVER}, ?MODULE, []).
20
+
21
+ init([]) ->
22
+ RestartStrategy = one_for_one,
23
+ MaxRestarts = 3, % 1000
24
+ MaxTimeBetRestarts = 30, % 3600
25
+
26
+ SupFlags = {RestartStrategy, MaxRestarts, MaxTimeBetRestarts},
27
+
28
+ LoadServers = [
29
+ {load_server, {load_server, start_link, []}, permanent, 5000, worker, [load_server, load_client]}
30
+ ],
31
+
32
+ {ok, {SupFlags, LoadServers}}.
33
+
@@ -1,11 +1,14 @@
1
1
  require 'rubygems'
2
2
 
3
3
  # Load required gems
4
- require 'active_support'
5
- require 'open4'
6
- require "ftools"
7
- require "logging"
8
- require "ruby2ruby"
4
+ %w(active_support open4 ftools logging ruby2ruby).each do |lib|
5
+ begin
6
+ require lib
7
+ rescue Exception => e
8
+ puts "Could not find library #{lib}: #{e}"
9
+ end
10
+
11
+ end
9
12
 
10
13
  # Use active supports auto load mechanism
11
14
  ActiveSupport::Dependencies.load_paths << File.dirname(__FILE__)
@@ -13,7 +16,7 @@ ActiveSupport::Dependencies.load_paths << File.dirname(__FILE__)
13
16
  ## Load PoolParty
14
17
  require "#{File.dirname(__FILE__)}/poolparty/version"
15
18
 
16
- %w(core modules exceptions net).each do |dir|
19
+ %w(core modules exceptions dependency_resolutions monitors net).each do |dir|
17
20
  Dir[File.dirname(__FILE__) + "/poolparty/#{dir}/**.rb"].each do |file|
18
21
  require file
19
22
  end
@@ -21,8 +21,15 @@ module PoolParty
21
21
 
22
22
  # Tempalte variables
23
23
  variable(:name => "name_haproxy", :value => "#{@parent.name}")
24
- variable(:name => "nodenames_haproxy", :value => 'generate(". /etc/profile && server-list-active -c name")')
25
- variable(:name => "node_ips_haproxy", :value => 'generate(". /etc/profile && server-list-active -c ip")')
24
+
25
+ if @parent.provisioning?
26
+ variable(:name => "nodenames_haproxy", :value => "#{list_of_running_instances.map{|a| "#{a.send :name}" }.join("\t")}")
27
+ variable(:name => "node_ips_haproxy", :value => "#{list_of_running_instances.map{|a| "#{a.send :ip}" }.join("\t")}")
28
+ else
29
+ variable(:name => "nodenames_haproxy", :value => "generate('/usr/bin/env', '/var/lib/gems/1.8/bin/server-list-active', '-c', 'name')")
30
+ variable(:name => "node_ips_haproxy", :value => "generate('/usr/bin/env', '/var/lib/gems/1.8/bin/server-list-active', '-c', 'ip')")
31
+ end
32
+
26
33
  variable(:name => "ports_haproxy", :value => ([(self.respond_to?(:port) ? port : Base.port)].flatten))
27
34
  variable(:name => "forwarding_port", :value => (respond_to?(:forwarding_port) ? forwarding_port : Base.forwarding_port))
28
35
  variable(:name => "proxy_mode", :value => (respond_to?(:proxy_mode) ? proxy_mode : Base.proxy_mode))
@@ -4,42 +4,43 @@ module PoolParty
4
4
 
5
5
  def enable
6
6
  execute_if("$hostname", "master") do
7
- has_package(:name => "heartbeat-2", :ensure => "installed")
8
- has_service(:name => "heartbeat", :hasstatus => true) do
9
- ensures "running"
10
- end
7
+ has_package(:name => "heartbeat-2", :ensure => "installed") do
8
+ # These can also be passed in via hash
9
+ has_remotefile(:name => "/etc/ha.d/ha.cf") do
10
+ mode 444
11
+ notify 'Service["heartbeat"]'
12
+ template File.join(File.dirname(__FILE__), "..", "templates/ha.cf")
13
+ end
14
+
15
+ has_remotefile(:name => "/etc/ha.d/authkeys") do
16
+ mode 400
17
+ notify 'Service["heartbeat"]'
18
+ template File.join(File.dirname(__FILE__), "..", "templates/authkeys")
19
+ end
20
+
21
+ has_remotefile(:name => "/etc/ha.d/cib.xml") do
22
+ mode 444
23
+ notify 'Exec["heartbeat-update-cib"]'
24
+ template File.join(File.dirname(__FILE__), "..", "templates/cib.xml")
25
+ end
26
+
27
+ has_service(:name => "heartbeat", :hasstatus => true)
28
+ end
11
29
 
12
30
  has_exec(:name => "heartbeat-update-cib", :command => "/usr/sbin/cibadmin -R -x /etc/ha.d/cib.xml", :refreshonly => true)
13
-
14
- # variables for the templates
15
- variable(:name => "ha_nodenames", :value => 'generate(". /etc/profile && server-list-active -c name")')
16
- variable(:name => "ha_node_ips", :value => 'generate(". /etc/profile && server-list-active -c ip")')
31
+
32
+ if @parent.provisioning?
33
+ variable(:name => "ha_nodenames", :value => "#{list_of_running_instances.map{|a| "#{a.send :name}" }.join("\t")}")
34
+ variable(:name => "ha_node_ips", :value => "#{list_of_running_instances.map{|a| "#{a.send :ip}" }.join("\t")}")
35
+ else
36
+ # variables for the templates
37
+ variable(:name => "ha_nodenames", :value => "generate('/usr/bin/env', '/var/lib/gems/1.8/bin/server-list-active', '-c', 'name')")
38
+ variable(:name => "ha_node_ips", :value => "generate('/usr/bin/env', '/var/lib/gems/1.8/bin/server-list-active', '-c', 'ip')")
39
+ end
17
40
 
18
41
  has_variable({:name => "ha_timeout", :value => (self.respond_to?(:timeout) ? timeout : "5s")})
19
42
  has_variable({:name => "ha_port", :value => (self.respond_to?(:port) ? port : Base.port)})
20
43
 
21
- # These can also be passed in via hash
22
- has_remotefile(:name => "/etc/ha.d/ha.cf") do
23
- mode 444
24
- requires 'Package["heartbeat-2"]'
25
- notify 'Service["heartbeat"]'
26
- template File.join(File.dirname(__FILE__), "..", "templates/ha.cf")
27
- end
28
-
29
- has_remotefile(:name => "/etc/ha.d/authkeys") do
30
- mode 400
31
- requires 'Package["heartbeat-2"]'
32
- notify 'Service["heartbeat"]'
33
- template File.join(File.dirname(__FILE__), "..", "templates/authkeys")
34
- end
35
-
36
- has_remotefile(:name => "/etc/ha.d/cib.xml") do
37
- mode 444
38
- requires 'Package["heartbeat-2"]'
39
- notify 'Exec["heartbeat-update-cib"]'
40
- template File.join(File.dirname(__FILE__), "..", "templates/cib.xml")
41
- end
42
-
43
44
  end
44
45
 
45
46
  execute_if("$hostname", "master") do
@@ -4,31 +4,44 @@ module PoolParty
4
4
 
5
5
  def enable
6
6
  has_package(:name => "erlang")
7
- # These should be installed automagically by poolparty, but just in case
8
- has_gempackage(:name => "activesupport", :requires => package(:name => "rubygems"))
9
- has_gempackage(:name => "logging", :requires => package(:name => "rubygems"))
10
- has_gempackage(:name => "hoe", :requires => package(:name => "rubygems"))
11
- has_gempackage(:name => "xml-simple", :requires => package(:name => "rubygems"))
12
- has_gempackage(:name => "ParseTree", :version => "2.2.0", :requires => gempackage(:name => "hoe"))
13
-
14
- has_gempackage(:name => "RubyInline", :requires => gempackage(:name => "ParseTree"))
15
- has_gempackage(:name => "open4", :requires => gempackage(:name => "hoe"))
16
- has_gempackage(:name => "ruby2ruby", :requires => gempackage(:name => "ParseTree"))
17
-
18
- has_gempackage(:name => "grempe-amazon-ec2", :source => "http://gems.github.com", :requires => gempackage(:name => "xml-simple"))
19
- has_gempackage(:name => "auser-poolparty", :source => "http://gems.github.com", :requires => [gempackage(:name => "activesupport"), gempackage(:name => "ParseTree")])
20
7
 
8
+ has_package(:name => "rubygems") do
9
+ # These should be installed automagically by poolparty, but just in case
10
+ # TODO: Fix the requires method with a helper
11
+ has_gempackage(:name => "logging")
12
+ has_gempackage(:name => "xml-simple") do
13
+ has_gempackage(:name => "grempe-amazon-ec2", :source => "http://gems.github.com")
14
+ end
15
+
16
+ has_gempackage(:name => "hoe") do
17
+ has_gempackage(:name => "open4")
18
+
19
+ has_gempackage(:name => "ParseTree", :version => "2.2.0") do
20
+ has_gempackage(:name => "ruby2ruby")
21
+ has_gempackage(:name => "activesupport") do
22
+ has_gempackage(:name => "auser-poolparty", :source => "http://gems.github.com")
23
+ end
24
+ has_gempackage(:name => "RubyInline")
25
+ end
26
+ end
27
+
28
+ end
29
+
21
30
  # Build hostsfile
22
31
  # TODO: COME BACK AND CLEAN THIS UP
23
32
  (self.respond_to?(:list_of_running_instances) ? self : parent).list_of_running_instances.each do |ri|
24
33
  has_host({:name => "#{ri.name}", :ip => ri.ip })
25
34
  end
26
35
 
27
- # -n #{parent.name}
36
+ # Custom run puppet to minimize footprint
37
+ # TODO: Update the offsetted times
38
+ has_cron(:name => "puppetd runner", :user => Base.user, :minute => [0,15,30,45]) do
39
+ command((self.respond_to?(:master) ? self : parent).master.puppet_runner_command)
40
+ end
41
+
42
+ # These are all requirements on the master
28
43
  execute_if("$hostname", "master") do
29
- has_cron({:command => ". /etc/profile && which cloud-maintain | /bin/sh"}) do
30
- minute "*/5"
31
- end
44
+ has_cron({:command => ". /etc/profile && which cloud-maintain | /bin/sh", :minute => "*/3"})
32
45
  end
33
46
  # has_host(:name => "puppet", :ip => (self.respond_to?(:master) ? self : parent).master.ip)
34
47
  end
@@ -10,8 +10,7 @@ module PoolParty
10
10
  has_package(:name => "libreadline-ruby1.8")
11
11
  has_package(:name => "libruby1.8")
12
12
  has_package(:name => "ruby1.8-dev")
13
- has_package(:name => "ruby1.8")
14
- # has_package(:name => "rubygems")
13
+ has_package(:name => "ruby1.8")
15
14
 
16
15
  has_line_in_file("export PATH=$PATH:/var/lib/gems/1.8/bin/", "/etc/profile")
17
16
 
@@ -15,8 +15,8 @@ class Array
15
15
  def nice_runnable(quiet=true)
16
16
  self.flatten.reject{|e| (e.nil? || e.empty?) }.join(" \n ").chomp.nice_runnable(quiet)
17
17
  end
18
- def to_string(prev="")
19
- map {|a| a.to_string(prev)}.join("\n")
18
+ def to_string(pre="")
19
+ map {|a| a.to_string(pre)}.join("\n")
20
20
  end
21
21
  def get_named(str="")
22
22
  map {|a| a.name == str ? a : nil }.reject {|a| a.nil? }