utils 0.2.4 → 0.6.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,112 +2,171 @@ require 'tins/terminal'
2
2
  require 'term/ansicolor'
3
3
 
4
4
  begin
5
- begin
6
- require 'rspec'
7
- rescue LoadError
8
- end
9
- require 'rspec/core/formatters/base_text_formatter'
10
- rescue LoadError
11
- end
12
-
13
- if defined?(RSpec) && defined?(RSpec::Core::Formatters::BaseTextFormatter)
5
+ require 'rspec/core'
6
+ require 'rspec/core/formatters'
7
+ rescue LoadError => e
8
+ $DEBUG and warn "Caught #{e.class}: #{e}"
9
+ else
14
10
  module Utils
15
- class LineFormatter < RSpec::Core::Formatters::BaseTextFormatter
16
- def start(example_count)
17
- super
18
- filename = 'errors.lst'
19
- output.puts "Storing error list in #{filename.inspect}: "
20
- @errors_lst = File.new(filename, 'w')
21
- @errors_lst.sync = true
22
- end
23
-
24
- def close
25
- super
26
- @errors_lst.puts "\n#{?= * 75}\nFinished in #{format_duration(duration)}\n"
27
- @errors_lst.puts summary_line(example_count, failure_count, pending_count)
28
- @errors_lst.close
29
- end
30
-
31
- def example_passed(example)
32
- super
33
- output.puts format_line(example)
34
- end
35
-
36
- def example_pending(example)
37
- super
38
- output.puts format_line(example)
39
- end
40
-
41
- def example_failed(example)
42
- super
43
- dump_line_to_error_file(example)
44
- output.puts format_line(example)
45
- dump_failure_info(example)
46
- end
47
-
48
- def dump_failures
49
- end
50
-
51
- def dump_pending
52
- end
53
-
54
- def dump_commands_to_rerun_failed_examples
55
- end
56
-
57
- private
58
-
59
- def dump_failure_for_error_file(example)
60
- result = ''
61
- exception = example.execution_result[:exception]
62
- exception_class_name = exception_class_name_for(exception)
63
- result << "#{long_padding}Failure/Error: #{read_failed_line(exception, example).strip}\n"
64
- result << "#{long_padding}#{exception_class_name}:\n" unless exception_class_name =~ /RSpec/
65
- exception.message.to_s.split("\n").each { |line| result << "#{long_padding} #{line}\n" } if exception.message
66
- result
67
- end
68
-
69
- def dump_line_to_error_file(example)
70
- @errors_lst.flock File::LOCK_EX
71
- @errors_lst.puts "%s\n%3.3fs %s\n%s\n%s" % [
72
- location(example), run_time(example), example.full_description,
73
- Term::ANSIColor.uncolored(dump_failure_for_error_file(example)),
74
- (%w[ {{{ ] +
75
- format_backtrace(example.execution_result[:exception].backtrace, example) +
76
- %w[ }}} ]) * ?\n
77
- ]
78
- ensure
79
- @errors_lst.flock File::LOCK_UN
80
- end
81
-
82
- def run_time(example)
83
- example.execution_result[:run_time]
84
- end
85
-
86
- def format_line(example)
87
- description =
88
- if ENV['VERBOSE'].to_i == 1
89
- example.full_description
11
+ class LineFormatter
12
+ ::RSpec::Core::Formatters.register self, :start, :close,
13
+ :example_passed, :example_pending, :example_failed, :dump_summary
14
+
15
+ def initialize(output)
16
+ @output = output
17
+ @output.sync = true
18
+ filename = 'errors.lst'
19
+ @errors_lst = File.new(filename, 'w')
20
+ @errors_lst.sync = true
21
+ end
22
+
23
+ attr_reader :output
24
+
25
+ def start(_ignore)
26
+ output.puts "Storing error list in #{@errors_lst.path.inspect}: "
27
+ output.puts ?- * Tins::Terminal.columns
28
+ end
29
+
30
+ def close(_ignore)
31
+ @errors_lst.close
32
+ end
33
+
34
+ def dump_summary(summary)
35
+ line = summary_line(summary)
36
+ @errors_lst.puts ?= * 80, line
37
+ output.puts ?= * Tins::Terminal.columns, line
38
+ end
39
+
40
+ def example_passed(example)
41
+ output.puts format_line(example)
42
+ end
43
+
44
+ def example_pending(example)
45
+ output.puts format_line(example)
46
+ end
47
+
48
+ def example_failed(example)
49
+ dump_failure_to_error_file(example)
50
+ output.puts format_line(example)
51
+ dump_failure(example)
52
+ end
53
+
54
+ private
55
+
56
+ def summary_line(summary)
57
+ failure_percentage = 100 * summary.failure_count.to_f / summary.example_count
58
+ failure_percentage.nan? and failure_percentage = 0.0
59
+ pending_percentage = 100 * summary.pending_count.to_f / summary.example_count
60
+ pending_percentage.nan? and pending_percentage = 0.0
61
+ "%u of %u (%.2f %%) failed, %u pending (%.2f %%) in %.3f seconds" % [
62
+ summary.failure_count,
63
+ summary.example_count,
64
+ failure_percentage,
65
+ summary.pending_count,
66
+ pending_percentage,
67
+ summary.duration,
68
+ ]
69
+ end
70
+
71
+ def dump_failure(example)
72
+ output.puts(
73
+ description(example, full: true),
74
+ dump_failure_for_example(example)
75
+ )
76
+ end
77
+
78
+ def read_failed_line(example)
79
+ ''.strip
80
+ end
81
+
82
+ def dump_failure_for_example(example)
83
+ result = ''
84
+ exception = execution_result(example).exception
85
+ exception_class_name = exception.class.name
86
+ result << "Failure/Error: #{read_failed_line(example)}\n"
87
+ result << "#{exception_class_name}:\n" unless exception_class_name =~ /RSpec/
88
+ if m = exception.message
89
+ m.to_s.split("\n").each { |line| result << " #{line}\n" }
90
+ end
91
+ result
92
+ end
93
+
94
+ def format_backtrace(example, folding: false, limit: nil)
95
+ backtrace = execution_result(example).exception.backtrace
96
+ backtrace.nil? and return ''
97
+ if limit
98
+ backtrace = backtrace[0, limit]
99
+ end
100
+ result = []
101
+ folding and result << '{{{'
102
+ for line in backtrace
103
+ result << RSpec::Core::Metadata::relative_path(line)
104
+ end
105
+ folding and result << '}}}'
106
+ result * ?\n
107
+ end
108
+
109
+ def dump_failure_to_error_file(example)
110
+ @errors_lst.flock File::LOCK_EX
111
+ @errors_lst.puts "%s\n%3.3fs %s\n%s\n%s" % [
112
+ location(example), run_time(example), description(example, full: true),
113
+ dump_failure_for_example(example), format_backtrace(example, folding: true)
114
+ ]
115
+ ensure
116
+ @errors_lst.flock File::LOCK_UN
117
+ end
118
+
119
+ def execution_result(example)
120
+ example.example.metadata[:execution_result]
121
+ end
122
+
123
+ def description(example, full: ENV['VERBOSE'].to_i == 1)
124
+ if full
125
+ example.example.full_description
126
+ else
127
+ example.example.description
128
+ end
129
+ end
130
+
131
+ def run_time(example)
132
+ execution_result(example).run_time
133
+ end
134
+
135
+ def format_line(example)
136
+ args = [ location(example), run_time(example), description(example) ]
137
+ uncolored = "%s # S %3.3fs %s" % args
138
+ uncolored = uncolored[0, Tins::Terminal.columns]
139
+ case execution_result(example).status
140
+ when :passed
141
+ success_color(uncolored)
142
+ when :failed
143
+ failure_color(uncolored)
144
+ when :pending
145
+ pending_color(uncolored)
90
146
  else
