kicker 3.0.0 → 4.0.0.p1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/COPYING +20 -0
- data/README.rdoc +0 -150
- data/bin/kicker +1 -3
- data/lib/kicker.rb +16 -117
- data/lib/kicker/cli.rb +130 -0
- data/lib/kicker/core_ext/array.rb +40 -0
- data/lib/kicker/debug.rb +5 -0
- data/lib/kicker/deprecated.rb +14 -0
- data/lib/kicker/formatter.rb +15 -0
- data/lib/kicker/option_parser.rb +37 -0
- data/lib/kicker/recipe/ignore.rb +2 -0
- data/lib/kicker/recipe/peck.rb +54 -0
- data/lib/kicker/recipe/reload.rb +0 -0
- data/lib/kicker/{recipes → recipe}/ruby.rb +10 -10
- data/lib/kicker/script.rb +77 -0
- data/lib/kicker/version.rb +2 -2
- data/lib/kicker/watcher.rb +113 -0
- metadata +32 -120
- data/LICENSE +0 -54
- data/lib/kicker/callback_chain.rb +0 -95
- data/lib/kicker/core_ext.rb +0 -38
- data/lib/kicker/fsevents.rb +0 -36
- data/lib/kicker/job.rb +0 -57
- data/lib/kicker/notification.rb +0 -31
- data/lib/kicker/options.rb +0 -96
- data/lib/kicker/recipes.rb +0 -98
- data/lib/kicker/recipes/could_not_handle_file.rb +0 -7
- data/lib/kicker/recipes/dot_kick.rb +0 -47
- data/lib/kicker/recipes/execute_cli_command.rb +0 -9
- data/lib/kicker/recipes/ignore.rb +0 -41
- data/lib/kicker/recipes/jstest.rb +0 -10
- data/lib/kicker/recipes/rails.rb +0 -109
- data/lib/kicker/utils.rb +0 -127
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b3f1b8e56a45f76314daaf713f764f37228d42b4
|
4
|
+
data.tar.gz: 4867e2dbf6f21d39ecd0f7367c6d87bb0b94a9d7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1ed6e797ccff80a9a4cbd9d056ac6ae205907fef59c94b72c61c811576caa678df63b82eb043b657fe08e48438b24e5ea8e49eee62839cbc66ca5a97b2eecf2f
|
7
|
+
data.tar.gz: f23d431352c3eea377adee626ae6a76001151585e3a2a50d5f6203a2426a68fd79ebe65eff3781969bafa5ba4c6b351b9ecf033ef7ee9990c2f0a9f9f1b942df
|
data/COPYING
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c)
|
2
|
+
Eloy Duran <eloy.de.enige@gmail.com>
|
3
|
+
Manfred Stienstra, Fingertips <manfred@fngtps.com>
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
CHANGED
@@ -1,150 +0,0 @@
|
|
1
|
-
= Kicker
|
2
|
-
|
3
|
-
{<img src="https://secure.travis-ci.org/alloy/kicker.png" />}[http://travis-ci.org/alloy/kicker]
|
4
|
-
|
5
|
-
A lean, agnostic, flexible file-change watcher.
|
6
|
-
|
7
|
-
== Installation
|
8
|
-
|
9
|
-
$ gem install kicker -s http://gemcutter.org
|
10
|
-
|
11
|
-
== The short version
|
12
|
-
|
13
|
-
Usage: ./bin/kicker [options] [paths to watch]
|
14
|
-
|
15
|
-
Available recipes: ignore, jstest, rails, ruby.
|
16
|
-
|
17
|
-
-s, --silent Keep output to a minimum.
|
18
|
-
-q, --quiet Quiet output. Don't print timestamps when logging.
|
19
|
-
-c, --clear Clear console before each run.
|
20
|
-
-l, --latency [FLOAT] The time to collect file change events before acting on them. Defaults to 1 second.
|
21
|
-
-r, --recipe [NAME] A named recipe to load.
|
22
|
-
-e, --execute [COMMAND] The command to execute.
|
23
|
-
-b, --ruby [PATH] Use an alternate Ruby binary for spawned test runners. (Default is `ruby')
|
24
|
-
|
25
|
-
|
26
|
-
== The long version
|
27
|
-
|
28
|
-
=== Execute a shell command
|
29
|
-
|
30
|
-
Show all files, whenever a change occurs in the current work directory:
|
31
|
-
|
32
|
-
$ kicker -e "ls -l" .
|
33
|
-
|
34
|
-
Show all files, whenever a change occurs to a specific file:
|
35
|
-
|
36
|
-
$ kicker -e "ls -l" foo.txt
|
37
|
-
|
38
|
-
Or use it as a ghetto-autotest, running tests whenever files change:
|
39
|
-
|
40
|
-
$ kicker -e "ruby test/test_case.rb" test/test_case.rb lib/file.rb
|
41
|
-
|
42
|
-
Et cetera.
|
43
|
-
|
44
|
-
=== Using recipes
|
45
|
-
|
46
|
-
A recipe is a predefined handler. You can use as many as you like, by
|
47
|
-
specifying them with the <tt>--recipe</tt> (<tt>-r</tt>) option.
|
48
|
-
|
49
|
-
For instance, when in the root of a typical Ruby on Rails application, using
|
50
|
-
the <tt>rails</tt> recipe will map models, concerns, controllers, helpers, and
|
51
|
-
views to their respective test files. These will then all be ran with Ruby.
|
52
|
-
|
53
|
-
A few recipes come shipped with Kicker:
|
54
|
-
* Typical Ruby library.
|
55
|
-
* Ruby on Rails, as aforementioned.
|
56
|
-
* JavaScript tests, to run it needs
|
57
|
-
HeadlessSquirrel[http://github.com/Fingertips/Headless-squirrel].
|
58
|
-
* Ignore, ignores logs, tmp, and svn and git files.
|
59
|
-
|
60
|
-
Add your own shared recipes to <tt>~/.kick</tt> folder or
|
61
|
-
current working directory <tt>.kick</tt>.
|
62
|
-
|
63
|
-
=== Project specific handlers
|
64
|
-
|
65
|
-
Most of the time, you’ll want to create handlers specific to the project at
|
66
|
-
hand. This can be done by adding your handlers to a <tt>.kick</tt> file and
|
67
|
-
running Kicker from the directory containing it.
|
68
|
-
|
69
|
-
This file is reloaded once saved. No need to stop Kicker.
|
70
|
-
|
71
|
-
== Writing handlers
|
72
|
-
|
73
|
-
Whenever file-change events occur, Kicker will go through a chain of handlers
|
74
|
-
until that the files list is empty, or the end of the chain is reached.
|
75
|
-
|
76
|
-
Handlers are objects that respond to <tt>#call</tt>. These are typically Proc
|
77
|
-
objects. (If you know Rack, you’re familiar with this concept.) Every handler
|
78
|
-
gets passed a list of changed files and can decide whether or not to act on
|
79
|
-
them. Normally when handling a file, you should remove it from the files list,
|
80
|
-
unless you want to let the file fall through to another handler. In the same
|
81
|
-
way, one can add files to handler to the files list.
|
82
|
-
|
83
|
-
==== Time for a simple example
|
84
|
-
|
85
|
-
process do |files|
|
86
|
-
execute("rake docs:generate && open -a Safari html/index.html") if files.delete("README.rdoc")
|
87
|
-
end
|
88
|
-
|
89
|
-
A handler is defined by passing a block to <tt>process</tt>. Which is one of
|
90
|
-
three possible callback chains to add your handlers to, the others being:
|
91
|
-
<tt>pre_process</tt> and <tt>post_process</tt>. See Kernel for more info.
|
92
|
-
|
93
|
-
Then <tt>README.rdoc</tt> is deleted from the files array. If it did exist in
|
94
|
-
the array and was deleted, a shell command is executed which runs a rake task
|
95
|
-
to generate rdoc and open the docs with Safari.
|
96
|
-
|
97
|
-
==== Something more elaborate.
|
98
|
-
|
99
|
-
Consider a Rails application with a mailer. Since the naming convention of
|
100
|
-
mailer views tend to be fairly application specific, a specific handler has to
|
101
|
-
be added:
|
102
|
-
|
103
|
-
process do |files|
|
104
|
-
test_files = files.take_and_map do |file|
|
105
|
-
if path =~ %r{^app/views/mailer/\w+\.erb$}
|
106
|
-
'test/unit/mailer_test.rb'
|
107
|
-
|
108
|
-
# elsif ... handle more app specific stuff
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
Ruby.run_tests test_files
|
113
|
-
end
|
114
|
-
|
115
|
-
The files list is iterated over with the Array#take_and_map method, which both
|
116
|
-
removes and maps the results. This is an easy way to do a common thing in
|
117
|
-
recipes. See Kicker::ArrayExt for details.
|
118
|
-
|
119
|
-
The handler then checks if the file is a mailer view and if so runs the
|
120
|
-
mailers test case. Ruby.run_tests runs them with something like the following
|
121
|
-
command:
|
122
|
-
|
123
|
-
execute "ruby -r #{test_files.join(' -r ')} -e ''" unless test_files.empty?
|
124
|
-
|
125
|
-
See Kernel for more info on the utility methods.
|
126
|
-
|
127
|
-
To load recipes from your <tt>~/.kick</tt> file:
|
128
|
-
|
129
|
-
recipe :ignore
|
130
|
-
ignore(/^data\//)
|
131
|
-
|
132
|
-
That’s basically it, just remember that the order of specifying handlers _can_
|
133
|
-
be important in your decision on where to specify handlers.
|
134
|
-
|
135
|
-
== Notifiers
|
136
|
-
|
137
|
-
For platform specific notifications we use the notify gem. For supported
|
138
|
-
backends see: https://github.com/jugyo/notify#feature.
|
139
|
-
|
140
|
-
You select the notify backend by setting the NOTIFY environment variable.
|
141
|
-
|
142
|
-
gem install terminal-notifier
|
143
|
-
env NOTIFY=terminal-notifier kicker
|
144
|
-
|
145
|
-
== Contributors
|
146
|
-
|
147
|
-
* Manfred Stienstra (@manfred)
|
148
|
-
* Cristi Balan (@evilchelu)
|
149
|
-
* Damir Zekic (@sidonath)
|
150
|
-
* Adam Keys (@therealadam)
|
data/bin/kicker
CHANGED
data/lib/kicker.rb
CHANGED
@@ -1,123 +1,22 @@
|
|
1
|
-
require '
|
2
|
-
require 'kicker/
|
3
|
-
require 'kicker/callback_chain'
|
4
|
-
require 'kicker/core_ext'
|
5
|
-
require 'kicker/job'
|
6
|
-
require 'kicker/notification'
|
7
|
-
require 'kicker/options'
|
8
|
-
require 'kicker/utils'
|
9
|
-
require 'kicker/recipes'
|
1
|
+
require 'tidings'
|
2
|
+
require 'kicker/core_ext/array'
|
10
3
|
|
11
|
-
class Kicker
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
def initialize
|
20
|
-
finished_processing!
|
21
|
-
end
|
22
|
-
|
23
|
-
def paths
|
24
|
-
@paths ||= Kicker.paths.map { |path| File.expand_path(path) }
|
25
|
-
end
|
26
|
-
|
27
|
-
def start
|
28
|
-
validate_options!
|
29
|
-
|
30
|
-
log "Watching for changes on: #{paths.join(', ')}"
|
31
|
-
log ''
|
32
|
-
|
33
|
-
run_startup_chain
|
34
|
-
run_watch_dog!
|
35
|
-
|
36
|
-
self
|
37
|
-
end
|
38
|
-
|
39
|
-
def loop!
|
40
|
-
(Thread.list - [Thread.current, Thread.main]).each(&:join)
|
41
|
-
end
|
42
|
-
|
43
|
-
private
|
44
|
-
|
45
|
-
def validate_options!
|
46
|
-
validate_paths_and_command!
|
47
|
-
validate_paths_exist!
|
48
|
-
end
|
4
|
+
class Kicker
|
5
|
+
autoload :CLI, 'kicker/cli'
|
6
|
+
autoload :Deprecated, 'kicker/deprecated'
|
7
|
+
autoload :Formatter, 'kicker/formatter'
|
8
|
+
autoload :OptionParser, 'kicker/option_parser'
|
9
|
+
autoload :Script, 'kicker/script'
|
10
|
+
autoload :VERSION, 'kicker/version'
|
11
|
+
autoload :Watcher, 'kicker/watcher'
|
49
12
|
|
50
|
-
|
51
|
-
if startup_chain.empty? && process_chain.empty? && pre_process_chain.empty?
|
52
|
-
puts Kicker::Options.parser.help
|
53
|
-
exit
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def validate_paths_exist!
|
58
|
-
paths.each do |path|
|
59
|
-
unless File.exist?(path)
|
60
|
-
puts "The given path `#{path}' does not exist"
|
61
|
-
exit 1
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def run_watch_dog!
|
67
|
-
dirs = @paths.map { |path| File.directory?(path) ? path : File.dirname(path) }
|
68
|
-
watch_dog = Kicker::FSEvents.start_watching(dirs, :latency => self.class.latency) do |events|
|
69
|
-
process events
|
70
|
-
end
|
71
|
-
trap('INT') do
|
72
|
-
log "Exiting ..."
|
73
|
-
watch_dog.stop
|
74
|
-
exit
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def run_startup_chain
|
79
|
-
startup_chain.call([], false)
|
80
|
-
end
|
81
|
-
|
82
|
-
def finished_processing!
|
83
|
-
@last_event_processed_at = Time.now
|
84
|
-
end
|
85
|
-
|
86
|
-
def process(events)
|
87
|
-
unless (files = changed_files(events)).empty?
|
88
|
-
Utils.should_clear_screen = true
|
89
|
-
full_chain.call(files)
|
90
|
-
finished_processing!
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def changed_files(events)
|
95
|
-
make_paths_relative(events.map do |event|
|
96
|
-
files_in_directory(event.path).select { |file| file_changed_since_last_event? file }
|
97
|
-
end.flatten.uniq.sort)
|
98
|
-
end
|
99
|
-
|
100
|
-
def files_in_directory(dir)
|
101
|
-
Dir.entries(dir).sort[2..-1].map { |f| File.join(dir, f) }
|
102
|
-
rescue Errno::ENOENT
|
103
|
-
[]
|
104
|
-
end
|
13
|
+
extend Kicker::Formatter
|
105
14
|
|
106
|
-
def
|
107
|
-
|
108
|
-
rescue Errno::ENOENT
|
109
|
-
false
|
15
|
+
def self.debug(*)
|
16
|
+
# By default we ignore all debugging information
|
110
17
|
end
|
111
18
|
|
112
|
-
def
|
113
|
-
|
114
|
-
wd = Dir.pwd
|
115
|
-
files.map do |file|
|
116
|
-
if file[0..wd.length-1] == wd
|
117
|
-
file[wd.length+1..-1]
|
118
|
-
else
|
119
|
-
file
|
120
|
-
end
|
121
|
-
end
|
19
|
+
def self.version
|
20
|
+
Kicker::VERSION
|
122
21
|
end
|
123
|
-
end
|
22
|
+
end
|
data/lib/kicker/cli.rb
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
class Kicker
|
2
|
+
class CLI
|
3
|
+
def initialize(argv)
|
4
|
+
@options, @argv = Kicker::OptionParser.parse(argv)
|
5
|
+
end
|
6
|
+
|
7
|
+
def switches
|
8
|
+
@options.map(&:first)
|
9
|
+
end
|
10
|
+
|
11
|
+
def any_switch?(*test)
|
12
|
+
!(switches & test).empty?
|
13
|
+
end
|
14
|
+
|
15
|
+
def option_value(*switches)
|
16
|
+
@options.each do |key, value|
|
17
|
+
if switches.include?(key)
|
18
|
+
return value
|
19
|
+
end
|
20
|
+
end; nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def osx?
|
24
|
+
RUBY_PLATFORM.downcase.include?("darwin")
|
25
|
+
end
|
26
|
+
|
27
|
+
def debug?
|
28
|
+
$DEBUG || any_switch?('d', 'debug')
|
29
|
+
end
|
30
|
+
|
31
|
+
def show_version?
|
32
|
+
any_switch?('v', 'version')
|
33
|
+
end
|
34
|
+
|
35
|
+
def show_usage?
|
36
|
+
any_switch?('h', 'help')
|
37
|
+
end
|
38
|
+
|
39
|
+
def show_version
|
40
|
+
puts Kicker::VERSION
|
41
|
+
end
|
42
|
+
|
43
|
+
def available_recipes_as_sentence
|
44
|
+
available = Kicker::Script.available_recipes
|
45
|
+
available[0..-2].join(', ') + ', and ' + available[-1]
|
46
|
+
end
|
47
|
+
|
48
|
+
def dump_options?
|
49
|
+
any_switch?('dump-options')
|
50
|
+
end
|
51
|
+
|
52
|
+
def dump_options
|
53
|
+
puts options.inspect
|
54
|
+
end
|
55
|
+
|
56
|
+
def show_usage
|
57
|
+
puts "Usage: #{$0} [options] [paths to watch]"
|
58
|
+
puts ""
|
59
|
+
puts " Available recipes: #{available_recipes_as_sentence}."
|
60
|
+
puts ""
|
61
|
+
puts "OPTIONS"
|
62
|
+
puts ""
|
63
|
+
if osx?
|
64
|
+
puts " -a, --activate: The application to activate when a notification is clicked. Defaults to `com.apple.Terminal'."
|
65
|
+
end
|
66
|
+
puts " -c, --clear: Clear console before each run."
|
67
|
+
puts " -d, --debug: Print debug messages for Kicker internals."
|
68
|
+
puts " -k, --kickfile: Use a specific Kickfile."
|
69
|
+
puts " -l, --latency: The time to collect events before acting on them. (float)."
|
70
|
+
puts " -n, --no-notify: Don't send notifications."
|
71
|
+
puts " -q, --quiet: Quiet output. Don't print timestamps when logging."
|
72
|
+
puts " -r, --recipe: Load named recipe."
|
73
|
+
puts " -s, --silent: Don't output anything."
|
74
|
+
puts " -v, --version: Print the Kicker version."
|
75
|
+
end
|
76
|
+
|
77
|
+
def verbosity
|
78
|
+
if any_switch?('s', 'silent')
|
79
|
+
:silent
|
80
|
+
elsif any_switch?('q', 'quiet')
|
81
|
+
:quiet
|
82
|
+
else
|
83
|
+
:regular
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def kickfile
|
88
|
+
if path = option_value('k', 'kickfile')
|
89
|
+
File.absolute_path(path)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def recipes
|
94
|
+
@options.map do |switch, name|
|
95
|
+
if %w(r recipe).include?(switch)
|
96
|
+
name
|
97
|
+
end
|
98
|
+
end.compact
|
99
|
+
end
|
100
|
+
|
101
|
+
def options
|
102
|
+
{
|
103
|
+
activate: option_value('a', 'activate'),
|
104
|
+
clear_before_execute: any_switch?('c', 'clear'),
|
105
|
+
kickfile: kickfile,
|
106
|
+
notifications: !any_switch?('n', 'no-notify'),
|
107
|
+
recipes: recipes,
|
108
|
+
verbosity: verbosity
|
109
|
+
}
|
110
|
+
end
|
111
|
+
|
112
|
+
def run
|
113
|
+
if show_version?
|
114
|
+
show_version
|
115
|
+
elsif show_usage?
|
116
|
+
show_usage
|
117
|
+
elsif dump_options?
|
118
|
+
dump_options
|
119
|
+
else
|
120
|
+
require 'kicker/debug' if debug?
|
121
|
+
watcher = ::Kicker::Watcher.new(options)
|
122
|
+
watcher.run
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def self.run(argv)
|
127
|
+
new(argv).run
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|