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.
@@ -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