91
- example.description
147
+ uncolored % args
92
148
  end
93
- args = [ location(example), run_time(example), description ]
94
- uncolored = "%s # S %3.3fs %s" % args
95
- uncolored = uncolored[0, Tins::Terminal.columns]
96
- case example.execution_result[:status]
97
- when 'passed'
98
- success_color(uncolored)
99
- when 'failed'
100
- failure_color(uncolored)
101
- when 'pending'
102
- pending_color(uncolored)
103
- else
104
- uncolored % args
105
- end
106
- end
107
-
108
- def location(example)
109
- RSpec::Core::Metadata::relative_path(example.location)
110
- end
149
+ end
150
+
151
+ def success_color(text)
152
+ Term::ANSIColor.green(text)
153
+ end
154
+
155
+ def failure_color(text)
156
+ Term::ANSIColor.red(text)
157
+ end
158
+
159
+ def pending_color(text)
160
+ Term::ANSIColor.yellow(text)
161
+ end
162
+
163
+ def location(example)
164
+ location = example.example.metadata[:location]
165
+ unless location.include?(?/)
166
+ location = example.example.metadata[:example_group][:location]
167
+ end
168
+ RSpec::Core::Metadata::relative_path(location)
169
+ end
111
170
  end
112
171
  end
113
172
  end
