rabbitt-githooks 1.3.0 → 1.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +5 -13
  2. data/Gemfile.lock +29 -19
  3. data/Rakefile +20 -0
  4. data/bin/githooks +2 -1
  5. data/bin/githooks-runner +10 -2
  6. data/lib/githooks.rb +11 -7
  7. data/lib/githooks/action.rb +67 -53
  8. data/lib/githooks/cli.rb +9 -6
  9. data/lib/githooks/commands/config.rb +26 -19
  10. data/lib/githooks/core_ext.rb +2 -1
  11. data/lib/githooks/core_ext/array/extract_options.rb +5 -1
  12. data/lib/githooks/core_ext/array/min_max.rb +4 -4
  13. data/lib/githooks/core_ext/array/select_with_index.rb +1 -1
  14. data/lib/githooks/core_ext/colorize.rb +30 -0
  15. data/lib/githooks/core_ext/object.rb +8 -0
  16. data/lib/githooks/core_ext/pathname.rb +7 -6
  17. data/lib/githooks/core_ext/string.rb +1 -1
  18. data/lib/githooks/core_ext/string/sanitize.rb +51 -0
  19. data/lib/githooks/error.rb +1 -0
  20. data/lib/githooks/hook.rb +21 -26
  21. data/lib/githooks/repository.rb +31 -35
  22. data/lib/githooks/repository/config.rb +16 -24
  23. data/lib/githooks/repository/diff_index_entry.rb +3 -2
  24. data/lib/githooks/repository/file.rb +14 -7
  25. data/lib/githooks/runner.rb +38 -36
  26. data/lib/githooks/section.rb +13 -19
  27. data/lib/githooks/system_utils.rb +132 -47
  28. data/lib/githooks/version.rb +1 -1
  29. data/lib/tasks/dev.task +14 -0
  30. data/rabbitt-githooks.gemspec +18 -15
  31. metadata +58 -33
  32. data/.gitignore +0 -34
  33. data/.hooks/commit-messages.rb +0 -29
  34. data/.hooks/formatting.rb +0 -44
  35. data/.rubocop.yml +0 -87
  36. data/lib/githooks/core_ext/process.rb +0 -9
  37. data/lib/githooks/core_ext/string/strip_empty_lines.rb +0 -9
  38. data/lib/githooks/terminal_colors.rb +0 -62
  39. data/thoughts.txt +0 -56
@@ -1,6 +1,5 @@
1
1
  require 'ostruct'
2
2
  require 'pathname'
3
- require 'githooks/repository/file'
4
3
 
5
4
  module GitHooks
6
5
  class Repository
@@ -31,7 +30,8 @@ module GitHooks
31
30
  super parse_data(entry)
32
31
  end
33
32
 
