rbbt-util 5.28.9 → 5.29.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/lib/rbbt/entity.rb +1 -1
  3. data/lib/rbbt/fix_width_table.rb +5 -4
  4. data/lib/rbbt/hpc.rb +1 -549
  5. data/lib/rbbt/hpc/orchestrate.rb +23 -0
  6. data/lib/rbbt/hpc/slurm.rb +570 -0
  7. data/lib/rbbt/persist.rb +9 -4
  8. data/lib/rbbt/persist/tsv/adapter.rb +0 -1
  9. data/lib/rbbt/persist/tsv/fix_width_table.rb +5 -3
  10. data/lib/rbbt/resource.rb +12 -6
  11. data/lib/rbbt/resource/path.rb +1 -1
  12. data/lib/rbbt/tsv/attach.rb +7 -4
  13. data/lib/rbbt/tsv/dumper.rb +6 -2
  14. data/lib/rbbt/tsv/parallel.rb +0 -3
  15. data/lib/rbbt/util/R.rb +2 -2
  16. data/lib/rbbt/util/cmd.rb +10 -0
  17. data/lib/rbbt/util/misc/bgzf.rb +1 -1
  18. data/lib/rbbt/util/misc/indiferent_hash.rb +8 -0
  19. data/lib/rbbt/util/misc/inspect.rb +11 -7
  20. data/lib/rbbt/util/named_array.rb +1 -1
  21. data/lib/rbbt/util/open.rb +17 -16
  22. data/lib/rbbt/workflow.rb +2 -1
  23. data/lib/rbbt/workflow/accessor.rb +3 -2
  24. data/lib/rbbt/workflow/definition.rb +3 -1
  25. data/lib/rbbt/workflow/examples.rb +2 -2
  26. data/lib/rbbt/workflow/integration/ansible.rb +53 -0
  27. data/lib/rbbt/workflow/integration/ansible/workflow.rb +60 -0
  28. data/lib/rbbt/workflow/step.rb +16 -5
  29. data/lib/rbbt/workflow/step/accessor.rb +36 -24
  30. data/lib/rbbt/workflow/step/dependencies.rb +8 -2
  31. data/lib/rbbt/workflow/step/run.rb +22 -19
  32. data/lib/rbbt/workflow/util/archive.rb +2 -0
  33. data/lib/rbbt/workflow/util/orchestrator.rb +30 -12
  34. data/lib/rbbt/workflow/util/provenance.rb +7 -3
  35. data/share/rbbt_commands/ansible +55 -0
  36. data/share/rbbt_commands/purge_job +0 -1
  37. data/share/rbbt_commands/slurm/list +141 -0
  38. data/share/rbbt_commands/slurm/orchestrate +47 -0
  39. data/share/rbbt_commands/{workflow/slurm → slurm/task} +10 -3
  40. data/share/rbbt_commands/system/status +22 -22
  41. data/share/rbbt_commands/workflow/forget_deps +9 -0
  42. data/share/rbbt_commands/workflow/info +12 -9
  43. data/share/rbbt_commands/workflow/prov +2 -1
  44. data/test/rbbt/association/test_index.rb +6 -6
  45. data/test/rbbt/knowledge_base/test_query.rb +3 -3
  46. data/test/rbbt/knowledge_base/test_registry.rb +1 -1
  47. data/test/rbbt/persist/tsv/test_cdb.rb +0 -7
  48. data/test/rbbt/persist/tsv/test_kyotocabinet.rb +2 -8
  49. data/test/rbbt/persist/tsv/test_leveldb.rb +0 -6
  50. data/test/rbbt/persist/tsv/test_lmdb.rb +0 -6
  51. data/test/rbbt/persist/tsv/test_tokyocabinet.rb +15 -14
  52. data/test/rbbt/test_entity.rb +0 -1
  53. data/test/rbbt/test_knowledge_base.rb +3 -4
  54. data/test/rbbt/test_persist.rb +10 -6
  55. data/test/rbbt/test_workflow.rb +49 -16
  56. data/test/rbbt/tsv/test_accessor.rb +11 -0
  57. data/test/rbbt/tsv/test_attach.rb +86 -8
  58. data/test/rbbt/tsv/test_index.rb +6 -7
  59. data/test/rbbt/tsv/test_manipulate.rb +2 -3
  60. data/test/rbbt/util/R/test_model.rb +2 -1
  61. data/test/rbbt/util/R/test_plot.rb +0 -2
  62. data/test/rbbt/util/concurrency/test_processes.rb +1 -1
  63. data/test/rbbt/util/misc/test_bgzf.rb +11 -7
  64. data/test/rbbt/util/misc/test_lock.rb +0 -1
  65. data/test/rbbt/util/misc/test_multipart_payload.rb +1 -1
  66. data/test/rbbt/util/misc/test_pipes.rb +0 -5
  67. data/test/rbbt/util/test_R.rb +1 -0
  68. data/test/rbbt/util/test_log.rb +4 -6
  69. data/test/rbbt/util/test_misc.rb +0 -2
  70. data/test/rbbt/util/test_open.rb +0 -1
  71. data/test/rbbt/util/test_python.rb +17 -1
  72. data/test/rbbt/workflow/test_remote_workflow.rb +1 -1
  73. data/test/rbbt/workflow/test_step.rb +8 -3
  74. data/test/rbbt/workflow/util/test_orchestrator.rb +50 -0
  75. metadata +10 -5
  76. data/test/rbbt/workflow/remote/test_client.rb +0 -56