@@ -9,6 +9,8 @@ module Utils
9
9
  @pattern = @pattern.gsub(/[^#{@cset}]/, '') if @cset
10
10
  end
11
11
 
12
+ attr_reader :matcher
13
+
12
14
  def method_missing(*a, &b)
13
15
  @matcher.__send__(*a, &b)
14
16
  end
@@ -7,19 +7,9 @@ end
7
7
  module Utils
8
8
  class ProbeServer
9
9
  class Job
10
-
11
- class << self
12
- attr_writer :colorize
13
-
14
- def colorize?
15
- !!@colorize
16
- end
17
- end
18
- self.colorize = false
19
-
20
10
  def initialize(probe_server, args)
21
11
  @id = probe_server.next_job_id
22
- @args = args
12
+ @args = Array(args)
23
13
  end
24
14
 
25
15
  attr_reader :id
@@ -37,18 +27,15 @@ module Utils
37
27
  end
38
28
 
39
29
  def ok_colorize(string)
40
- return string unless self.class.colorize?
41
30
  case @ok
42
31
  when false then string.white.on_red
43
32
  when true then string.black.on_green
44
- else string.black.on_yellow
33
+ else string
45
34
  end
46
35
  end
47
36
 
48
37
  def inspect
49
- ok_colorize(
50
- "#<Job id=#{id} args=#{args.inspect} ok=#{ok}>"
51
- )
38
+ ok_colorize("Job##{id} #{args.map { |a| a.include?(' ') ? a.inspect : a } * ' '}")
52
39
  end
53
40
 
54
41
  alias to_s inspect
@@ -62,6 +49,12 @@ module Utils
62
49
  Thread.new { work_loop }
63
50
  end
64
51
 
52
+ def print(*msg)
53
+ if msg.first !~ /^irb: warn: can't alias / # shut your god d*mn wh*re mouth
54
+ super
55
+ end
56
+ end
57
+
65
58
  def start
66
59
  output_message "Starting probe server listening to #{@uri.inspect}.", type: :info
67
60
  DRb.start_service(@uri, self)
@@ -69,7 +62,8 @@ module Utils
69
62
  DRb.thread.join
70
63
  rescue Interrupt
71
64
  ARGV.clear << '-f'
72
- output_message %{\nEntering interactive mode: Type "commands" to get help for the commands.}, type: :info
65
+ output_message %{\nEntering interactive mode.}, type: :info
66
+ help
73
67
  begin
74
68
  old, $VERBOSE = $VERBOSE, nil
75
69
  examine(self)
@@ -88,40 +82,24 @@ module Utils
88
82
 
89
83
  annotate :doc
90
84
 
91
- def commands
92
- annotations = self.class.doc_annotations.sort_by(&:first)
93
- max_size = annotations.map { |a| a.first.size }.max
94
- output_message annotations.map { |n, v| "#{n.to_s.ljust(max_size + 1)}#{v}" }
95
- end
85
+ annotate :shortcut
96
86
 
97
- doc 'Pause processing of the job queue.'
98
- def pause
99
- mutex.lock
100
- true
101
- rescue ThreadError
102
- false
103
- end
104
-
105
- doc 'Continue processing of the job queue.'
106
- def continue
107
- mutex.unlock
108
- true
109
- rescue ThreadError
110
- false
111
- end
112
-
113
- doc 'Return the currently running job.'
114
- def job
115
- queue_synchronize do
116
- @job
117
- end
118
- end
119
-
120
- def next_job_id
121
- @current_job_id += 1
87
+ doc 'Display this help.'
88
+ shortcut :h
89
+ def help
90
+ docs = doc_annotations.sort_by(&:first)
91
+ docs_size = docs.map { |a| a.first.size }.max
92
+ format = '%-20s %-3s %s'
93
+ output_message [
94
+ (format % %w[ command sho description ]).on_color(20).white
95
+ ] << docs.map { |cmd, doc|
96
+ shortcut = shortcut_of(cmd) and shortcut = "(#{shortcut})"
97
+ format % [ cmd, shortcut, doc ]
98
+ }
122
99
  end
123
100
 
124
101
  doc 'Enqueue a new job with the argument array <job_args>.'
102
+ shortcut :e
125
103
  def job_enqueue(job_args)
126
104
  job = Job.new(self, job_args)
127
105
  output_message " → #{job.inspect} enqueued.", type: :info
@@ -130,35 +108,15 @@ module Utils
130
108
  alias enqueue job_enqueue
131
109
 
132
110
  doc 'Send the <signal> to the process that is working on the current job, if any.'
133
- def job_kill(signal = :TERM)
134
- @pid and Process.kill signal, @pid
135
- end
136
-
137
- doc 'Shutdown the server.'
111
+ doc 'Quit the server.'
112
+ shortcut :q
138
113
  def shutdown
139
114
  output_message "Server was shutdown down – HARD!", type: :warn
140
- exit! 23
141
- end
142
-
143
- doc 'List the currently pending jobs waiting to be run.'
144
- def jobs_list
145
- output_message @jobs_queue.instance_variable_get(:@que)
146
- end
147
-
148
- doc 'Clear all pending jobs.'
149
- def jobs_clear
150
- queue_synchronize do
151
- unless @jobs_queue.empty?
152
- @jobs_queue.clear
153
- output_message "Cleared all queued jobs.", type: :warn
154
- true
155
- else
156
- false
157
- end
158
- end
115
+ exit 23
159
116
  end
160
117
 
161
118
  doc 'Repeat the job with <job_id> or the last, it will be assigned a new id, though.'
119
+ shortcut :r
162
120
  def job_repeat(job_id = @history.last)
163
121
  Job === job_id and job_id = job_id.id
164
122
  if old_job = @history.find { |job| job.id == job_id }
@@ -170,6 +128,7 @@ module Utils
170
128
  end
171
129
 
172
130
  doc 'List the history of run jobs.'
131
+ shortcut :l
173
132
  def history_list
174
133
  output_message @history
175
134
  end
@@ -180,19 +139,39 @@ module Utils
180
139
  true
181
140
  end
182
141
 
142
+ class LogWrapper < BasicObject
143
+ def initialize(server, object)
144
+ @server, @object = server, object
145
+ end
146
+
147
+ def []=(name, value)
148
+ name, value = name.to_s, value.to_s
149
+ @server.output_message("Setting #{name}=#{value.inspect}.", type: :info)
150
+ @object[name] = value
151
+ end
152
+
153
+ def method_missing(*a, &b)
154
+ @object.__send__(*a, &b)
155
+ end
156
+ end
157
+
183
158
  doc "The environment of the server process, use env['a'] = 'b' and env['a']."
184
- def env
185
- ENV
159
+ memoize_method def env
160
+ LogWrapper.new(self, ENV)
186
161
  end
187
162
 
188
- private
163
+ doc "Clear the terminal screen"
164
+ shortcut :c
165
+ def clear
166
+ system "clear"
167
+ end
189
168
 
190
- def mutex
191
- @jobs_queue.instance_variable_get(:@mutex)
169
+ for (method_name, shortcut) in shortcut_annotations
170
+ alias_method shortcut, method_name
192
171
  end
193
172
 
194
- def queue_synchronize(&block)
195
- mutex.synchronize(&block)
173
+ def next_job_id
174
+ @current_job_id += 1
196
175
  end
197
176
 
198
177
  def output_message(msg, type: nil)
@@ -204,7 +183,7 @@ module Utils
204
183
  when :info
205
184
  msg.on_color(20).white
206
185
  when :warn
207
- msg.on_color(40).white
186
+ msg.on_color(94).white
208
187
  when :failure
209
188
  msg.on_color(124).blink.white
210
189
  else
@@ -215,10 +194,11 @@ module Utils
215
194
  self
216
195
  end
217
196
 
197
+ private
198
+
218
199
  def run_job(job)
219
- @pid = fork { exec(*cmd(job.args)) }
220
- output_message " → #{job.inspect} now running with pid #@pid.", type: :info
221
- Process.wait @pid
200
+ output_message " #{job.inspect} now running.", type: :info
201
+ system *cmd(job.args)
222
202
  message = " → #{job.inspect} was just run"
223
203
  if $?.success?
224
204
  job.ok = true