kicker 3.0.0 → 4.0.0.p1

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.
@@ -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.
@@ -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
@@ -2,9 +2,7 @@
2
2
 
3
3
  if $0 == __FILE__
4
4
  $:.unshift File.expand_path('../../lib', __FILE__)
5
- $:.unshift File.expand_path('../../vendor', __FILE__)
6
- require 'rubygems'
7
5
  end
8
6
 
9
7
  require 'kicker'
10
- Kicker.run
8
+ Kicker::CLI.run(ARGV)
@@ -1,123 +1,22 @@
1
- require 'kicker/version'
2
- require 'kicker/fsevents'
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 #:nodoc:
12
- def self.run(argv = ARGV)
13
- Kicker::Options.parse(argv)
14
- new.start.loop!
15
- end
16
-
17
- attr_reader :last_event_processed_at
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
- def validate_paths_and_command!
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 file_changed_since_last_event?(file)
107
- File.mtime(file) > @last_event_processed_at
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 make_paths_relative(files)
113
- return files if files.empty?
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
@@ -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