kicker 3.0.0pre1 → 3.0.0pre2
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/README.rdoc +10 -4
- data/lib/kicker.rb +26 -20
- data/lib/kicker/callback_chain.rb +14 -14
- data/lib/kicker/core_ext.rb +1 -1
- data/lib/kicker/fsevents.rb +6 -4
- data/lib/kicker/job.rb +57 -0
- data/lib/kicker/notification.rb +13 -24
- data/lib/kicker/options.rb +26 -22
- data/lib/kicker/recipes.rb +19 -16
- data/lib/kicker/recipes/could_not_handle_file.rb +1 -1
- data/lib/kicker/recipes/dot_kick.rb +8 -8
- data/lib/kicker/recipes/execute_cli_command.rb +1 -1
- data/lib/kicker/recipes/ignore.rb +4 -4
- data/lib/kicker/recipes/jstest.rb +1 -1
- data/lib/kicker/recipes/rails.rb +11 -11
- data/lib/kicker/recipes/ruby.rb +23 -23
- data/lib/kicker/utils.rb +44 -50
- data/lib/kicker/version.rb +1 -1
- metadata +108 -136
- data/vendor/terminal-notifier_v1.0/README.markdown +0 -70
- data/vendor/terminal-notifier_v1.0/terminal-notifier.app/Contents/Info.plist +0 -52
- data/vendor/terminal-notifier_v1.0/terminal-notifier.app/Contents/MacOS/terminal-notifier +0 -0
- data/vendor/terminal-notifier_v1.0/terminal-notifier.app/Contents/PkgInfo +0 -1
- data/vendor/terminal-notifier_v1.0/terminal-notifier.app/Contents/Resources/Terminal.icns +0 -0
- data/vendor/terminal-notifier_v1.0/terminal-notifier.app/Contents/Resources/en.lproj/Credits.rtf +0 -29
- data/vendor/terminal-notifier_v1.0/terminal-notifier.app/Contents/Resources/en.lproj/InfoPlist.strings +0 -0
- data/vendor/terminal-notifier_v1.0/terminal-notifier.app/Contents/Resources/en.lproj/MainMenu.nib +0 -0
- data/vendor/terminal-notifier_v1.0/terminal-notifier.app/Contents/_CodeSignature/CodeResources +0 -61
data/lib/kicker/recipes.rb
CHANGED
@@ -1,6 +1,3 @@
|
|
1
|
-
RECIPES_DIR = File.expand_path('../recipes', __FILE__)
|
2
|
-
USER_RECIPES_DIR = File.expand_path('~/.kick')
|
3
|
-
|
4
1
|
module Kernel
|
5
2
|
# If only given a <tt>name</tt>, the specified recipe will be loaded. For
|
6
3
|
# instance, the following, in a <tt>.kick</tt> file, will load the Rails
|
@@ -25,6 +22,12 @@ end
|
|
25
22
|
|
26
23
|
class Kicker
|
27
24
|
module Recipes #:nodoc:
|
25
|
+
RECIPES_DIR = Pathname.new('../recipes').expand_path(__FILE__)
|
26
|
+
USER_RECIPES_DIR = Pathname.new('~/.kick').expand_path
|
27
|
+
CURRENT_RECIPES_DIR = Pathname.pwd.join('.kick').expand_path
|
28
|
+
|
29
|
+
RECIPES_DIRS = [RECIPES_DIR, USER_RECIPES_DIR, CURRENT_RECIPES_DIR]
|
30
|
+
|
28
31
|
class << self
|
29
32
|
def reset!
|
30
33
|
@recipes = nil
|
@@ -33,33 +36,33 @@ class Kicker
|
|
33
36
|
load_recipe :could_not_handle_file
|
34
37
|
load_recipe :dot_kick
|
35
38
|
end
|
36
|
-
|
39
|
+
|
37
40
|
def recipes
|
38
41
|
@recipes ||= {}
|
39
42
|
end
|
40
|
-
|
43
|
+
|
41
44
|
def recipe_filename(name)
|
42
45
|
[
|
43
46
|
USER_RECIPES_DIR,
|
44
47
|
RECIPES_DIR
|
45
48
|
].each do |directory|
|
46
|
-
filename =
|
47
|
-
return filename if
|
49
|
+
filename = directory.join("#{name}.rb")
|
50
|
+
return filename if filename.exist?
|
48
51
|
end
|
49
52
|
end
|
50
|
-
|
53
|
+
|
51
54
|
def recipe_names
|
52
|
-
recipe_files.map { |filename|
|
55
|
+
recipe_files.map { |filename| filename.basename('.rb').to_s.to_sym }
|
53
56
|
end
|
54
|
-
|
57
|
+
|
55
58
|
def recipe_files
|
56
|
-
|
59
|
+
RECIPES_DIRS.map{|dir| Pathname.glob(dir.join('*.rb')) }.flatten.uniq.map(&:expand_path)
|
57
60
|
end
|
58
|
-
|
61
|
+
|
59
62
|
def define_recipe(name, &block)
|
60
63
|
recipes[name] = block
|
61
64
|
end
|
62
|
-
|
65
|
+
|
63
66
|
def load_recipe(name)
|
64
67
|
if recipe_names.include?(name)
|
65
68
|
load recipe_filename(name)
|
@@ -67,7 +70,7 @@ class Kicker
|
|
67
70
|
raise LoadError, "Can't load recipe `#{name}', it doesn't exist on disk. Loadable recipes are: #{recipe_names[0..-2].join(', ')}, and #{recipe_names[-1]}"
|
68
71
|
end
|
69
72
|
end
|
70
|
-
|
73
|
+
|
71
74
|
def activate_recipe(name)
|
72
75
|
unless recipes.has_key?(name)
|
73
76
|
load_recipe(name)
|
@@ -78,7 +81,7 @@ class Kicker
|
|
78
81
|
raise ArgumentError, "Can't activate the recipe `#{name}' because it hasn't been defined yet."
|
79
82
|
end
|
80
83
|
end
|
81
|
-
|
84
|
+
|
82
85
|
# See Kernel#recipe for more information about the usage.
|
83
86
|
def recipe(name, &block)
|
84
87
|
name = name.to_sym
|
@@ -89,7 +92,7 @@ class Kicker
|
|
89
92
|
end
|
90
93
|
end
|
91
94
|
end
|
92
|
-
|
95
|
+
|
93
96
|
reset!
|
94
97
|
end
|
95
98
|
end
|
@@ -4,32 +4,32 @@ module ReloadDotKick #:nodoc
|
|
4
4
|
@features_before_dot_kick = $LOADED_FEATURES.dup
|
5
5
|
@chains_before_dot_kick = Kicker.full_chain.map { |c| c.dup }
|
6
6
|
end
|
7
|
-
|
7
|
+
|
8
8
|
def call(files)
|
9
9
|
reset! if files.delete('.kick')
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
def use?
|
13
13
|
File.exist?('.kick')
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
def load!
|
17
17
|
load '.kick'
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
def reset!
|
21
21
|
remove_loaded_features!
|
22
22
|
reset_chains!
|
23
23
|
load!
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
def reset_chains!
|
27
27
|
Kicker.full_chain = nil
|
28
|
-
|
28
|
+
|
29
29
|
chains = @chains_before_dot_kick.map { |c| c.dup }
|
30
30
|
Kicker.pre_process_chain, Kicker.process_chain, Kicker.post_process_chain = *chains
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
def remove_loaded_features!
|
34
34
|
($LOADED_FEATURES - @features_before_dot_kick).each do |feat|
|
35
35
|
$LOADED_FEATURES.delete(feat)
|
@@ -44,4 +44,4 @@ if ReloadDotKick.use?
|
|
44
44
|
ReloadDotKick.save_state
|
45
45
|
ReloadDotKick.load!
|
46
46
|
end
|
47
|
-
end
|
47
|
+
end
|
@@ -7,11 +7,11 @@ module Ignore
|
|
7
7
|
def self.call(files) #:nodoc:
|
8
8
|
files.reject! { |file| ignores.any? { |ignore| file =~ ignore } }
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
def self.ignores #:nodoc:
|
12
12
|
@ignores ||= []
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
def self.ignore(regexp_or_string) #:nodoc:
|
16
16
|
ignores << (regexp_or_string.is_a?(Regexp) ? regexp_or_string : /^#{regexp_or_string}$/)
|
17
17
|
end
|
@@ -33,9 +33,9 @@ end
|
|
33
33
|
|
34
34
|
recipe :ignore do
|
35
35
|
pre_process Ignore
|
36
|
-
|
36
|
+
|
37
37
|
ignore("tmp")
|
38
38
|
ignore(/\w+\.log/)
|
39
39
|
ignore(/\.(svn|git)\//)
|
40
40
|
ignore("svn-commit.tmp")
|
41
|
-
end
|
41
|
+
end
|
data/lib/kicker/recipes/rails.rb
CHANGED
@@ -6,7 +6,7 @@ class Kicker::Recipes::Rails < Kicker::Recipes::Ruby
|
|
6
6
|
%w{ test_type runner_bin test_cases_root test_options }.each do |delegate|
|
7
7
|
define_method(delegate) { Kicker::Recipes::Ruby.send(delegate) }
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
# Maps +type+, for instance `models', to a test directory.
|
11
11
|
def type_to_test_dir(type)
|
12
12
|
if test_type == 'test'
|
@@ -43,7 +43,7 @@ class Kicker::Recipes::Rails < Kicker::Recipes::Ruby
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
end
|
46
|
-
|
46
|
+
|
47
47
|
# Returns an array of all tests related to the given model.
|
48
48
|
def tests_for_model(model)
|
49
49
|
if test_type == 'test'
|
@@ -60,37 +60,37 @@ class Kicker::Recipes::Rails < Kicker::Recipes::Ruby
|
|
60
60
|
}
|
61
61
|
end.map { |f| test_file f }
|
62
62
|
end
|
63
|
-
|
63
|
+
|
64
64
|
def handle!
|
65
65
|
@tests.concat(@files.take_and_map do |file|
|
66
66
|
case file
|
67
67
|
# Run all functional tests when routes.rb is saved
|
68
68
|
when 'config/routes.rb'
|
69
69
|
Kicker::Recipes::Rails.all_controller_tests
|
70
|
-
|
70
|
+
|
71
71
|
# Match lib/*
|
72
72
|
when /^(lib\/.+)\.rb$/
|
73
73
|
test_file($1)
|
74
|
-
|
74
|
+
|
75
75
|
# Map fixtures to their related tests
|
76
76
|
when %r{^#{test_cases_root}/fixtures/(\w+)\.yml$}
|
77
77
|
tests_for_model($1)
|
78
|
-
|
78
|
+
|
79
79
|
# Match any file in app/ and map it to a test file
|
80
80
|
when %r{^app/(\w+)([\w/]*)/([\w\.]+)\.\w+$}
|
81
81
|
type, namespace, file = $1, $2, $3
|
82
|
-
|
82
|
+
|
83
83
|
if dir = Kicker::Recipes::Rails.type_to_test_dir(type)
|
84
84
|
if type == "views"
|
85
85
|
namespace = namespace.split('/')[1..-1]
|
86
86
|
file = "#{namespace.pop}_controller"
|
87
87
|
end
|
88
|
-
|
88
|
+
|
89
89
|
test_file File.join(dir, namespace, file)
|
90
90
|
end
|
91
91
|
end
|
92
92
|
end)
|
93
|
-
|
93
|
+
|
94
94
|
# And let the Ruby handler match other stuff.
|
95
95
|
super
|
96
96
|
end
|
@@ -99,9 +99,9 @@ end
|
|
99
99
|
recipe :rails do
|
100
100
|
require 'rubygems' rescue LoadError
|
101
101
|
require 'active_support/inflector'
|
102
|
-
|
102
|
+
|
103
103
|
process Kicker::Recipes::Rails
|
104
|
-
|
104
|
+
|
105
105
|
# When changing the schema, prepare the test database.
|
106
106
|
process do |files|
|
107
107
|
execute 'rake db:test:prepare' if files.delete('db/schema.rb')
|
data/lib/kicker/recipes/ruby.rb
CHANGED
@@ -2,19 +2,19 @@ class Kicker::Recipes::Ruby
|
|
2
2
|
class << self
|
3
3
|
# Assigns the type of tests to run. Eg: `test' or `spec'.
|
4
4
|
attr_writer :test_type
|
5
|
-
|
5
|
+
|
6
6
|
# Returns the type of tests to run. Eg: `test' or `spec'.
|
7
7
|
#
|
8
8
|
# Defaults to `test' if no `spec' directory exists.
|
9
9
|
def test_type
|
10
10
|
@test_type ||= File.exist?('spec') ? 'spec' : 'test'
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
# Assigns the ruby command to run the tests with. Eg: `ruby19' or `specrb'.
|
14
14
|
#
|
15
15
|
# This can be set from the command line with the `-b' or `--ruby' options.
|
16
16
|
attr_writer :runner_bin
|
17
|
-
|
17
|
+
|
18
18
|
# Returns the ruby command to run the tests with. Eg: `ruby' or `spec'.
|
19
19
|
#
|
20
20
|
# Defaults to `ruby' if test_type is `test' and `spec' if test_type is
|
@@ -22,33 +22,33 @@ class Kicker::Recipes::Ruby
|
|
22
22
|
def runner_bin
|
23
23
|
@runner_bin ||= test_type == 'test' ? 'ruby' : 'rspec'
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
# Assigns the root directory of where test cases will be looked up.
|
27
27
|
attr_writer :test_cases_root
|
28
|
-
|
28
|
+
|
29
29
|
# Returns the root directory of where test cases will be looked up.
|
30
30
|
#
|
31
31
|
# Defaults to the value of test_type. Eg: `test' or `spec'.
|
32
32
|
def test_cases_root
|
33
33
|
@test_cases_root ||= test_type
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
attr_writer :test_options #:nodoc:
|
37
|
-
|
37
|
+
|
38
38
|
# Assigns extra options that are to be passed on to the runner_bin.
|
39
39
|
#
|
40
40
|
# Ruby.test_options << '-I ./lib/foo'
|
41
41
|
def test_options
|
42
42
|
@test_options ||= []
|
43
43
|
end
|
44
|
-
|
44
|
+
|
45
45
|
def reset!
|
46
46
|
@test_type = nil
|
47
47
|
@runner_bin = nil
|
48
48
|
@test_cases_root = nil
|
49
49
|
@test_options = nil
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
52
|
def runner_command(*parts)
|
53
53
|
parts.map do |part|
|
54
54
|
case part
|
@@ -59,63 +59,63 @@ class Kicker::Recipes::Ruby
|
|
59
59
|
end
|
60
60
|
end.compact.join(' ')
|
61
61
|
end
|
62
|
-
|
62
|
+
|
63
63
|
# Runs the given tests, if there are any, with the method defined by
|
64
64
|
# test_type. If test_type is `test' the run_with_test_runner method is
|
65
65
|
# used. The same applies when test_type is `spec'.
|
66
66
|
def run_tests(tests)
|
67
67
|
send("run_with_#{test_type}_runner", tests) unless tests.empty?
|
68
68
|
end
|
69
|
-
|
69
|
+
|
70
70
|
def test_runner_command(tests)
|
71
71
|
tests_without_ext = tests.map { |f| f[0,f.size-3] }
|
72
72
|
runner_command(runner_bin, %w{ -I. } + test_options, '-r', tests_without_ext.join(' -r '), "-e ''")
|
73
73
|
end
|
74
|
-
|
74
|
+
|
75
75
|
# Runs the given tests with `ruby' as unit-test tests.
|
76
76
|
def run_with_test_runner(tests)
|
77
77
|
execute(test_runner_command(tests))
|
78
78
|
end
|
79
|
-
|
79
|
+
|
80
80
|
def spec_runner_command(tests)
|
81
81
|
runner_command(runner_bin, test_options, tests)
|
82
82
|
end
|
83
|
-
|
83
|
+
|
84
84
|
# Runs the given tests with `spec' as RSpec tests.
|
85
85
|
def run_with_spec_runner(tests)
|
86
86
|
execute(spec_runner_command(tests))
|
87
87
|
end
|
88
88
|
end
|
89
|
-
|
89
|
+
|
90
90
|
def self.call(files) #:nodoc:
|
91
91
|
handler = new(files)
|
92
92
|
handler.handle!
|
93
93
|
run_tests(handler.tests)
|
94
94
|
end
|
95
|
-
|
95
|
+
|
96
96
|
# The list of collected tests.
|
97
97
|
attr_reader :tests
|
98
|
-
|
98
|
+
|
99
99
|
def initialize(files) #:nodoc:
|
100
100
|
@files = files
|
101
101
|
@tests = []
|
102
102
|
end
|
103
|
-
|
103
|
+
|
104
104
|
# A shortcut to Ruby.test_type.
|
105
105
|
def test_type
|
106
106
|
self.class.test_type
|
107
107
|
end
|
108
|
-
|
108
|
+
|
109
109
|
# A shortcut to Ruby.runner_bin.
|
110
110
|
def runner_bin
|
111
111
|
self.class.runner_bin
|
112
112
|
end
|
113
|
-
|
113
|
+
|
114
114
|
# A shortcut to Ruby.test_cases_root.
|
115
115
|
def test_cases_root
|
116
116
|
self.class.test_cases_root
|
117
117
|
end
|
118
|
-
|
118
|
+
|
119
119
|
# Returns the file for +name+ if it exists.
|
120
120
|
#
|
121
121
|
# test_file('foo') # => "test/foo_test.rb"
|
@@ -125,7 +125,7 @@ class Kicker::Recipes::Ruby
|
|
125
125
|
file = File.join(test_cases_root, "#{name}_#{test_type}.rb")
|
126
126
|
file if File.exist?(file)
|
127
127
|
end
|
128
|
-
|
128
|
+
|
129
129
|
# This method is called to collect tests. Override this if you're subclassing
|
130
130
|
# and make sure to call +super+.
|
131
131
|
def handle!
|
@@ -134,7 +134,7 @@ class Kicker::Recipes::Ruby
|
|
134
134
|
# Match any ruby test file
|
135
135
|
when /^#{test_cases_root}\/.+_#{test_type}\.rb$/
|
136
136
|
file
|
137
|
-
|
137
|
+
|
138
138
|
# A file such as ./lib/namespace/foo.rb is mapped to:
|
139
139
|
# * ./test/namespace/foo_test.rb
|
140
140
|
# * ./test/foo_test.rb
|
data/lib/kicker/utils.rb
CHANGED
@@ -1,36 +1,32 @@
|
|
1
1
|
require 'shellwords' if RUBY_VERSION >= "1.9"
|
2
2
|
|
3
3
|
class Kicker
|
4
|
-
Status = Struct.new(:command, :exit_code, :output) do
|
5
|
-
def success?
|
6
|
-
exit_code == 0
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
4
|
module Utils #:nodoc:
|
11
5
|
extend self
|
12
6
|
|
13
7
|
attr_accessor :should_clear_screen
|
14
8
|
alias_method :should_clear_screen?, :should_clear_screen
|
15
9
|
|
16
|
-
def perform_work(
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
end
|
24
|
-
|
25
|
-
def execute(command)
|
26
|
-
perform_work(command) do |status|
|
27
|
-
_execute(status)
|
28
|
-
yield status if block_given?
|
10
|
+
def perform_work(command_or_options)
|
11
|
+
if command_or_options.is_a?(Hash)
|
12
|
+
options = command_or_options
|
13
|
+
elsif command_or_options.is_a?(String)
|
14
|
+
options = { :command => command_or_options }
|
15
|
+
else
|
16
|
+
raise ArgumentError, "Should be a string or a hash."
|
29
17
|
end
|
18
|
+
job = Job.new(options)
|
19
|
+
will_execute_command(job)
|
20
|
+
yield job
|
21
|
+
did_execute_command(job)
|
22
|
+
job
|
30
23
|
end
|
31
24
|
|
32
|
-
def
|
33
|
-
|
25
|
+
def execute(command_or_options)
|
26
|
+
perform_work(command_or_options) do |job|
|
27
|
+
_execute(job)
|
28
|
+
yield job if block_given?
|
29
|
+
end
|
34
30
|
end
|
35
31
|
|
36
32
|
def log(message)
|
@@ -41,14 +37,6 @@ class Kicker
|
|
41
37
|
puts "#{now.strftime('%H:%M:%S')}.#{now.usec.to_s[0,2]} | #{message}"
|
42
38
|
end
|
43
39
|
end
|
44
|
-
|
45
|
-
def last_command_succeeded?
|
46
|
-
$?.success?
|
47
|
-
end
|
48
|
-
|
49
|
-
def last_command_status
|
50
|
-
$?.exitstatus
|
51
|
-
end
|
52
40
|
|
53
41
|
def clear_console!
|
54
42
|
puts(CLEAR) if Kicker.clear_console?
|
@@ -58,22 +46,22 @@ class Kicker
|
|
58
46
|
|
59
47
|
CLEAR = "\e[H\e[2J"
|
60
48
|
|
61
|
-
def _execute(
|
49
|
+
def _execute(job)
|
62
50
|
silent = Kicker.silent?
|
63
51
|
unless silent
|
64
52
|
puts
|
65
53
|
sync_before, $stdout.sync = $stdout.sync, true
|
66
54
|
end
|
67
55
|
output = ""
|
68
|
-
popen(
|
56
|
+
popen(job.command) do |io|
|
69
57
|
while str = io.read(1)
|
70
58
|
output << str
|
71
59
|
$stdout.print str unless silent
|
72
60
|
end
|
73
61
|
end
|
74
|
-
|
75
|
-
|
76
|
-
|
62
|
+
job.output = output.strip
|
63
|
+
job.exit_code = $?.exitstatus
|
64
|
+
job
|
77
65
|
ensure
|
78
66
|
unless silent
|
79
67
|
$stdout.sync = sync_before
|
@@ -90,19 +78,30 @@ class Kicker
|
|
90
78
|
IO.popen("#{command} 2>&1", &block)
|
91
79
|
end
|
92
80
|
end
|
93
|
-
|
94
|
-
def will_execute_command(
|
81
|
+
|
82
|
+
def will_execute_command(job)
|
95
83
|
puts(CLEAR) if Kicker.clear_console? && should_clear_screen?
|
96
84
|
@should_clear_screen = false
|
97
85
|
|
98
|
-
|
99
|
-
|
86
|
+
if message = job.print_before
|
87
|
+
log(message)
|
88
|
+
end
|
89
|
+
|
90
|
+
if notification = job.notify_before
|
91
|
+
Notification.notify(notification)
|
92
|
+
end
|
100
93
|
end
|
101
|
-
|
102
|
-
def did_execute_command(
|
103
|
-
|
104
|
-
|
105
|
-
|
94
|
+
|
95
|
+
def did_execute_command(job)
|
96
|
+
if message = job.print_after
|
97
|
+
puts(message)
|
98
|
+
end
|
99
|
+
|
100
|
+
log(job.success? ? "Success" : "Failed (#{job.exit_code})")
|
101
|
+
|
102
|
+
if notification = job.notify_after
|
103
|
+
Notification.notify(notification)
|
104
|
+
end
|
106
105
|
end
|
107
106
|
end
|
108
107
|
end
|
@@ -112,22 +111,17 @@ module Kernel
|
|
112
111
|
def log(message)
|
113
112
|
Kicker::Utils.log(message)
|
114
113
|
end
|
115
|
-
|
114
|
+
|
116
115
|
# When you perform some work (like shelling out a command to run without
|
117
116
|
# using +execute+) you need to call this method, with a block in which you
|
118
117
|
# perform your work, which will take care of logging the work appropriately.
|
119
118
|
def perform_work(command, &block)
|
120
119
|
Kicker::Utils.perform_work(command, &block)
|
121
120
|
end
|
122
|
-
|
121
|
+
|
123
122
|
# Executes the +command+, logs the output, and optionally sends user
|
124
123
|
# notifications on Mac OS X (10.8 or higher).
|
125
124
|
def execute(command, &block)
|
126
125
|
Kicker::Utils.execute(command, &block)
|
127
126
|
end
|
128
|
-
|
129
|
-
# Returns the last executed command.
|
130
|
-
def last_command
|
131
|
-
Kicker::Utils.last_command
|
132
|
-
end
|
133
127
|
end
|