34
- def parse_data(entry) # rubocop:disable MethodLength
33
+ # rubocop:disable MultilineOperationIndentation
34
+ def parse_data(entry) # rubocop:disable MethodLength, AbcSize
35
35
  data = Hash[
36
36
  DIFF_STRUCTURE_REGEXP.names.collect(&:to_sym).zip(
37
37
  entry.match(DIFF_STRUCTURE_REGEXP).captures
@@ -53,6 +53,7 @@ module GitHooks
53
53
  score: data[:score].to_i
54
54
  }
55
55
  end
56
+ # rubocop:enable MultilineOperationIndentation
56
57
 
57
58
  def to_repo_file
58
59
  Repository::File.new(self)
@@ -17,16 +17,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
17
17
  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
18
  =end
19
19
 
20
- require 'ostruct'
21
20
  require 'delegate'
22
21
 
23
- # allow for reloading of class
24
- unless defined? DiffIndexEntryDelegateClass
25
- DiffIndexEntryDelegateClass = DelegateClass(GitHooks::Repository::DiffIndexEntry)
26
- end
27
-
28
22
  module GitHooks
29
23
  class Repository
24
+ # allow for reloading of class
25
+ unless defined? DiffIndexEntryDelegateClass
26
+ DiffIndexEntryDelegateClass = DelegateClass(DiffIndexEntry)
27
+ end
28
+
30
29
  class File < DiffIndexEntryDelegateClass
31
30
  def initialize(entry)
32
31
  unless entry.is_a? Repository::DiffIndexEntry
@@ -68,7 +67,7 @@ module GitHooks
68
67
  end
69
68
  end
70
69
 
71
- def match(type, selector)
70
+ def match(type, selector) # rubocop:disable AbcSize
72
71
  value = attribute_value(type)
73
72
  return selector.call(value) if selector.respond_to? :call
74
73
 
@@ -122,6 +121,14 @@ module GitHooks
122
121
  return [] unless fd
123
122
  strip_newlines ? fd.readlines.collect(&:chomp!) : fd.readlines
124
123
  end
124
+
125
+ def <=>(other)
126
+ path.to_s <=> other.path.to_s
127
+ end
128
+
129
+ def ==(other)
130
+ path.to_s == other.path.to_s
131
+ end
125
132
  end
126
133
  end
127
134
  end
@@ -17,23 +17,23 @@ with this program; if not, write to the Free Software Foundation, Inc.,
17
17
  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
18
  =end
19
19
 
20
+ require 'thor'
20
21
  require 'fileutils'
21
- require 'githooks/terminal_colors'
22
22
  require 'shellwords'
23
- require 'thor'
23
+
24
+ require_relative 'error'
25
+ require_relative 'hook'
26
+ require_relative 'repository'
27
+ require_relative 'system_utils'
24
28
 
25
29
  module GitHooks
26
30
  module Runner
27
- extend TerminalColors
28
-
29
- MARK_SUCCESS = '✓'
30
- MARK_FAILURE = 'X'
31
- MARK_UNKNOWN = '?'
32
-
33
- def run(options = {}) # rubocop:disable CyclomaticComplexity, MethodLength
34
- # unfreeze options
31
+ # rubocop:disable CyclomaticComplexity, MethodLength, AbcSize, PerceivedComplexity
32
+ def run(options = {})
35
33
  options = Thor::CoreExt::HashWithIndifferentAccess.new(options)
36
34
 
35
+ options['staged'] = options['staged'].nil? ? true : options['staged']
36
+
37
37
  repo = options['repo'] ||= Repository.root_path
38
38
  script = options['script'] ||= Repository.instance(repo).config.script
39
39
  libpath = options['path'] ||= Repository.instance(repo).config.path
@@ -56,7 +56,7 @@ module GitHooks
56
56
  load_tests(libpath, options['skip-bundler'])
57
57
  start(options)
58
58
  else
59
- puts 'I can\'t figure out what to run - specify either path or script to give me a hint...'
59
+ puts %q"I can't figure out what to run - specify either path or script to give me a hint..."
60
60
  end
61
61
 
62
62
  if options['skip-post']
@@ -64,17 +64,17 @@ module GitHooks
64
64
  else
65
65
  run_externals('post-run-execute', repo, args)
66
66
  end
67
- rescue GitHooks::Error::NotAGitRepo
67
+ rescue GitHooks::Error::NotAGitRepo => e
68
68
  puts "Unable to find a valid git repo in #{repo}."
69
69
  puts 'Please specify path to repo via --repo <path>' if GitHooks::SCRIPT_NAME == 'githooks'
70
+ raise e
70
71
  end
71
72
  module_function :run
72
73
 
73
- def attach(options = {}) # rubocop:disable CyclomaticComplexity, MethodLength
74
+ def attach(options = {})
74
75
  repo_path = options[:repo] || Repository.root_path
75
76
  repo_path = Pathname.new(repo_path) unless repo_path.nil?
76
- repo_hooks = repo_path + '.git' + 'hooks'
77
-
77
+ repo_hooks = repo_path.join('.git', 'hooks')
78
78
  entry_path = options[:script] || options[:path]
79
79
 
80
80
  hook_phases = options[:hooks]
@@ -114,7 +114,7 @@ module GitHooks
114
114
 
115
115
  def detach(repo_path, hook_phases)
116
116
  repo_path ||= Repository.root_path
117
- repo_hooks = Pathname.new(repo_path) + '.git' + 'hooks'
117
+ repo_hooks = Pathname.new(repo_path).join('.git', 'hooks')
118
118
  hook_phases ||= Hook::VALID_PHASES
119
119
 
120
120
  repo = Repository.instance(repo_path)
@@ -136,7 +136,7 @@ module GitHooks
136
136
  end
137
137
  module_function :detach
138
138
 
139
- def list(repo_path) # rubocop:disable Style/CyclomaticComplexity, Style/MethodLength
139
+ def list(repo_path)
140
140
  repo_path ||= Pathname.new(Repository.root_path)
141
141
 
142
142
  repo = Repository.instance(repo_path)
@@ -149,9 +149,7 @@ module GitHooks
149
149
 
150
150
  if (executables = repo.config.pre_run_execute).size > 0
151
151
  puts 'PreRun Executables (in execution order):'
152
- executables.each do |exe|
153
- puts " #{exe}"
154
- end
152
+ puts executables.collect { |exe| " #{exe}" }.join("\n")
155
153
  puts
156
154
  end
157
155
 
@@ -196,9 +194,10 @@ module GitHooks
196
194
  end
197
195
  puts
198
196
  end
199
- rescue GitHooks::Error::NotAGitRepo
197
+ rescue Error::NotAGitRepo
200
198
  puts "Unable to find a valid git repo in #{repo}."
201
199
  puts 'Please specify path to repo via --repo <path>' if GitHooks::SCRIPT_NAME == 'githooks'
200
+ raise
202
201
  end
203
202
  module_function :list
204
203
 
@@ -206,7 +205,7 @@ module GitHooks
206
205
 
207
206
  def run_externals(which, repo_path, args)
208
207
  Repository.instance(repo_path).config[which].all? do |executable|
209
- command = SystemUtils::Command.new(File.basename(executable), path: executable)
208
+ command = SystemUtils::Command.new(File.basename(executable), bin_path: executable)
210
209
 
211
210
  puts "#{which.camelize}: #{command.build_command(args)}" if GitHooks.verbose
212
211
  unless (r = command.execute(*args)).status.success?
@@ -222,7 +221,7 @@ module GitHooks
222
221
  end
223
222
  module_function :run_externals
224
223
 
225
- def start(options = {}) # rubocop:disable Style/CyclomaticComplexity, Style/MethodLength
224
+ def start(options = {}) # rubocop:disable CyclomaticComplexity, MethodLength
226
225
  phase = options[:hook] || GitHooks.hook_name || 'pre-commit'
227
226
  puts "PHASE: #{phase}" if GitHooks.debug
228
227
 
@@ -237,21 +236,22 @@ module GitHooks
237
236
  end
238
237
 
239
238
  success = active_hook.run
240
- section_length = active_hook.sections.max { |s| s.title.length }
239
+ section_length = active_hook.sections.maximum { |s| s.title.length }
241
240
  sections = active_hook.sections.select { |section| !section.actions.empty? }
242
241
 
242
+ # TODO: refactor to show this in realtime instead of after the hooks have run
243
243
  sections.each do |section|
244
244
  hash_tail_length = (section_length - section.title.length)
245
245
  printf "===== %s %s===== (%ds)\n", section.colored_name(phase), ('=' * hash_tail_length), section.benchmark
246
-
247
246
  section.actions.each_with_index do |action, index|
248
- printf " %d. [ %s ] %s (%ds)\n", (index + 1), action.state_symbol, action.colored_title, action.benchmark
247
+ printf " %d. [ %s ] %s (%ds)\n", (index + 1), action.status_symbol, action.colored_title, action.benchmark
249
248
 
250
249
  action.errors.each do |error|
251
- printf " %s %s\n", color_bright_red(MARK_FAILURE), error
250
+ printf " %s %s\n", GitHooks::FAILURE_SYMBOL, error
252
251
  end
253
252
 
254
- state_string = ( action.success? ? color_bright_green(MARK_SUCCESS) : color_bright_yellow(MARK_UNKNOWN))
253
+ state_string = action.success? ? GitHooks::SUCCESS_SYMBOL : GitHooks::UNKNOWN_SYMBOL
254
+
255
255
  action.warnings.each do |warning|
256
256
  printf " %s %s\n", state_string, warning
257
257
  end
@@ -270,11 +270,11 @@ module GitHooks
270
270
  end
271
271
  module_function :start
272
272
 
273
- def load_tests(path, skip_bundler = false) # rubocop:disable MethodLength,Style/CyclomaticComplexity
273
+ def load_tests(path, skip_bundler = false)
274
274
  hooks_root = Pathname.new(path).realpath
275
- hooks_path = (p = (hooks_root + 'hooks')).exist? ? p : (hooks_root + '.hooks')
276
- hooks_libs = hooks_root + 'libs'
277
- gemfile = hooks_root + 'Gemfile'
275
+ hooks_path = (p = (hooks_root.join('hooks'))).exist? ? p : (hooks_root.join('.hooks'))
276
+ hooks_libs = hooks_root.join('libs')
277
+ gemfile = hooks_root.join('Gemfile')
278
278
 
279
279
  if gemfile.exist? && !skip_bundler
280
280
  puts "loading Gemfile from: #{gemfile}" if GitHooks.verbose
@@ -286,16 +286,16 @@ module GitHooks
286
286
  # executable-hooks gem preloading bundler. hence the following ...
287
287
  if defined? Bundler
288
288
  [:@settings, :@bundle_path, :@configured, :@definition, :@load].each do |var|
289
- Bundler.instance_variable_set(var, nil)
289
+ ::Bundler.instance_variable_set(var, nil)
290
290
  end
291
291
  else
292
292
  require 'bundler'
293
293
  end
294
- Bundler.require(:default)
294
+ ::Bundler.require(:default)
295
295
  rescue LoadError
296
- puts %Q|Unable to load bundler - please make sure it's installed.|
296
+ puts %q"Unable to load bundler - please make sure it's installed."
297
297
  raise # rubocop:disable SignalException
298
- rescue Bundler::GemNotFound => e
298
+ rescue ::Bundler::GemNotFound => e
299
299
  puts "Error: #{e.message}"
300
300
  puts 'Did you bundle install your Gemfile?'
301
301
  raise # rubocop:disable SignalException
@@ -311,5 +311,7 @@ module GitHooks
311
311
  end
312
312
  end
313
313
  module_function :load_tests
314
+
315
+ # rubocop:enable CyclomaticComplexity, MethodLength, AbcSize, PerceivedComplexity
314
316
  end
315
317
  end
@@ -21,8 +21,6 @@ require 'delegate'
21
21
 
22
22
  module GitHooks
23
23
  class Section < DelegateClass(Array)
24
- include TerminalColors
25
-
26
24
  attr_reader :name, :hook, :success, :actions, :benchmark
27
25
  alias_method :title, :name
28
26
  alias_method :success?, :success
@@ -49,7 +47,7 @@ module GitHooks
49
47
  # overrides previous action method to only return
50
48
  # actions that have a non-empty manifest
51
49
  def actions
52
- @actions.select { |action| !action.manifest.empty? }
50
+ @actions.reject { |action| action.manifest.empty? }
53
51
  end
54
52
  alias_method :__getobj__, :actions
55
53
 
@@ -63,40 +61,36 @@ module GitHooks
63
61
  end
64
62
 
65
63
  def completed?
66
- @actions.all? { |action| action.finished? }
64
+ @actions.all?(&:finished?)
67
65
  end
68
66
 
69
67
  def wait_count
70
- @actions.select { |action| action.waiting? }.size
68
+ @actions.select(&:waiting?).size
71
69
  end
72
70
 
73
71
  def name(phase = GitHooks::HOOK_NAME)
74
- phase = (phase || GitHooks::HOOK_NAME).to_s.gsub('-', '_').camelize
75
- "#{phase} :: #{@name}"
72
+ "#{(phase || GitHooks::HOOK_NAME).to_s.camelize} :: #{@name}"
73
+ end
74
+
75
+ def colored_name(phase = GitHooks::HOOK_NAME)
76
+ status_colorize name(phase)
76
77
  end
77
78
 
78
79
  def key_name
79
80
  self.class.key_from_name(@name)
80
81
  end
81
82
 
82
- def colored_name(phase = GitHooks::HOOK_NAME)
83
- status_colorize name(phase)
83
+ def status_colorize(text)
84
+ return text.unknown! unless finished? && completed?
85
+ success? ? text.success! : text.failure!
84
86
  end
85
87
 
86
- def action(title, options = {}, &block)
87
- fail ArgumentError, 'Missing required block to #perform' unless block_given?
88
+ def action(title, &block)
89
+ fail ArgumentError, 'expected block, received none' unless block_given?
88
90
  @actions << Action.new(title, self, &block)
89
91
  self
90
92
  end
91
93
 
92
- def status_colorize(text)
93
- if finished? && completed?
94
- success? ? color_bright_green(text) : color_bright_red(text)
95
- else
96
- color_dark_cyan(text)
97
- end
98
- end
99
-
100
94
  def run
101
95
  running!
102
96
  begin
@@ -1,7 +1,8 @@
1
- # encoding: utf-8
1
+ # sublime: x_syntax Packages/Ruby/Ruby.tmLanguage
2
+ # sublime: translate_tabs_to_spaces true; tab_size 2
3
+
2
4
  require 'pathname'
3
- require 'open3'
4
- require 'ostruct'
5
+ require 'tempfile'
5
6
  require 'shellwords'
6
7
 
7
8
  module GitHooks
@@ -22,7 +23,7 @@ module GitHooks
22
23
  end
23
24
  module_function :find_bin
24
25
 
25
- def with_path(path, &block)
26
+ def with_path(path, &_block)
26
27
  fail ArgumentError, 'Missing required block' unless block_given?
27
28
  begin
28
29
  cwd = Dir.getwd
@@ -34,7 +35,7 @@ module GitHooks
34
35
  end
35
36
  module_function :with_path
36
37
 
37
- def quiet(&block)
38
+ def quiet(&_block)
38
39
  od, ov = GitHooks.debug, GitHooks.verbose
39
40
  GitHooks.debug, GitHooks.verbose = false, false
40
41
  yield
@@ -43,67 +44,151 @@ module GitHooks
43
44
  end
44
45
  module_function :quiet
45
46
 
47
+ def command(name)
48
+ (@commands ||= {})[name] ||= begin
49
+ Command.new(name).tap { |cmd|
50
+ define_method("command_#{cmd.name}") { |*args| cmd.execute(*args) }
51
+ alias_method cmd.method, "command_#{cmd.name}"
52
+ }
53
+ end
54
+ end
55
+ module_function :command
56
+
57
+ def commands(*names)
58
+ names.each { |name| command(name) }
59
+ end
60
+ module_function :commands
61
+
46
62
  class Command
47
63
  include Shellwords
48
64
 
49
- attr_reader :aliases, :path, :name
50
- def initialize(name, options = {})
51
- @name = name
52
- @path = options.delete(:path) || SystemUtils.which(name)
65
+ ENV_WHITELIST = %w(
66
+ PATH HOME LDFLAGS CPPFLAGS DISPLAY EDITOR
67
+ LANG LC_ALL SHELL SHLVL TERM TMPDIR USER
68
+ SSH_USER SSH_AUTH_SOCK
69
+ GEM_HOME GEM_PATH MY_RUBY_HOME
70
+ )
71
+
72
+ class Result
73
+ attr_reader :output, :error, :status
74
+ def initialize(output, error, status)
75
+ @output = output.strip
76
+ @error = error.strip
77
+ @status = status
78
+ end
53
79
 
54
- @aliases = options.delete(:aliases) || []
55
- @aliases << name
56
- @aliases.collect! { |alias_name| normalize(alias_name) }
57
- @aliases.uniq!
80
+ def output_lines(prefix = nil)
81
+ @output.split(/\n/).collect { |line|
82
+ prefix ? "#{prefix}: #{line}" : line
83
+ }
84
+ end
85
+
86
+ def error_lines(prefix = nil)
87
+ @error.split(/\n/).collect { |line|
88
+ prefix ? "#{prefix}: #{line}" : line
89
+ }
90
+ end
91
+
92
+ def sanitize!(*args)
93
+ @output.sanitize!(*args)
94
+ @error.sanitize!(*args)
95
+ end
96
+
97
+ def success?
98
+ status? ? @status.success? : false
99
+ end
100
+
101
+ def failure?
102
+ !success?
103
+ end
104
+
105
+ def status?
106
+ !!@status
107
+ end
108
+
109
+ def exitstatus
110
+ status? ? @status.exitstatus : -1
111
+ end
112
+ alias_method :code, :exitstatus
113
+ end
114
+
115
+ attr_reader :run_path, :bin_path, :name
116
+
117
+ def initialize(name, options = {})
118
+ @bin_path = options.delete(:bin_path) || SystemUtils.which(name) || name
119
+ @run_path = options.delete(:chdir)
120
+ @name = name.to_s.gsub(/([\W-]+)/, '_')
58
121
  end
59
122
 
60
- def command_path
61
- path || name.to_s
123
+ def method
124
+ @name.to_sym
62
125
  end
63
126
 
64
- def build_command(args, options = {})
65
- change_to_path = options['path'] || options[:path]
127
+ def build_command(args, options)
128
+ Array(args).unshift(command_path(options))
129
+ end
66
130
 
67
- args = [args].flatten
68
- args.collect! { |arg| arg.is_a?(Repository::File) ? arg.path.to_s : arg }
69
- args.collect!(&:to_s)
131
+ def command_path(options = {})
132
+ options.delete(:use_name) ? name : bin_path.to_s
133
+ end
70
134
 
71
- command = shelljoin([command_path] | args)
72
- command = ("cd #{shellescape(change_to_path.to_s)} ; " + command) unless change_to_path.nil?
73
- command
135
+ def prep_env(env = {})
136
+ Hash[env].each_with_object([]) do |(k, v), array|
137
+ array << %Q|#{k}="#{v}"| if ENV_WHITELIST.include? k
138
+ end.join(' ')
74
139
  end
75
140
 
76
- def execute(*args, &block) # rubocop:disable MethodLength, CyclomaticComplexity
77
- options = args.extract_options
78
- strip_empty_lines = !!options.delete(:strip_empty_lines)
141
+ def execute(*args, &_block) # rubocop:disable MethodLength, CyclomaticComplexity, AbcSize, PerceivedComplexity
142
+ options = args.extract_options!
79
143
 
80
144
  command = build_command(args, options)
81
- result = OpenStruct.new(output: nil, error: nil, status: nil).tap do |r|
82
- puts "#{Dir.getwd} $ #{command}" if GitHooks.debug
83
-
84
- r.output, r.error, r.status = if RUBY_ENGINE == 'jruby'
85
- _, o, e, t = Open3.popen3('/usr/bin/env', 'sh', '-c', command)
86
- [o.read, e.read, t.value]
87
- else
88
- Open3.capture3(command)
89
- end
90
-
91
- if strip_empty_lines
92
- r.output = r.output.strip_empty_lines!
93
- r.error = r.error.strip_empty_lines!
145
+ command.unshift("cd #{run_path} ;") if run_path
146
+ command.unshift('sudo') if options.delete(:use_sudo)
147
+ command = Array(command.flatten.join(' '))
148
+
149
+ command.unshift options.delete(:pre_pipe) if options[:pre_pipe]
150
+ command.push options.delete(:post_pipe) if options[:post_pipe]
151
+ command = Array(command.flatten.join('|'))
152
+
153
+ command.unshift options.delete(:pre_run) if options[:pre_run]
154
+ command.push options.delete(:post_run) if options[:post_run]
155
+ command = shellwords(command.flatten.join(';'))
156
+
157
+ environment = prep_env(options.delete(:env) || ENV)
158
+
159
+ error_file = Tempfile.new('ghstderr')
160
+ begin
161
+ real_command = %Q{
162
+ /usr/bin/env -i #{environment} bash -c '
163
+ ( #{command.join(' ').gsub("'", %q|'"'"'|)}) 2>#{error_file.path}
164
+ '
165
+ }
166
+
167
+ $stderr.puts real_command if GitHooks.debug?
168
+
169
+ output = %x{ #{real_command} }
170
+ result = Result.new(output, error_file.read, $?)
171
+
172
+ if GitHooks.verbose? && result.failure?
173
+ STDERR.puts "Command failed with exit code [#{result.status.exitstatus}]",
174
+ "ENVIRONMENT:\n\t#{environment}\n\n",
175
+ "COMMAND:\n\t#{command.join(' ')}\n\n",
176
+ "OUTPUT:\n-----\n#{result.output}\n-----\n\n",
177
+ "ERROR:\n-----\n#{result.error}\n-----\n\n"
94
178
  end
95
- end
96
179
 
97
- puts result.inspect if GitHooks.debug
180
+ sanitize = [ :strip, :non_printable ]
181
+ sanitize << :colors unless options.delete(:color)
182
+ sanitize << :empty_lines if options.delete(:strip_empty_lines)
183
+ result.sanitize!(*sanitize)
98
184
 
99
- block_given? ? yield(result) : result
185
+ block_given? ? yield(result) : result
186
+ ensure
187
+ error_file.close
188
+ error_file.unlink
189
+ end
100
190
  end
101
191
  alias_method :call, :execute
102
-
103
- def normalize(name)
104
- name.to_s.gsub(/[^a-z_]+/, '_')
105
- end
106
- private :normalize
107
192
  end
108
193
  end
109
194
  end