ruport-util 0.6.0 → 0.7.0
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.
- 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
|