ruport-util 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/INSTALL +8 -7
- data/Rakefile +66 -11
- data/bin/csv2ods +53 -0
- data/example/data/blank.ods +0 -0
- data/example/graph_report.rb +6 -2
- data/example/invoice_report.rb +2 -1
- data/example/mailer.rb +7 -7
- data/example/managed_report.rb +5 -5
- data/example/ods.rb +12 -0
- data/lib/ruport/util/generator.rb +2 -11
- data/lib/ruport/util/graph.rb +8 -0
- data/lib/ruport/util/ods.rb +80 -0
- data/lib/ruport/util/report.rb +33 -299
- data/lib/ruport/util/report_manager.rb +2 -3
- data/lib/ruport/util.rb +9 -1
- data/test/helper/layout.rb +55 -0
- data/test/helper/wrap.rb +190 -0
- data/test/helper.rb +27 -0
- data/test/test_format_ods.rb +46 -0
- data/test/test_graph_renderer.rb +21 -31
- data/test/test_invoice.rb +30 -28
- data/test/test_mailer.rb +116 -159
- data/test/test_report.rb +80 -183
- data/test/test_report_manager.rb +32 -29
- metadata +30 -22
- data/example/foo.svg +0 -83
- data/example/form.pdf +0 -160
- data/example/out.pdf +0 -229
- data/test/init.rb +0 -7
data/lib/ruport/util/report.rb
CHANGED
@@ -11,71 +11,6 @@ require "forwardable"
|
|
11
11
|
|
12
12
|
module Ruport
|
13
13
|
|
14
|
-
require 'timeout'
|
15
|
-
|
16
|
-
class Attempt # :nodoc:
|
17
|
-
VERSION = '0.1.0 (vendored)'
|
18
|
-
|
19
|
-
# Number of attempts to make before failing. The default is 3.
|
20
|
-
attr_accessor :tries
|
21
|
-
|
22
|
-
# Number of seconds to wait between attempts. The default is 60.
|
23
|
-
attr_accessor :interval
|
24
|
-
|
25
|
-
# a level which ruport understands.
|
26
|
-
attr_accessor :log_level
|
27
|
-
|
28
|
-
# If set, this increments the interval with each failed attempt by that
|
29
|
-
# number of seconds.
|
30
|
-
attr_accessor :increment
|
31
|
-
|
32
|
-
# If set, the code block is further wrapped in a timeout block.
|
33
|
-
attr_accessor :timeout
|
34
|
-
|
35
|
-
# Determines which exception level to check when looking for errors to
|
36
|
-
# retry. The default is 'Exception' (i.e. all errors).
|
37
|
-
attr_accessor :level
|
38
|
-
|
39
|
-
# :call-seq:
|
40
|
-
# Attempt.new{ |a| ... }
|
41
|
-
#
|
42
|
-
# Creates and returns a new +Attempt+ object. Use a block to set the
|
43
|
-
# accessors.
|
44
|
-
#
|
45
|
-
def initialize
|
46
|
-
@tries = 3 # Reasonable default
|
47
|
-
@interval = 60 # Reasonable default
|
48
|
-
@increment = nil # Should be an int, if provided
|
49
|
-
@timeout = nil # Wrap the code in a timeout block if provided
|
50
|
-
@level = Exception # Level of exception to be caught
|
51
|
-
|
52
|
-
yield self if block_given?
|
53
|
-
end
|
54
|
-
|
55
|
-
def attempt
|
56
|
-
count = 1
|
57
|
-
begin
|
58
|
-
if @timeout
|
59
|
-
Timeout.timeout(@timeout){ yield }
|
60
|
-
else
|
61
|
-
yield
|
62
|
-
end
|
63
|
-
rescue @level => error
|
64
|
-
@tries -= 1
|
65
|
-
if @tries > 0
|
66
|
-
msg = "Error on attempt # #{count}: #{error}; retrying"
|
67
|
-
count += 1
|
68
|
-
@interval += @increment if @increment
|
69
|
-
sleep @interval
|
70
|
-
retry
|
71
|
-
end
|
72
|
-
raise
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
|
78
|
-
|
79
14
|
# === Overview
|
80
15
|
#
|
81
16
|
# The Ruport::Report class provides a high level interface to much of Ruport's
|
@@ -98,41 +33,19 @@ module Ruport
|
|
98
33
|
# renders_as_grouping(:style => :inline)
|
99
34
|
#
|
100
35
|
# def generate
|
101
|
-
# table =
|
36
|
+
# table = Table("foo.csv")
|
102
37
|
# Grouping(table, :by => "username")
|
103
38
|
# end
|
104
39
|
#
|
105
40
|
# end
|
106
41
|
#
|
107
|
-
# report = MyReport.new
|
108
|
-
# report.
|
42
|
+
# report = MyReport.new
|
43
|
+
# report.save_as("bar.pdf")
|
109
44
|
#
|
110
45
|
class Report
|
111
46
|
extend Forwardable
|
112
47
|
include Renderer::Hooks
|
113
48
|
|
114
|
-
# Builds a report instance. If provided a format parameter,
|
115
|
-
# this format will be used by default when rendering the report.
|
116
|
-
#
|
117
|
-
def initialize( format=nil )
|
118
|
-
use_source :default
|
119
|
-
@format = format
|
120
|
-
@report_name = ""
|
121
|
-
@results = ""
|
122
|
-
@file = nil
|
123
|
-
end
|
124
|
-
|
125
|
-
# By default, this file will be used by Report#write.
|
126
|
-
attr_accessor :file
|
127
|
-
|
128
|
-
# This attribute will get the results of Report#generate when the report is
|
129
|
-
# run.
|
130
|
-
#
|
131
|
-
attr_accessor :results
|
132
|
-
|
133
|
-
# This attribute defines which format the Report will render in by default.
|
134
|
-
attr_accessor :format
|
135
|
-
|
136
49
|
# This is a simplified interface to Ruport::Query.
|
137
50
|
#
|
138
51
|
# You can use it to read SQL statements from file or string:
|
@@ -168,9 +81,8 @@ module Ruport
|
|
168
81
|
# See Ruport::Query for details.
|
169
82
|
#
|
170
83
|
def query(sql, options={})
|
171
|
-
options[:
|
172
|
-
options[:
|
173
|
-
q = options[:query_obj] || Query.new(sql, options)
|
84
|
+
options[:source] ||= :default
|
85
|
+
q = options[:query_obj] || Ruport::Query.new(sql, options)
|
174
86
|
if block_given?
|
175
87
|
q.each { |r| yield(r) }
|
176
88
|
elsif options[:as]
|
@@ -179,225 +91,47 @@ module Ruport
|
|
179
91
|
q.result
|
180
92
|
end
|
181
93
|
end
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
#
|
186
|
-
# For example, if you have a data source :test, which is defined as such:
|
187
|
-
#
|
188
|
-
# Ruport::Query.add_source(:test, :dsn => "dbi:mysql:test",
|
189
|
-
# :user => "root" )
|
190
|
-
#
|
191
|
-
#
|
192
|
-
# The following report would use that data source rather than the
|
193
|
-
# <tt>:default</tt> source:
|
194
|
-
#
|
195
|
-
# class MyReport < Ruport::Report
|
196
|
-
#
|
197
|
-
# renders_as_table
|
198
|
-
#
|
199
|
-
# def generate
|
200
|
-
# use_source :test
|
201
|
-
# query "select * from foo"
|
202
|
-
# end
|
203
|
-
#
|
204
|
-
# end
|
205
|
-
def use_source(label)
|
206
|
-
@source = label
|
207
|
-
end
|
208
|
-
|
209
|
-
# Writes the contents of Report#results to file.
|
210
|
-
# If given a string as a second argument, writes that to file, instead.
|
211
|
-
#
|
212
|
-
# Examples:
|
213
|
-
#
|
214
|
-
# # write the results of the report to a file
|
215
|
-
# Report.run { |r| r.write("foo.txt") }
|
216
|
-
#
|
217
|
-
# # write the results in reverse
|
218
|
-
# Report.run { |r| r.write("foo.txt",r.results.reverse) }
|
219
|
-
#
|
220
|
-
def write(my_file=file,my_results=results)
|
221
|
-
File.open(my_file,"w") { |f| f << my_results }
|
94
|
+
|
95
|
+
def renderable_data #:nodoc:
|
96
|
+
generate
|
222
97
|
end
|
98
|
+
|
99
|
+
alias_method :old_as, :as
|
223
100
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
101
|
+
def as(*args,&block)
|
102
|
+
prepare if respond_to?(:prepare)
|
103
|
+
output = old_as(*args,&block)
|
104
|
+
cleanup if respond_to?(:cleanup)
|
105
|
+
return output
|
229
106
|
end
|
230
107
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
options
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
# Allows you to override the default format.
|
241
|
-
#
|
242
|
-
# Example:
|
243
|
-
#
|
244
|
-
# my_report.as(:csv)
|
245
|
-
#
|
246
|
-
def as(format,*args)
|
247
|
-
self.format,old = format, self.format
|
248
|
-
results = run(*args)
|
249
|
-
self.format = old
|
250
|
-
return results
|
251
|
-
end
|
252
|
-
|
253
|
-
# Provides syntactic sugar, allowing to_foo in place of as(:foo)
|
254
|
-
def method_missing(id,*args)
|
255
|
-
id.to_s =~ /^to_(.*)/
|
256
|
-
$1 ? as($1.to_sym,*args) : super
|
257
|
-
end
|
258
|
-
|
259
|
-
# Loads a CSV in from a file.
|
260
|
-
#
|
261
|
-
# Example:
|
262
|
-
#
|
263
|
-
# my_table = load_csv "foo.csv" #=> Data::Table
|
264
|
-
# my_array = load_csv "foo.csv", :as => :array #=> Array
|
265
|
-
#
|
266
|
-
# See also Ruport::Data::Table.load and Table()
|
267
|
-
#
|
268
|
-
def load_csv(file,options={})
|
269
|
-
case options[:as]
|
270
|
-
when :array
|
271
|
-
a = []
|
272
|
-
Data::Table.load(file,options) { |s,r| a << r } ; a
|
108
|
+
def save_as(filename,options={})
|
109
|
+
formats = { "csv" => ["w",:csv], "txt" => ["w",:text],
|
110
|
+
"html" => ["w", :html], "pdf" => ["wb", :pdf ] }
|
111
|
+
|
112
|
+
fo = filename =~ /.*\.(.*)/ && formats[$1]
|
113
|
+
flags = options.delete(:flags)
|
114
|
+
if fo
|
115
|
+
File.open(filename,flags || fo[0]) { |f| f << as(fo[1],options) }
|
273
116
|
else
|
274
|
-
|
117
|
+
File.open(filename,flags || "w") { |f| f << as($1.to_sym,options) }
|
275
118
|
end
|
276
119
|
end
|
277
120
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
# directly.
|
282
|
-
#
|
283
|
-
# Examples:
|
284
|
-
#
|
285
|
-
# @foo = 'greg'
|
286
|
-
# erb "My name is <%= @foo %>" #=> "My name is greg"
|
287
|
-
#
|
288
|
-
# erb "foo.rhtml" #=> contents of evaluated text in foo.rhtml
|
289
|
-
#
|
290
|
-
def erb(s)
|
291
|
-
if s =~ /\.r\w+$/
|
292
|
-
ERB.new(File.read(s)).result(binding)
|
121
|
+
def method_missing(id,*args,&block)
|
122
|
+
if id.to_s =~ /^to_(.*)/
|
123
|
+
as($1.to_sym,*args,&block)
|
293
124
|
else
|
294
|
-
|
125
|
+
super
|
295
126
|
end
|
296
127
|
end
|
297
128
|
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
#
|
302
|
-
# Example:
|
303
|
-
#
|
304
|
-
# my_report.as(:csv)
|
305
|
-
#
|
306
|
-
def as(format,options={})
|
307
|
-
report = new(format)
|
308
|
-
report.run(rendering_options.merge(options))
|
309
|
-
end
|
310
|
-
|
311
|
-
# Provides syntactic sugar, allowing to_foo in place of as(:foo)
|
312
|
-
def method_missing(id,*args)
|
313
|
-
id.to_s =~ /^to_(.*)/
|
314
|
-
$1 ? as($1.to_sym,*args) : super
|
315
|
-
end
|
316
|
-
|
317
|
-
# Defines an instance method which will be run before the
|
318
|
-
# <tt>generate</tt> method when Ruport.run is executed.
|
319
|
-
#
|
320
|
-
def prepare(&block); define_method(:prepare,&block) end
|
321
|
-
|
322
|
-
# Defines an instance method which will be executed by Report.run.
|
323
|
-
#
|
324
|
-
# The return value of this method is assigned to the <tt>results</tt>
|
325
|
-
# attribute.
|
326
|
-
#
|
327
|
-
def generate(&block); define_method(:generate,&block) end
|
328
|
-
|
329
|
-
# Defines an instance method which will be executed after the object is
|
330
|
-
# yielded in Report.run.
|
331
|
-
#
|
332
|
-
def cleanup(&block); define_method(:cleanup,&block) end
|
333
|
-
|
334
|
-
private :prepare, :generate, :cleanup
|
335
|
-
|
336
|
-
# Runs the reports specified. If no reports are specified, then it
|
337
|
-
# creates a new instance via <tt>self.new</tt>.
|
338
|
-
#
|
339
|
-
# Hooks called, in order:
|
340
|
-
# * Report#prepare
|
341
|
-
# * Report#generate #=> return value stored in @results
|
342
|
-
# * yields self to block, if given
|
343
|
-
# * if a renderer is specified, passes along @results and options
|
344
|
-
# * Report#cleanup
|
345
|
-
#
|
346
|
-
# Options:
|
347
|
-
# :reports: A list of reports to run, defaults to a single generic
|
348
|
-
# instance of the current report (self.new).
|
349
|
-
#
|
350
|
-
# :tries:, :timeout:, :interval: Wrappers on attempt.rb
|
351
|
-
#
|
352
|
-
# all other options will be forwarded to a renderer if one is specified
|
353
|
-
# via the Renderer::Hooks methods
|
354
|
-
def run(options={})
|
355
|
-
options[:reports] ||= [self.new]
|
356
|
-
|
357
|
-
formatting_options = ( options.keys -
|
358
|
-
[:reports,:tries,:timeout,:interval])
|
359
|
-
|
360
|
-
fopts = formatting_options.inject({}) { |s,k|
|
361
|
-
s.merge( k => options[k] )
|
362
|
-
}
|
363
|
-
|
364
|
-
|
365
|
-
process = lambda do
|
366
|
-
options[:reports].each { |rep|
|
367
|
-
rep.prepare if rep.respond_to? :prepare
|
368
|
-
rep.results = rep.generate
|
369
|
-
|
370
|
-
if renderer
|
371
|
-
rep.results =
|
372
|
-
renderer.render(rep.format,rendering_options.merge(fopts)) { |r|
|
373
|
-
r.data = rep.results
|
374
|
-
}
|
375
|
-
end
|
376
|
-
|
377
|
-
yield(rep) if block_given?
|
378
|
-
rep.cleanup if rep.respond_to? :cleanup
|
379
|
-
}
|
380
|
-
end
|
381
|
-
|
382
|
-
if options[:tries] && (options[:interval] || options[:timeout])
|
383
|
-
code = Attempt.new { |a|
|
384
|
-
a.tries = options[:tries]
|
385
|
-
a.interval = options[:interval] if options[:interval]
|
386
|
-
a.timeout = options[:timeout] if options[:timeout]
|
387
|
-
}
|
388
|
-
code.attempt(&process)
|
389
|
-
else
|
390
|
-
process.call
|
391
|
-
end
|
129
|
+
def add_source(*args)
|
130
|
+
Ruport::Query.add_source(*args)
|
131
|
+
end
|
392
132
|
|
393
|
-
|
394
|
-
|
395
|
-
outs.last
|
396
|
-
else
|
397
|
-
outs
|
398
|
-
end
|
399
|
-
|
400
|
-
end
|
133
|
+
def self.generate
|
134
|
+
yield(new)
|
401
135
|
end
|
402
136
|
end
|
403
137
|
end
|
@@ -14,8 +14,7 @@ module Ruport
|
|
14
14
|
#
|
15
15
|
# just kidding, use models#find or reports#find
|
16
16
|
def self.[](name)
|
17
|
-
reports.find
|
18
|
-
models.find { |m| m.name.eql?(name) }
|
17
|
+
(reports + models).find{|n| n.name == name }
|
19
18
|
end
|
20
19
|
|
21
20
|
def self.models
|
@@ -46,4 +45,4 @@ class Ruport::Report
|
|
46
45
|
def self.acts_as_managed_report
|
47
46
|
Ruport::ReportManager.add_report(self)
|
48
47
|
end
|
49
|
-
end
|
48
|
+
end
|
data/lib/ruport/util.rb
CHANGED
@@ -1,8 +1,15 @@
|
|
1
1
|
module Ruport
|
2
2
|
module Util
|
3
|
-
VERSION = "0.
|
3
|
+
VERSION = "0.7.0"
|
4
|
+
|
5
|
+
file = __FILE__
|
6
|
+
file = File.readlink(file) if File.symlink?(file)
|
7
|
+
dir = File.dirname(file)
|
8
|
+
BASEDIR = File.expand_path(File.join(dir, '..', '..'))
|
9
|
+
LIBDIR = File.expand_path(File.join(dir, '..'))
|
4
10
|
end
|
5
11
|
end
|
12
|
+
|
6
13
|
require "ruport/util/report"
|
7
14
|
require "ruport/util/graph"
|
8
15
|
require "ruport/util/invoice"
|
@@ -11,3 +18,4 @@ require "ruport/util/mailer"
|
|
11
18
|
require "ruport/util/bench"
|
12
19
|
require "ruport/util/generator"
|
13
20
|
require "ruport/util/pdf/form"
|
21
|
+
require "ruport/util/ods"
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'test/helper/wrap'
|
2
|
+
|
3
|
+
class SpecLayout
|
4
|
+
attr_accessor :base, :layout, :files
|
5
|
+
|
6
|
+
def initialize(base, layout)
|
7
|
+
@base, @layout = base, layout
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
SpecWrap.new(@files).run
|
12
|
+
end
|
13
|
+
|
14
|
+
def gather
|
15
|
+
@files = gather_files(@base, @layout)
|
16
|
+
end
|
17
|
+
|
18
|
+
def gather_files(base, layout)
|
19
|
+
files = Set.new
|
20
|
+
base = File.expand_path(base)
|
21
|
+
|
22
|
+
layout.each do |key, value|
|
23
|
+
if value.is_a?(Hash)
|
24
|
+
files += gather_files(base/key, value)
|
25
|
+
else
|
26
|
+
glob = base/key/"#{value}.rb"
|
27
|
+
files += Dir[glob].map{|f| File.expand_path(f)}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
files.reject{|f| File.directory?(f)}
|
32
|
+
end
|
33
|
+
|
34
|
+
def clean
|
35
|
+
@files = clean_files(@files, @layout)
|
36
|
+
end
|
37
|
+
|
38
|
+
def clean_files(files, layout)
|
39
|
+
layout.each do |key, value|
|
40
|
+
if value.is_a?(Hash)
|
41
|
+
clean_files(files, value)
|
42
|
+
elsif files
|
43
|
+
files.dup.each do |file|
|
44
|
+
name = File.basename(file, File.extname(file))
|
45
|
+
dir = file.gsub(File.extname(file), '')
|
46
|
+
if name == key and File.directory?(dir)
|
47
|
+
files.delete(file)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
files
|
54
|
+
end
|
55
|
+
end
|
data/test/helper/wrap.rb
ADDED
@@ -0,0 +1,190 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'set'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubygems'
|
6
|
+
rescue LoadError
|
7
|
+
end
|
8
|
+
|
9
|
+
begin
|
10
|
+
require 'systemu'
|
11
|
+
rescue LoadError
|
12
|
+
puts "Please install systemu for better-looking results"
|
13
|
+
|
14
|
+
# small drop-in replacement for systemu... far from perfect though, so please
|
15
|
+
# install the library
|
16
|
+
|
17
|
+
def systemu command
|
18
|
+
stdout = `#{command} 2>&1`
|
19
|
+
status, stdout, stderr = $?, stdout, ''
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class String
|
24
|
+
{ :red => 31,
|
25
|
+
:green => 32,
|
26
|
+
:yellow => 33,
|
27
|
+
}.each do |key, value|
|
28
|
+
define_method key do
|
29
|
+
"\e[#{value}m" + self + "\e[0m"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def /(str)
|
34
|
+
File.join(self, str.to_s)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Array
|
39
|
+
def commonize
|
40
|
+
snips, rest = map{|s| [s[0,1], s[1..-1]]}.transpose
|
41
|
+
unless snips.uniq.size != 1 or rest.any?{|r| File.basename(r) == r}
|
42
|
+
rest.commonize
|
43
|
+
else
|
44
|
+
self.map{|e| e.gsub(/^\//, '')}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def namize
|
49
|
+
commonize.map do |e|
|
50
|
+
dir = File.dirname(e)
|
51
|
+
file = File.basename(e, File.extname(e))
|
52
|
+
( dir / file ).gsub(/^\.\//, '')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
$stdout.sync = true
|
58
|
+
|
59
|
+
class SpecWrap
|
60
|
+
def initialize(*files)
|
61
|
+
@files = files.flatten
|
62
|
+
@names = @files.namize
|
63
|
+
@specs = Hash[*@files.zip(@names).flatten]
|
64
|
+
@done = Set.new
|
65
|
+
end
|
66
|
+
|
67
|
+
def run
|
68
|
+
@specs.sort_by{|s| s.last}.each do |file, name|
|
69
|
+
spec = SpecFile.new(file, name, term_width)
|
70
|
+
spec.run
|
71
|
+
spec.short_summary
|
72
|
+
@done << spec
|
73
|
+
end
|
74
|
+
|
75
|
+
@done.sort_by{|d| d.name}.each do |spec|
|
76
|
+
puts(spec.long_summary) if spec.failed?
|
77
|
+
end
|
78
|
+
|
79
|
+
summarize
|
80
|
+
end
|
81
|
+
|
82
|
+
def summarize
|
83
|
+
total_passed = @done.inject(0){|s,v| s + v.passed }
|
84
|
+
total_failed = @done.inject(0){|s,v| s + v.failed }
|
85
|
+
total_specs = total_failed + total_passed
|
86
|
+
|
87
|
+
puts "#{total_specs} examples, #{total_failed} failures"
|
88
|
+
puts
|
89
|
+
|
90
|
+
if total_failed.nonzero?
|
91
|
+
failed = @done.select{|d| d.failed.nonzero? or d.passed.zero?}.map{|f| f.name.red }
|
92
|
+
puts "These failed: #{failed.join(', ')}"
|
93
|
+
exit 1
|
94
|
+
else
|
95
|
+
puts("No failing examples, let's add some tests!")
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def term_width
|
100
|
+
@names.sort_by{|s| s.size }.last.size
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class SpecFile
|
105
|
+
attr_reader :file, :name, :passed, :failed, :mark_passed
|
106
|
+
|
107
|
+
def initialize file, name, term_width
|
108
|
+
@file, @name, @term_width = file, name, term_width
|
109
|
+
@inc = $:.map{|e| "-I#{e}" }.join(" ")
|
110
|
+
end
|
111
|
+
|
112
|
+
def run
|
113
|
+
init
|
114
|
+
execute
|
115
|
+
parse
|
116
|
+
done
|
117
|
+
self
|
118
|
+
end
|
119
|
+
|
120
|
+
def init
|
121
|
+
print "Running #@name... ".ljust(@term_width + 20)
|
122
|
+
end
|
123
|
+
|
124
|
+
def execute
|
125
|
+
@status, @stdout, @stderr = systemu("ruby #@inc #@file")
|
126
|
+
end
|
127
|
+
|
128
|
+
def done
|
129
|
+
@ran = true
|
130
|
+
end
|
131
|
+
|
132
|
+
def short_summary
|
133
|
+
f = lambda{|n| n.to_s.rjust(3) }
|
134
|
+
total = f[@passed + @failed] rescue nil
|
135
|
+
failed, passed = f[@failed], f[@passed]
|
136
|
+
color = :red
|
137
|
+
width = 22
|
138
|
+
|
139
|
+
if total_failure?
|
140
|
+
text = 'total failure'.center(width)
|
141
|
+
elsif failed?
|
142
|
+
text = "#{total} specs - #{failed} failed".rjust(width)
|
143
|
+
if @stdout =~ /Usually you should not worry about this failure, just install the/
|
144
|
+
lib = @stdout.scan(/^no such file to load -- (.*?)$/).flatten.first
|
145
|
+
text = "needs #{lib}".center(width)
|
146
|
+
@mark_passed = true
|
147
|
+
end
|
148
|
+
elsif (not @mark_passed) and succeeded?
|
149
|
+
color = :green
|
150
|
+
text = "#{total} specs - all passed".rjust(width)
|
151
|
+
end
|
152
|
+
|
153
|
+
puts "[ #{text.send(color)} ]"
|
154
|
+
end
|
155
|
+
|
156
|
+
def long_summary
|
157
|
+
puts "[ #@name ]".center(80, '-'), "ExitStatus:".yellow
|
158
|
+
pp @status
|
159
|
+
puts "StdOut:".yellow, @stdout, "StdErr:".yellow, @stderr
|
160
|
+
end
|
161
|
+
|
162
|
+
def parse
|
163
|
+
@passed = 0
|
164
|
+
@failed = 0
|
165
|
+
found = false
|
166
|
+
@stdout.grep(/(\d+) examples?, (\d+) failures?/)
|
167
|
+
@passed, @failed = $1.to_i, $2.to_i
|
168
|
+
end
|
169
|
+
|
170
|
+
def failed?
|
171
|
+
not succeeded?
|
172
|
+
end
|
173
|
+
|
174
|
+
def total_failure?
|
175
|
+
succeeded? == nil
|
176
|
+
end
|
177
|
+
|
178
|
+
def succeeded?
|
179
|
+
run unless @ran
|
180
|
+
return @mark_passed unless @mark_passed.nil?
|
181
|
+
crits = [
|
182
|
+
[@status.exitstatus.zero?, @stderr.empty?],
|
183
|
+
[@passed, @failed],
|
184
|
+
[@passed.nonzero?, @failed.zero?],
|
185
|
+
]
|
186
|
+
crits.all?{|c| c.all? }
|
187
|
+
rescue
|
188
|
+
nil
|
189
|
+
end
|
190
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
begin
|
2
|
+
require "rubygems"
|
3
|
+
rescue LoadError
|
4
|
+
nil
|
5
|
+
end
|
6
|
+
|
7
|
+
require "spec"
|
8
|
+
require 'ruport'
|
9
|
+
this = File.dirname(__FILE__)
|
10
|
+
lib = File.expand_path(File.join(this, '..', 'lib'))
|
11
|
+
$LOAD_PATH.unshift(lib)
|
12
|
+
require "ruport/util"
|
13
|
+
|
14
|
+
# Use this to require optional dependencies where tests are expected to fail if
|
15
|
+
# a library is not installed, for example Hpricot or Scruffy.
|
16
|
+
# It will be parsed by the wrapper and marked.
|
17
|
+
|
18
|
+
def testcase_requires(*following)
|
19
|
+
following.each do |file|
|
20
|
+
require(file.to_s)
|
21
|
+
end
|
22
|
+
rescue LoadError => ex
|
23
|
+
puts ex
|
24
|
+
puts "Can't run #{$0}: #{ex}"
|
25
|
+
puts "Usually you should not worry about this failure, just install the"
|
26
|
+
puts "library and try again (if you want to use that feature later on)"
|
27
|
+
end
|