@@ -66,7 +66,7 @@ class Step
66
66
  str << "\n"
67
67
  end
68
68
 
69
- def self.prov_report(step, offset = 0, task = nil, seen = [])
69
+ def self.prov_report(step, offset = 0, task = nil, seen = [], expand_repeats = false)
70
70
  info = step.info || {}
71
71
  info[:task_name] = task
72
72
  path = step.path
@@ -82,9 +82,13 @@ class Step
82
82
  new = ! seen.include?(path)
83
83
  if new
84
84
  seen << path
85
- str << prov_report(dep, offset + 1, task, seen)
85
+ str << prov_report(dep, offset + 1, task, seen, expand_repeats)
86
86
  else
87
- str << Log.color(:green, Log.uncolor(prov_report(dep, offset+1, task)))
87
+ if expand_repeats
88
+ str << Log.color(:green, Log.uncolor(prov_report(dep, offset+1, task)))
89
+ else
90
+ str << Log.color(:green, " " * (offset + 1) + Log.uncolor(prov_report_msg(status, name, path, info)))
91
+ end
88
92
  end
89
93
  end if step.dependencies
90
94
  str
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rbbt-util'
4
+ require 'rbbt/util/simpleopt'
5
+
6
+ $0 = "rbbt #{$previous_commands*" "} #{ File.basename(__FILE__) }" if $previous_commands
7
+
8
+ options = SOPT.setup <<EOF
9
+
10
+ Run ansible
11
+
12
+ $ #{$0} [options] <rules.yml|->
13
+
14
+ Use - to read from STDIN
15
+
16
+ -h--help Print this help
17
+ -dr--dry_run Only print the playbook
18
+ -H--hosts* hosts to run it on
19
+ EOF
20
+ if options[:help]
21
+ if defined? rbbt_usage
22
+ rbbt_usage
23
+ else
24
+ puts SOPT.doc
25
+ end
26
+ exit 0
27
+ end
28
+
29
+ require 'rbbt/workflow/integration/ansible'
30
+
31
+ playbook, task = ARGV
32
+ playbook_file = Rbbt.share.ansible["test.yaml"].find if playbook.nil?
33
+
34
+ dry_run = options.delete :dry_run
35
+
36
+ if ! Misc.filename?(playbook_file)
37
+ playbook_file = Rbbt.share.ansible.glob(playbook + ".*").first
38
+ end
39
+
40
+ if ! Misc.filename?(playbook_file)
41
+ recipe = Rbbt.share.recipes.glob(playbook + ".*").first
42
+ playbook_file = Ansible.playbook recipe, task, options
43
+ end
44
+
45
+
46
+ if dry_run
47
+ if Hash === playbook_file
48
+ ppp playbook_file.to_yaml
49
+ else
50
+ ppp Log.color :magenta, playbook_file
51
+ ppp Open.read(playbook_file)
52
+ end
53
+ else
54
+ Ansible.play playbook_file
55
+ end
@@ -14,7 +14,6 @@ $ rbbt purge [options] <job_path>
14
14
 
15
15
  -h--help Print this help
16
16
  -r--recursive Remove recursively
17
-
18
17
  EOF
19
18
  if options[:help]
