pre-commit 0.11.0 → 0.12.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.
- checksums.yaml +4 -4
- data/README.md +14 -5
- data/bin/pre-commit +7 -5
- data/lib/plugins/pre_commit/checks/before_all.rb +13 -0
- data/lib/plugins/pre_commit/checks/ci.rb +12 -0
- data/lib/plugins/pre_commit/checks/closure.rb +19 -0
- data/lib/plugins/pre_commit/checks/coffeelint.rb +19 -0
- data/lib/plugins/pre_commit/checks/console_log.rb +13 -0
- data/lib/plugins/pre_commit/checks/debugger.rb +21 -0
- data/lib/plugins/pre_commit/checks/gemfile_path.rb +14 -0
- data/lib/plugins/pre_commit/checks/js.rb +36 -0
- data/lib/plugins/pre_commit/checks/jshint.rb +24 -0
- data/lib/plugins/pre_commit/checks/jslint.rb +24 -0
- data/lib/plugins/pre_commit/checks/local.rb +13 -0
- data/lib/plugins/pre_commit/checks/merge_conflict.rb +12 -0
- data/lib/plugins/pre_commit/checks/migration.rb +38 -0
- data/lib/plugins/pre_commit/checks/nb_space.rb +23 -0
- data/lib/plugins/pre_commit/checks/php.rb +27 -0
- data/lib/plugins/pre_commit/checks/pry.rb +12 -0
- data/lib/plugins/pre_commit/checks/rspec_focus.rb +13 -0
- data/lib/plugins/pre_commit/checks/rubocop.rb +46 -0
- data/lib/plugins/pre_commit/checks/ruby_symbol_hashrockets.rb +16 -0
- data/lib/plugins/pre_commit/checks/tabs.rb +14 -0
- data/lib/plugins/pre_commit/checks/whitespace.rb +21 -0
- data/lib/pre-commit/checks.rb +66 -58
- data/lib/pre-commit/cli.rb +29 -2
- data/lib/pre-commit/support/templates/automatic_hook +35 -0
- data/lib/pre-commit/support/templates/default_hook +35 -0
- data/lib/pre-commit/support/templates/manual_hook +14 -0
- metadata +101 -26
- data/lib/pre-commit/checks/ci_check.rb +0 -10
- data/lib/pre-commit/checks/closure_check.rb +0 -13
- data/lib/pre-commit/checks/console_log_check.rb +0 -11
- data/lib/pre-commit/checks/debugger_check.rb +0 -19
- data/lib/pre-commit/checks/gemfile_path_check.rb +0 -12
- data/lib/pre-commit/checks/js_check.rb +0 -35
- data/lib/pre-commit/checks/jshint_check.rb +0 -26
- data/lib/pre-commit/checks/jslint_check.rb +0 -22
- data/lib/pre-commit/checks/local_check.rb +0 -11
- data/lib/pre-commit/checks/merge_conflict_check.rb +0 -10
- data/lib/pre-commit/checks/migration_check.rb +0 -33
- data/lib/pre-commit/checks/nb_space_check.rb +0 -21
- data/lib/pre-commit/checks/php_check.rb +0 -25
- data/lib/pre-commit/checks/pry_check.rb +0 -10
- data/lib/pre-commit/checks/rspec_focus_check.rb +0 -11
- data/lib/pre-commit/checks/rubocop_check.rb +0 -38
- data/lib/pre-commit/checks/ruby_symbol_hashrockets.rb +0 -14
- data/lib/pre-commit/checks/tabs_check.rb +0 -12
- data/lib/pre-commit/checks/whitespace_check.rb +0 -16
- data/lib/pre-commit/support/templates/pre-commit-hook +0 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c7355c40a01a77e3e1a80b3aa54e0fad68f14384
|
4
|
+
data.tar.gz: 523aafecb538cb6ee6212a8aec9b3bb272475c53
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 442f543f6a1c50f4346bbb7ff94e0340c255ba81baff3f7a24ed9159f183173302ada1b5dfe92186c86fa587c2501c8318b6483dfbf94f65d032612ed9546f1c
|
7
|
+
data.tar.gz: 066024f84516424170d618568f1177d8ce34c3293131f348772af247143787033e0082341a9979aa6621470cb6b29b8aae88736e9068f60b0068efff0ec733ab
|
data/README.md
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
A better pre-commit hook for git.
|
2
2
|
|
3
|
-
[]
|
4
|
-
[](https://rubygems.org/gems/pre-commit)
|
4
|
+
[](https://codeclimate.com/github/jish/pre-commit)
|
5
|
+
[](https://coveralls.io/r/jish/pre-commit?branch=master)
|
6
|
+
[](https://travis-ci.org/jish/pre-commit)
|
7
|
+
[](https://gemnasium.com/jish/pre-commit)
|
8
|
+
[](http://rubydoc.info/gems/pre-commit/frames)
|
5
9
|
|
6
10
|
## Installation
|
7
11
|
|
@@ -36,6 +40,8 @@ These are the available checks:
|
|
36
40
|
* migrations (Will make sure you check in the proper files after creating a Rails migration)
|
37
41
|
* ci (Will run the `pre_commit:ci` rake task and pass or fail accordingly)
|
38
42
|
* rubocop (Check ruby code style using the rubocop gem. Rubocop must be installed)
|
43
|
+
* before_all (Check your RSpec tests for the use of `before(:all)`)
|
44
|
+
* coffeelint (Check your coffeescript files using the [coffeelint gem.](https://github.com/clutchski/coffeelint))
|
39
45
|
|
40
46
|
To configure which checks you would like to run, simply set the `pre-commit.checks` git configuration setting.
|
41
47
|
|
@@ -53,10 +59,13 @@ Note: If no checks are configured, a default set of checks is run:
|
|
53
59
|
|
54
60
|
white_space, console_log, debugger, pry, tabs, jshint, migrations, merge_conflict, local
|
55
61
|
|
62
|
+
You may also enable checks that will produce warnings if detected but NOT stop the commit:
|
63
|
+
|
64
|
+
# From your git repo
|
65
|
+
$ git config "pre-commit.warnings" "jshint, ruby_symbol_hashrockets"
|
66
|
+
|
67
|
+
|
56
68
|
For the rubocop check, you can tell it what config file to use by setting a path relative to the repo:
|
57
69
|
|
58
70
|
# From your git repo
|
59
71
|
$ git config "pre-commit.rubocop.config" "config/rubocop.yml"
|
60
|
-
|
61
|
-
[1]: https://rubygems.org/gems/pre-commit
|
62
|
-
[2]: https://travis-ci.org/jish/pre-commit
|
data/bin/pre-commit
CHANGED
@@ -3,16 +3,18 @@
|
|
3
3
|
require 'pre-commit/cli'
|
4
4
|
|
5
5
|
if ARGV[0] != "install"
|
6
|
-
|
7
|
-
exit(1)
|
6
|
+
abort "Usage: pre-commit install"
|
8
7
|
end
|
9
8
|
|
10
9
|
if !File.exists?(".git")
|
11
|
-
|
12
|
-
exit(1)
|
10
|
+
abort "No .git directory found."
|
13
11
|
end
|
14
12
|
|
15
|
-
|
13
|
+
begin
|
14
|
+
PreCommit::Cli.new.install(ARGV[1])
|
15
|
+
rescue PreCommit::TemplateNotFound => e
|
16
|
+
abort e.message
|
17
|
+
end
|
16
18
|
|
17
19
|
puts "Installed hook: #{PreCommit::Cli::PRE_COMMIT_HOOK_PATH}"
|
18
20
|
puts
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module PreCommit
|
2
|
+
module Checks
|
3
|
+
class BeforeAll
|
4
|
+
def self.call(staged_files)
|
5
|
+
staged_files.reject! { |f| File.extname(f) != ".rb" }
|
6
|
+
return if staged_files.empty?
|
7
|
+
errors = `#{PreCommit::Utils.grep} -e "before.*:all" #{staged_files.join(" ")} | grep -v \/\/`.strip
|
8
|
+
return unless $?.success?
|
9
|
+
"before(:all) found:\n#{errors}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module PreCommit
|
2
|
+
module Checks
|
3
|
+
class Closure
|
4
|
+
#TODO: add pluginator assets support
|
5
|
+
CLOSURE_PATH = File.expand_path("../../../../pre-commit/support/closure/compiler.jar", __FILE__)
|
6
|
+
|
7
|
+
def self.supports(name)
|
8
|
+
name == :closure_syntax_check
|
9
|
+
end
|
10
|
+
def self.call(staged_files)
|
11
|
+
return if staged_files.empty?
|
12
|
+
js_args = staged_files.map {|arg| "--js #{arg}"}.join(' ')
|
13
|
+
errors = `java -jar #{CLOSURE_PATH} #{js_args} --js_output_file /tmp/jammit.js 2>&1`.strip
|
14
|
+
return if errors.empty?
|
15
|
+
errors
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'pre-commit/utils'
|
2
|
+
require 'stringio'
|
3
|
+
require 'open3'
|
4
|
+
|
5
|
+
module PreCommit
|
6
|
+
module Checks
|
7
|
+
class Coffeelint
|
8
|
+
def self.call(staged_files)
|
9
|
+
staged_files = staged_files.grep(/\.coffee$/)
|
10
|
+
return if staged_files.empty?
|
11
|
+
|
12
|
+
args = staged_files.join(' ')
|
13
|
+
|
14
|
+
stdout, stderr, result = Open3.capture3("coffeelint #{args}")
|
15
|
+
stdout + stderr unless result.success?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module PreCommit
|
2
|
+
module Checks
|
3
|
+
class ConsoleLog
|
4
|
+
def self.call(staged_files)
|
5
|
+
staged_files.reject! { |f| File.extname(f) != ".js" }
|
6
|
+
return if staged_files.empty?
|
7
|
+
errors = `#{PreCommit::Utils.grep} -e "console\\.log" #{staged_files.join(" ")} | grep -v \/\/`.strip
|
8
|
+
return unless $?.success?
|
9
|
+
"console.log found:\n#{errors}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'pre-commit/utils'
|
2
|
+
|
3
|
+
module PreCommit
|
4
|
+
module Checks
|
5
|
+
class Debugger
|
6
|
+
def self.call(staged_files)
|
7
|
+
files = files_to_check(staged_files)
|
8
|
+
return if files.empty?
|
9
|
+
|
10
|
+
errors = `#{PreCommit::Utils.grep} debugger #{files.join(" ")}`.strip
|
11
|
+
return unless $?.success?
|
12
|
+
|
13
|
+
"debugger statement(s) found:\n#{errors}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.files_to_check(files)
|
17
|
+
files.reject { |file| File.basename(file) =~ /^Gemfile/ }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'pre-commit/utils'
|
2
|
+
|
3
|
+
module PreCommit
|
4
|
+
module Checks
|
5
|
+
class GemfilePath
|
6
|
+
def self.call(staged_files)
|
7
|
+
return unless staged_files.include?("Gemfile")
|
8
|
+
errors = `#{PreCommit::Utils.grep} 'path:|:path\\s*=>' Gemfile`.strip
|
9
|
+
return unless $?.success?
|
10
|
+
"local path found in Gemfile:\n#{errors}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'pre-commit/utils'
|
2
|
+
|
3
|
+
module PreCommit
|
4
|
+
module Checks
|
5
|
+
class Js
|
6
|
+
def self.call(staged_files)
|
7
|
+
require 'execjs'
|
8
|
+
rescue RuntimeError, LoadError => e
|
9
|
+
$stderr.puts "Could not load execjs: #{e}"
|
10
|
+
else
|
11
|
+
staged_files = staged_files.select { |f| File.extname(f) == ".js" }
|
12
|
+
return if staged_files.empty?
|
13
|
+
|
14
|
+
errors = []
|
15
|
+
staged_files.each do |file|
|
16
|
+
error_list = Array(run_check(file))
|
17
|
+
error_list.each { |error_object| errors << display_error(error_object, file) }
|
18
|
+
end
|
19
|
+
|
20
|
+
return if errors.empty?
|
21
|
+
errors.join("\n")
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.linter_src
|
25
|
+
raise "Must be defined by subclass"
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.display_error(error_object, file)
|
29
|
+
return "" unless error_object
|
30
|
+
|
31
|
+
line = error_object['line'].to_i + 1
|
32
|
+
"#{error_object['reason']}\n#{file}:#{line} #{error_object['evidence']}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'plugins/pre_commit/checks/js'
|
2
|
+
|
3
|
+
module PreCommit
|
4
|
+
module Checks
|
5
|
+
class Jshint < Js
|
6
|
+
def self.config
|
7
|
+
if config_file = [ENV['JSHINT_CONFIG'], ".jshintrc"].compact.detect { |f| File.exist?(f) }
|
8
|
+
ExecJS.exec("return (#{File.read(config_file)});")
|
9
|
+
else
|
10
|
+
{}
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.run_check(file)
|
15
|
+
context = ExecJS.compile(File.read(linter_src))
|
16
|
+
context.call("JSHINT", File.read(file), config, config["globals"])
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.linter_src
|
20
|
+
File.expand_path("../../../../pre-commit/support/jshint/jshint.js", __FILE__)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'plugins/pre_commit/checks/js'
|
2
|
+
|
3
|
+
module PreCommit
|
4
|
+
module Checks
|
5
|
+
class Jslint < Js
|
6
|
+
def self.supports(name)
|
7
|
+
[ :js_lint, :js_lint_all, :js_lint_new ].include?(name)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.run_check(file)
|
11
|
+
context = ExecJS.compile(File.read(linter_src))
|
12
|
+
if !(context.call('JSLINT', File.read(file)))
|
13
|
+
context.exec('return JSLINT.errors;')
|
14
|
+
else
|
15
|
+
[]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.linter_src
|
20
|
+
File.expand_path("../../../../pre-commit/support/jslint/lint.js", __FILE__)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module PreCommit
|
2
|
+
module Checks
|
3
|
+
class Local
|
4
|
+
DEFAULT_LOCATION = "config/pre-commit.rb"
|
5
|
+
|
6
|
+
def self.call(staged_files, script=DEFAULT_LOCATION)
|
7
|
+
return unless File.exist?(script)
|
8
|
+
output = `ruby #{script} #{staged_files.join(" ")} 2>&1`
|
9
|
+
"#{script} failed:\n#{output}" unless $?.success?
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module PreCommit
|
2
|
+
module Checks
|
3
|
+
class MergeConflict
|
4
|
+
def self.call(staged_files)
|
5
|
+
return if staged_files.empty?
|
6
|
+
errors = `#{PreCommit::Utils.grep} '<<<<<<<' #{staged_files.join(" ")}`.strip
|
7
|
+
return unless $?.success?
|
8
|
+
"detected a merge conflict\n#{errors}"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module PreCommit
|
2
|
+
module Checks
|
3
|
+
class Migration
|
4
|
+
def self.supports(name)
|
5
|
+
name == :migrations
|
6
|
+
end
|
7
|
+
class << self
|
8
|
+
def call(staged_files)
|
9
|
+
migration_files = migration_files(staged_files)
|
10
|
+
schema_files = schema_files(staged_files)
|
11
|
+
|
12
|
+
if migration_files.any? && schema_files.none?
|
13
|
+
"It looks like you're adding a migration, but did not update the schema file"
|
14
|
+
elsif migration_files.none? && schema_files.any?
|
15
|
+
"You're trying to change the schema without adding a migration file"
|
16
|
+
elsif migration_files.any? && schema_files.any?
|
17
|
+
versions = migration_files.map { |f| f[/\d+/] }
|
18
|
+
schema = schema_files.map { |f| File.read(f) }.join
|
19
|
+
missing_versions = versions.select { |version| !schema.include?(version) }
|
20
|
+
if missing_versions.any?
|
21
|
+
"You did not add the schema versions for #{versions.join(', ')} to #{schema_files.join(' or ')}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def migration_files(staged_files)
|
29
|
+
staged_files.grep(/db\/migrate\/.*\.rb/)
|
30
|
+
end
|
31
|
+
|
32
|
+
def schema_files(staged_files)
|
33
|
+
staged_files.select { |f| File.basename(f) =~ /schema\.rb|structure.*\.sql/ }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module PreCommit
|
3
|
+
module Checks
|
4
|
+
class NbSpace
|
5
|
+
def self.call(staged_files)
|
6
|
+
nb_space = " "
|
7
|
+
raise "you messed that up" unless nb_space.bytes.to_a == [194, 160]
|
8
|
+
|
9
|
+
staged_files.reject! { |f| f =~ /^vendor\// || !File.read(f, encoding: 'utf-8').include?(nb_space) }
|
10
|
+
|
11
|
+
bad = staged_files.map do |file|
|
12
|
+
content = File.read(file).lines.to_a
|
13
|
+
line_no = content.index { |l| l.include?(nb_space) }
|
14
|
+
char_no = content[line_no].index(nb_space)
|
15
|
+
[file, line_no, char_no]
|
16
|
+
end
|
17
|
+
|
18
|
+
return if bad.empty?
|
19
|
+
"Detected non-breaking space in #{bad.map { |f,l,c| "#{f}:#{l+1} character:#{c+1}" }.join(" and")}, remove it!"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'pre-commit/utils'
|
2
|
+
|
3
|
+
module PreCommit
|
4
|
+
module Checks
|
5
|
+
class Php
|
6
|
+
def self.call(staged_files)
|
7
|
+
staged_files = staged_files.grep /\.(php|engine|theme|install|inc|module|test)$/
|
8
|
+
return if staged_files.empty?
|
9
|
+
|
10
|
+
errors = staged_files.map { |file| run_check(file) }.compact
|
11
|
+
return if errors.empty?
|
12
|
+
|
13
|
+
errors.join("\n")
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.run_check(file)
|
17
|
+
# We force PHP to display errors otherwise they will likely end up in the
|
18
|
+
# error_log and not in stdout.
|
19
|
+
result = `php -d display_errors=1 -l #{file} 2>&1`
|
20
|
+
# Filter out the obvious note from PHP.
|
21
|
+
result = result.split($/).find_all {|line| line !~ /Errors/}.join($/)
|
22
|
+
# If PHP exited non-zero then there was a parse error.
|
23
|
+
result.strip unless $? == 0
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module PreCommit
|
2
|
+
module Checks
|
3
|
+
class Pry
|
4
|
+
def self.call(staged_files)
|
5
|
+
return if staged_files.empty?
|
6
|
+
result = `#{PreCommit::Utils.grep} binding.pry #{staged_files.join(" ")}`.strip
|
7
|
+
return unless $?.success?
|
8
|
+
"binding.pry found:\n#{result}"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module PreCommit
|
2
|
+
module Checks
|
3
|
+
class RspecFocus
|
4
|
+
def self.call(staged_files)
|
5
|
+
staged_files = staged_files.grep(/_spec\.rb$/)
|
6
|
+
return if staged_files.empty?
|
7
|
+
result = `#{PreCommit::Utils.grep} ':focus' #{staged_files.join(" ")}`.strip
|
8
|
+
return unless $?.success?
|
9
|
+
":focus found in specs:\n#{result}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'pre-commit/utils'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
module PreCommit
|
5
|
+
module Checks
|
6
|
+
class Rubocop
|
7
|
+
def self.supports(name)
|
8
|
+
[ :rubocop_all, :rubocop_new ].include?(name)
|
9
|
+
end
|
10
|
+
def self.call(staged_files)
|
11
|
+
require 'rubocop'
|
12
|
+
rescue LoadError => e
|
13
|
+
$stderr.puts "Could not find rubocop: #{e}"
|
14
|
+
else
|
15
|
+
staged_files = staged_files.grep(/\.rb$/)
|
16
|
+
return if staged_files.empty?
|
17
|
+
config_file = `git config pre-commit.rubocop.config`.chomp
|
18
|
+
|
19
|
+
args = staged_files
|
20
|
+
if !config_file.empty?
|
21
|
+
if !File.exist? config_file
|
22
|
+
$stderr.puts "Warning: rubocop config file '" + config_file + "' does not exist"
|
23
|
+
$stderr.puts "Set the path to the config file using:"
|
24
|
+
$stderr.puts "\tgit config pre-commit.rubocop.config 'path/relative/to/git/dir/rubocop.yml'"
|
25
|
+
$stderr.puts "rubocop will use its default configuration or look for a .rubocop.yml file\n\n"
|
26
|
+
else
|
27
|
+
args = ['-c', config_file] + args
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
success, captured = capture { Rubocop::CLI.new.run(args) == 0 }
|
32
|
+
captured unless success
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.capture
|
36
|
+
$stdout, stdout = StringIO.new, $stdout
|
37
|
+
$stderr, stderr = StringIO.new, $stderr
|
38
|
+
result = yield
|
39
|
+
[result, $stdout.string + $stderr.string]
|
40
|
+
ensure
|
41
|
+
$stdout = stdout
|
42
|
+
$stderr = stderr
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|