20
19
  if defined? rbbt_usage
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rbbt-util'
4
+ require 'rbbt/util/simpleopt'
5
+
6
+ #$0 = "rbbt #{$previous_commands*""} #{ File.basename(__FILE__) }" if $previous_commands
7
+
8
+ options = SOPT.setup <<EOF
9
+
10
+ Queue a job in Marenostrum
11
+
12
+ $ rbbt mnl [options]
13
+
14
+ -h--help Print this help
15
+ -d--done Done jobs only
16
+ -e--error Error jobs only
17
+ -a--aborted SLURM aboted jobs
18
+ -r--running Running jobs only
19
+ -q--queued Queued jobs only
20
+ -j--job* Job ids
21
+ -s--search* Regular expression
22
+ -t--tail* Show the last lines of the STDERR
23
+ EOF
24
+
25
+ if options[:help]
26
+ if defined? rbbt_usage
27
+ rbbt_usage
28
+ else
29
+ puts SOPT.doc
30
+ end
31
+ exit 0
32
+ end
33
+
34
+ Log.severity = 4
35
+ done, error, running, queued, aborted, jobid, search, tail = options.values_at :done, :error, :running, :queued, :aborted, :job, :search, :tail
36
+
37
+ workdir = File.expand_path('~/rbbt-slurm')
38
+ Path.setup(workdir)
39
+
40
+ running_jobs = begin
41
+ CMD.cmd('squeue').read.split("\n").collect{|l| l.to_i.to_s}
42
+ rescue
43
+ Log.warn "Cannot determine if jobs are running, they will seem to be all alive (Job ID in green)"
44
+ $norunningjobs = true
45
+ []
46
+ end
47
+
48
+ count = 0
49
+ workdir.glob("**/command.slurm").sort_by{|f| File.mtime(f)}.each do |fcmd|
50
+ dir = File.dirname(fcmd)
51
+
52
+ if m = Open.read(fcmd).match(/#CMD: (.*)/)
53
+ cmd = m[1]
54
+ else
55
+ cmd = nil
56
+ end
57
+
58
+ if m = Open.read(fcmd).match(/# Run command\n(.*?)\n/im)
59
+ exe = m[1]
60
+ else
61
+ exe = nil
62
+ end
63
+
64
+ if m = Open.read(fcmd).match(/^CONTAINER_DIR=(.*)/)
65
+ container_home = m[1]
66
+ else
67
+ container_home = nil
68
+ end
69
+
70
+
71
+ if File.exists?(fid = File.join(dir, 'job.id'))
72
+ id = Open.read(fid).chomp
73
+ else
74
+ id = nil
75
+ end
76
+
77
+ if File.exists?(fstatus = File.join(dir, 'exit.status'))
78
+ exit_status = Open.read(fstatus).to_i
79
+ else
80
+ exit_status = nil
81
+ end
82
+
83
+ if File.exists?(fstatus = File.join(dir, 'job.status'))
84
+ nodes = Open.read(fstatus).split("\n").last.split(/\s+/).last.split(",")
85
+ else
86
+ nodes = []
87
+ end
88
+
89
+ if File.exists?(File.join(dir, 'std.out'))
90
+ outt = File.mtime File.join(dir, 'std.out')
91
+ errt = File.mtime File.join(dir, 'std.err')
92
+ time_diff = Time.now - [outt, errt].max
93
+ end
94
+
95
+ fdep = File.join(dir, 'dependencies.list')
96
+ deps = Open.read(fdep).split("\n") if File.exists?(fdep)
97
+
98
+ if done || error || aborted || running || queued || jobid || search
99
+ select = false
100
+ select = true if done && exit_status == 0
101
+ select = true if error && exit_status && exit_status != 0
102
+ select = true if aborted && (exit_status.nil? && ! running_jobs.include?(id))
103
+ select = true if queued && deps && (running_jobs & deps).any?
104
+ select = true if running && (exit_status.nil? && running_jobs.include?(id)) && (!deps || (running_jobs & deps).empty?)
105
+ select = true if jobid && jobid.split(",").include?(id)
106
+ select = true if search && cmd.match(/#{search}/)
107
+ next unless select
108
+ end
109
+
110
+
111
+ puts Log.color :blue, dir
112
+ puts Log.color(:magenta, "Creation: ") << File.mtime(File.join(dir, 'command.slurm')).to_s
113
+ puts Log.color(:magenta, "Done: ") << File.mtime(File.join(dir, 'exit.status')).to_s if File.exist?(File.join(dir, 'exit.status'))
114
+ puts Log.color(:magenta, "Exec: ") << (exe || "Missing")
115
+ puts Log.color(:magenta, "CMD: ") << (Log.color(:yellow, cmd) || "Missing")
116
+ puts Log.color(:magenta, "HOME: ") << Log.color(:yellow, container_home) if container_home
117
+ puts Log.color(:magenta, "Job ID: ") << (exit_status ? (exit_status == 0 ? Log.color(:green, "Done") : Log.color(:red, "Error")) : (running_jobs.include?(id) || $norunningjobs ? Log.color(:green, id) : Log.color(:red, id) ))
118
+ puts Log.color(:magenta, "Dependencies: ") << deps * ", " if deps
119
+ puts Log.color(:magenta, "Nodes: ") << nodes * ", "
120
+ puts Log.color(:magenta, "Output: ") << File.exists?(File.join(dir, 'std.out')).to_s << (id.nil? ? "" : " (last update " + Misc.format_seconds(time_diff) + " ago)")
121
+
122
+ if tail && File.exists?(File.join(dir, 'std.err'))
123
+ if exit_status && exit_status != 0
124
+ puts Log.color(:magenta, "First error or exception found: ")
125
+ puts CMD.cmd("grep -i -w 'error\\|[a-z]*exception' #{File.join(dir, 'std.err')} -A #{tail.to_i} |head -n #{tail.to_i}", :no_fail => true).read
126
+ elsif exit_status
127
+ puts Log.color(:magenta, "Completed jobs: ")
128
+ puts CMD.cmd("grep -i -w 'Completed step' #{File.join(dir, 'std.err')} | grep -v 'Retrying dep.' | tail -n #{tail.to_i}", :no_fail => true).read
129
+ else
130
+ puts Log.color(:magenta, "Log tail: ")
131
+ puts CMD.cmd("tail -n #{tail.to_i} #{File.join(dir, 'std.err')}").read
132
+ end
133
+ end
134
+
135
+ count += 1
136
+
137
+ end
138
+
139
+ puts
140
+ puts "Found #{count} jobs"
141
+
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rbbt/util/simpleopt'
4
+ require 'rbbt/workflow'
5
+ require 'rbbt/workflow/usage'
6
+ require 'rbbt/hpc'
7
+ require 'rbbt/hpc/orchestrate'
8
+ require 'time'
9
+
10
+ $slurm_options = SOPT.get <<EOF
11
+ -dr--dry_run Print only the template
12
+ -cj--clean_job Clean job
13
+ --drbbt* Use development version of rbbt
14
+ -sing--singularity Use Singularity
15
+ -ug--user_group* Use alternative user group for group project directory
16
+ -c--contain* Contain in directory (using Singularity)
17
+ -s--sync* Contain in directory and sync jobs
18
+ -e--exclusive Make exclusive use of the node
19
+ -hm--highmem Make use of highmem cores
20
+ -wc--wipe_container* Wipe the jobs from the contain directory
21
+ -CS--contain_and_sync Contain and sync to default locations
22
+ -ci--copy_image When using a container directory, copy image there
23
+ -t--tail Tail the logs
24
+ -q--queue* Queue
25
+ -t--task_cpus* Tasks
26
+ -W--workflows* Additional workflows
27
+ -tm--time* Time
28
+ -R--rules* Orchestration rules
29
+ -rmb--remove_slurm_basedir Remove the SLURM working directory (command, STDIN, exit status, ...)
30
+ EOF
31
+
32
+ class Step
33
+ def run(*args)
34
+ if done?
35
+ self.load
36
+ else
37
+ begin
38
+ Log.debug "Issuing SLURM job for #{self.path}"
39
+ HPC::SLURM.orchestrate_job(self, SOPT::GOT_OPTIONS.merge($slurm_options))
40
+ rescue HPC::SBATCH
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ ARGV.concat ["-W", $slurm_options[:workflows]] if $slurm_options[:workflows]
47
+ load Rbbt.share.rbbt_commands.workflow.task.find
@@ -9,8 +9,9 @@ require 'time'
9
9
  $slurm_options = SOPT.get <<EOF
10
10
  -dr--dry_run Print only the template
11
11
  -cj--clean_job Clean job
12
- --drbbt Use development version of rbbt
12
+ --drbbt* Use development version of rbbt
13
13
  -sing--singularity Use Singularity
14
+ -ug--user_group* Use alternative user group for group project directory
14
15
  -c--contain* Contain in directory (using Singularity)
15
16
  -s--sync* Contain in directory and sync jobs
16
17
  -e--exclusive Make exclusive use of the node
@@ -21,8 +22,9 @@ $slurm_options = SOPT.get <<EOF
21
22
  -t--tail Tail the logs
22
23
  -q--queue* Queue
23
24
  -t--task_cpus* Tasks
25
+ -W--workflows* Additional workflows
24
26
  -tm--time* Time
25
- -S--server* SLURM login node
27
+ -rmb--remove_slurm_basedir Remove the SLURM working directory (command, STDIN, exit status, ...)
26
28
  EOF
27
29
 
28
30
  class Step
@@ -30,9 +32,14 @@ class Step
30
32
  if done?
31
33
  self.load
32
34
  else
33
- Marenostrum::SLURM.run_job(self, SOPT::GOT_OPTIONS.merge($slurm_options))
35
+ begin
36
+ Log.debug "Issuing SLURM job for #{self.path}"
37
+ HPC::SLURM.run_job(self, SOPT::GOT_OPTIONS.merge($slurm_options))
38
+ rescue HPC::SBATCH
39
+ end
34
40
  end
35
41
  end
36
42
  end
37
43
 
44
+ ARGV.concat ["-W", $slurm_options[:workflows]] if $slurm_options[:workflows]
38
45
  load Rbbt.share.rbbt_commands.workflow.task.find
@@ -49,27 +49,27 @@ def pid_msg(pid)
49
49
  end
50
50
 
51
51
 
52
- def status_msg(status)
53
- color = case status.to_sym
54
- when :error, :aborted, :missing, :dead, :broken
55
- :red
56
- when :streaming, :started
57
- :cyan
58
- when :done
59
- :green
60
- when :noinfo
61
- :blue
62
- when :dependencies, :waiting, :setup
63
- :yellow
64
- else
65
- if status.to_s.index ">"
66
- :cyan
67
- else
68
- :cyan
69
- end
70
- end
71
- Log.color(color, status.to_s)
72
- end
52
+ #def status_msg(status)
53
+ # color = case status.to_sym
54
+ # when :error, :aborted, :missing, :dead, :broken
55
+ # :red
56
+ # when :streaming, :started
57
+ # :cyan
58
+ # when :done
59
+ # :green
60
+ # when :noinfo, :notfound
61
+ # :blue
62
+ # when :dependencies, :waiting, :setup
63
+ # :yellow
64
+ # else
65
+ # if status.to_s.index ">"
66
+ # :cyan
67
+ # else
68
+ # :cyan
69
+ # end
70
+ # end
71
+ # Log.color(color, status.to_s)
72
+ #end
73
73
 
74
74
  def input_msg(file, inputs)
75
75
 
@@ -218,7 +218,7 @@ workflows.sort.each do |workflow,tasks|
218
218
  status << Log.color(:red, " (dead)")
219
219
  end
220
220
  end
221
- str << " #{ status_msg status }"
221
+ str << " #{ Step.prov_status_msg status }"
222
222
  str << " (dirty)" if status == 'done' && Workflow.load_step(file).dirty?
223
223
 
224
224
  if inputs and inputs.any?
@@ -13,6 +13,8 @@ Make a job forget all its dependencies and archive their meta-data
13
13
  $ #{$0} [options] <job_path>
14
14
 
15
15
  -h--help Print this help
16
+ -p--purge Purge dependencies
17
+ -r--recursive_purge Purge dependencies recursively
16
18
 
17
19
  EOF
18
20
  if options[:help]
@@ -31,4 +33,11 @@ step = Workflow.load_step path
31
33
 
32
34
  step.archive_deps
33
35
  step.copy_files_dir
36
+ dependencies = step.dependencies
34
37
  step.set_info :dependencies, []
38
+
39
+ if options[:purge]
40
+ dependencies.each do |dependency|
41
+ Step.purge(dependency.path, options[:recursive_purge])
42
+ end
43
+ end
@@ -17,6 +17,7 @@ $ rbbt workflow info <job-result>
17
17
  -a--all Print all info entries
18
18
  -r--recursive Print recursive input values
19
19
  -o--original Print original object
20
+ -w--width* Screen width
20
21
  EOF
21
22
 
22
23
  SOPT.usage if options[:help]
@@ -24,6 +25,7 @@ SOPT.usage if options[:help]
24
25
  file = ARGV.shift
25
26
  all = options.delete :all
26
27
  recursive = options.delete :recursive
28
+ width = (options.delete(:width) || 80).to_i
27
29
 
28
30
  def get_step(file)
29
31
  file = file.sub(/\.(info|files)/,'')
@@ -86,6 +88,7 @@ pid = info[:pid]
86
88
  exception = info[:exception]
87
89
  rest = info.keys - [:inputs, :dependencies, :status, :time_elapsed, :messages, :backtrace, :exception, :pid, :archived_info]
88
90
 
91
+
89
92
  puts Log.color(:magenta, "File") << ": " << step.path
90
93
  puts Log.color(:magenta, "Status") << ": " << status_msg(status) << ((step.aborted? || step.error?) && step.recoverable_error? ? " (recoverable)" : "" ) << (step.dirty? ? " (dirty)" : "")
91
94
  puts Log.color(:magenta, "Pid") << ": " << pid_msg(pid, status.to_s == "done")
@@ -96,14 +99,14 @@ if inputs and inputs.any?
96
99
  inputs.each do |input,value|
97
100
  case value
98
101
  when nil
99
- puts Misc.format_definition_list_item(" " + input.to_s, 'nil', 80, 20, :blue)
102
+ puts Misc.format_definition_list_item(" " + input.to_s, 'nil', width, 20, :blue)
100
103
  when Array
101
- puts Misc.format_definition_list_item(" " + input.to_s, (value.length > 6 ? value[0..5]*"\n" << "\n" << "..." : value * "\n" ), 80, 20, :blue)
104
+ puts Misc.format_definition_list_item(" " + input.to_s, (value.length > 6 ? value[0..5]*"\n" << "\n" << "..." : value * "\n" ), width, 20, :blue)
102
105
  when TrueClass, FalseClass
103
- puts Misc.format_definition_list_item(" " + input.to_s, value.to_s, 80, 20, :blue)
106
+ puts Misc.format_definition_list_item(" " + input.to_s, value.to_s, width, 20, :blue)
104
107
  else
105
108
  text = value.to_s.split("\n")[0..5].compact * "\n\n"
106
- puts Misc.format_definition_list_item(" " + input.to_s, text, 80, 20, :blue)
109
+ puts Misc.format_definition_list_item(" " + input.to_s, text, width, 20, :blue)
107
110
  end
108
111
  end
109
112
  end
@@ -162,16 +165,16 @@ if recursive
162
165
  inputs.each do |input,value|
163
166
  case value
164
167
  when nil
165
- puts Misc.format_definition_list_item(" " << input.to_s, 'nil', 80, 20, :blue)
168
+ puts Misc.format_definition_list_item(" " << input.to_s, 'nil', width, 20, :blue)
166
169
  when Array
167
- puts Misc.format_definition_list_item(" " << input.to_s, (value.length > 6 ? (value[0..5])*"\n\n" << "\n\n" << "..." : value * "\n\n" ), 80, 20, :blue).gsub("\n\n","\n")
170
+ puts Misc.format_definition_list_item(" " << input.to_s, (value.length > 6 ? (value[0..5])*"\n\n" << "\n\n" << "..." : value * "\n\n" ), width, 20, :blue).gsub("\n\n","\n")
168
171
  when TrueClass, FalseClass
169
- puts Misc.format_definition_list_item(" " << input.to_s, value.to_s, 80, 20, :blue)
172
+ puts Misc.format_definition_list_item(" " << input.to_s, value.to_s, width, 20, :blue)
170
173
  else
171
- lines = value.to_s.split("\n").collect{|l| l.length >= 60 ? l[0..45] + " ..." : l }
174
+ lines = value.to_s.split("\n").collect{|l| l.length >= width - 5 ? l[0..width - 5] + " ..." : l }
172
175
  text = lines[0..5].compact * "\n\n"
173
176
  text << "\n\n...\n\n" if lines.length > 6
174
- puts Misc.format_definition_list_item(" " << input.to_s, text, 80, 20, :blue).gsub("\n\n","\n")
177
+ puts Misc.format_definition_list_item(" " << input.to_s, text, width, 20, :blue).gsub("\n\n","\n")
175
178
  end
176
179
  end
177
180
  end