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.
data/LICENSE DELETED
@@ -1,54 +0,0 @@
1
- Kicker:
2
-
3
- Copyright (c) 2009 Eloy Duran <eloy.de.enige@gmail.com>
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining
6
- a copy of this software and associated documentation files (the
7
- "Software"), to deal in the Software without restriction, including
8
- without limitation the rights to use, copy, modify, merge, publish,
9
- distribute, sublicense, and/or sell copies of the Software, and to
10
- permit persons to whom the Software is furnished to do so, subject to
11
- the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be
14
- included in all copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
-
24
- ======================================================================
25
-
26
- Rucola: http://github.com/alloy/rucola/tree/master
27
-
28
- Copyright (c) 2008 Eloy Duran <eloy.de.enige@gmail.com>
29
-
30
- Permission is hereby granted, free of charge, to any person obtaining
31
- a copy of this software and associated documentation files (the
32
- "Software"), to deal in the Software without restriction, including
33
- without limitation the rights to use, copy, modify, merge, publish,
34
- distribute, sublicense, and/or sell copies of the Software, and to
35
- permit persons to whom the Software is furnished to do so, subject to
36
- the following conditions:
37
-
38
- The above copyright notice and this permission notice shall be
39
- included in all copies or substantial portions of the Software.
40
-
41
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
42
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
43
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
44
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
45
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
46
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
47
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
48
-
49
- ======================================================================
50
-
51
- growlnotifier: http://github.com/psychs/growlnotifier/tree/master
52
-
53
- Copyright (c) 2007-2008 Satoshi Nakagawa <psychs@limechat.net>, Eloy Duran <e.duran@superalloy.nl>
54
- You can redistribute it and/or modify it under the same terms as Ruby.
@@ -1,95 +0,0 @@
1
- class Kicker
2
- class CallbackChain < Array #:nodoc:
3
- alias_method :append_callback, :push
4
- alias_method :prepend_callback, :unshift
5
-
6
- def call(files, stop_when_empty = true)
7
- each do |callback|
8
- break if stop_when_empty and files.empty?
9
- callback.call(files)
10
- end
11
- end
12
- end
13
-
14
- class << self
15
- attr_writer :startup_chain
16
- def startup_chain
17
- @startup_chain ||= CallbackChain.new
18
- end
19
-
20
- attr_writer :pre_process_chain
21
- def pre_process_chain
22
- @pre_process_chain ||= CallbackChain.new
23
- end
24
-
25
- attr_writer :process_chain
26
- def process_chain
27
- @process_chain ||= CallbackChain.new
28
- end
29
-
30
- attr_writer :post_process_chain
31
- def post_process_chain
32
- @post_process_chain ||= CallbackChain.new
33
- end
34
-
35
- attr_writer :full_chain
36
- def full_chain
37
- @full_chain ||= CallbackChain.new([pre_process_chain, process_chain, post_process_chain])
38
- end
39
- end
40
-
41
- def startup_chain
42
- self.class.startup_chain
43
- end
44
-
45
- def pre_process_chain
46
- self.class.pre_process_chain
47
- end
48
-
49
- def process_chain
50
- self.class.process_chain
51
- end
52
-
53
- def post_process_chain
54
- self.class.post_process_chain
55
- end
56
-
57
- def full_chain
58
- self.class.full_chain
59
- end
60
- end
61
-
62
- module Kernel
63
- # Adds a handler to the startup chain. This chain is ran once Kicker is done
64
- # loading _before_ starting the normal operations. Note that an empty files
65
- # array is given to the callback.
66
- #
67
- # Takes a +callback+ object that responds to <tt>#call</tt>, or a block.
68
- def startup(callback = nil, &block)
69
- Kicker.startup_chain.append_callback(block ? block : callback)
70
- end
71
-
72
- # Adds a handler to the pre_process chain. This chain is ran before the
73
- # process chain and is processed from first to last.
74
- #
75
- # Takes a +callback+ object that responds to <tt>#call</tt>, or a block.
76
- def pre_process(callback = nil, &block)
77
- Kicker.pre_process_chain.append_callback(block ? block : callback)
78
- end
79
-
80
- # Adds a handler to the process chain. This chain is ran in between the
81
- # pre_process and post_process chains. It is processed from first to last.
82
- #
83
- # Takes a +callback+ object that responds to <tt>#call</tt>, or a block.
84
- def process(callback = nil, &block)
85
- Kicker.process_chain.append_callback(block ? block : callback)
86
- end
87
-
88
- # Adds a handler to the post_process chain. This chain is ran after the
89
- # process chain and is processed from last to first.
90
- #
91
- # Takes a +callback+ object that responds to <tt>#call</tt>, or a block.
92
- def post_process(callback = nil, &block)
93
- Kicker.post_process_chain.prepend_callback(block ? block : callback)
94
- end
95
- end
@@ -1,38 +0,0 @@
1
- class Kicker
2
- module ArrayExt
3
- # Deletes elements from self for which the block evaluates to +true+. A new
4
- # array is returned with those values the block returned. So basically, a
5
- # combination of reject! and map.
6
- #
7
- # a = [1,2,3]
8
- # b = a.take_and_map { |x| x * 2 if x == 2 }
9
- # b # => [4]
10
- # a # => [1, 3]
11
- #
12
- # If +pattern+ is specified then files matching the pattern will be taken.
13
- #
14
- # a = [ 'bar', 'foo/bar' ]
15
- # b = a.take_and_map('*/bar') { |x| x }
16
- # b # => ['foo/bar']
17
- # a # => ['bar']
18
- #
19
- # If +flatten_and_compact+ is +true+, the result array will be flattened
20
- # and compacted. The default is +true+.
21
- def take_and_map(pattern = nil, flatten_and_compact = true)
22
- took = []
23
- reject! do |x|
24
- next if pattern and !File.fnmatch?(pattern, x)
25
- if result = yield(x)
26
- took << result
27
- end
28
- end
29
- if flatten_and_compact
30
- took.flatten!
31
- took.compact!
32
- end
33
- took
34
- end
35
- end
36
- end
37
-
38
- Array.send(:include, Kicker::ArrayExt)
@@ -1,36 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'listen'
4
-
5
- class Kicker
6
- class FSEvents
7
- class FSEvent
8
- attr_reader :path
9
-
10
- def initialize(path)
11
- @path = path
12
- end
13
-
14
- def files
15
- Dir.glob("#{File.expand_path(path)}/*").map do |filename|
16
- begin
17
- [File.mtime(filename), filename]
18
- rescue Errno::ENOENT
19
- nil
20
- end
21
- end.compact.sort.reverse.map { |_, filename| filename }
22
- end
23
- end
24
-
25
- def self.start_watching(paths, options={}, &block)
26
- listener = Listen.to(*(paths.dup << options))
27
- listener.change do |modified, added, removed|
28
- files = modified + added + removed
29
- directories = files.map { |file| File.dirname(file) }.uniq
30
- yield directories.map { |directory| Kicker::FSEvents::FSEvent.new(directory) }
31
- end
32
- listener.start
33
- listener
34
- end
35
- end
36
- end
@@ -1,57 +0,0 @@
1
- class Kicker
2
- class Job
3
- def self.attr_with_default(name, merge_hash = false, &default)
4
- # If `nil` this returns the `default`, unless explicitely set to `nil` by
5
- # the user.
6
- define_method(name) do
7
- if instance_variable_get("@#{name}_assigned")
8
- if assigned_value = instance_variable_get("@#{name}")
9
- merge_hash ? instance_eval(&default).merge(assigned_value) : assigned_value
10
- end
11
- else
12
- instance_eval(&default)
13
- end
14
- end
15
- define_method("#{name}=") do |value|
16
- instance_variable_set("@#{name}_assigned", true)
17
- instance_variable_set("@#{name}", value)
18
- end
19
- end
20
-
21
- attr_accessor :command, :exit_code, :output
22
-
23
- def initialize(attributes)
24
- @exit_code = 0
25
- @output = ''
26
- attributes.each { |k,v| send("#{k}=", v) }
27
- end
28
-
29
- def success?
30
- exit_code == 0
31
- end
32
-
33
- attr_with_default(:print_before) do
34
- "Executing: #{command}"
35
- end
36
-
37
- attr_with_default(:print_after) do
38
- # Show all output if it wasn't shown before and the command fails.
39
- "\n#{output}\n\n" if Kicker.silent? && !success?
40
- end
41
-
42
- # TODO default titles??
43
-
44
- attr_with_default(:notify_before, true) do
45
- { :title => "Kicker: Executing", :message => command }
46
- end
47
-
48
- attr_with_default(:notify_after, true) do
49
- message = Kicker.silent? ? "" : output
50
- if success?
51
- { :title => "Kicker: Success", :message => message }
52
- else
53
- { :title => "Kicker: Failed (#{exit_code})", :message => message }
54
- end
55
- end
56
- end
57
- end
@@ -1,31 +0,0 @@
1
- require 'notify'
2
-
3
- class Kicker
4
- module Notification #:nodoc:
5
- TITLE = 'Kicker'
6
-
7
- class << self
8
- attr_accessor :use, :app_bundle_identifier
9
- alias_method :use?, :use
10
-
11
- def notify(options)
12
- return unless use?
13
-
14
- unless message = options.delete(:message)
15
- raise "A notification requires a `:message'"
16
- end
17
-
18
- options = {
19
- :group => Dir.pwd,
20
- :activate => app_bundle_identifier
21
- }.merge(options)
22
-
23
- Notify.notify(TITLE, message, options)
24
- end
25
- end
26
- end
27
-
28
- Notification.use = ENV['NOTIFY'].to_s != ''
29
- Notification.app_bundle_identifier = 'com.apple.Terminal'
30
- end
31
-
@@ -1,96 +0,0 @@
1
- require 'optparse'
2
-
3
- class Kicker
4
- class << self
5
- attr_accessor :latency, :paths, :silent, :quiet, :clear_console
6
-
7
- def silent?
8
- @silent
9
- end
10
-
11
- def quiet?
12
- @quiet
13
- end
14
-
15
- def clear_console?
16
- @clear_console
17
- end
18
-
19
-
20
- def osx?
21
- RUBY_PLATFORM.downcase.include?("darwin")
22
- end
23
- end
24
-
25
- self.latency = 1
26
- self.paths = %w{ . }
27
- self.silent = false
28
- self.quiet = false
29
- self.clear_console = false
30
-
31
- module Options #:nodoc:
32
- DONT_SHOW_RECIPES = %w{ could_not_handle_file execute_cli_command dot_kick }
33
-
34
- def self.recipes_for_display
35
- Kicker::Recipes.recipe_files.map { |f| File.basename(f, '.rb') } - DONT_SHOW_RECIPES
36
- end
37
-
38
- def self.parser
39
- @parser ||= OptionParser.new do |opt|
40
- opt.banner = "Usage: #{$0} [options] [paths to watch]"
41
- opt.separator " "
42
- opt.separator " Available recipes: #{recipes_for_display.join(", ")}."
43
- opt.separator " "
44
-
45
- opt.on('-v', 'Print the Kicker version') do
46
- puts Kicker::VERSION
47
- exit
48
- end
49
-
50
- opt.on('-s', '--silent', 'Keep output to a minimum.') do |silent|
51
- Kicker.silent = true
52
- end
53
-
54
- opt.on('-q', '--quiet', "Quiet output. Don't print timestamps when logging.") do |quiet|
55
- Kicker.silent = Kicker.quiet = true
56
- end
57
-
58
- opt.on('-c', '--clear', "Clear console before each run.") do |clear|
59
- Kicker.clear_console = true
60
- end
61
-
62
-
63
- opt.on('--[no-]notification', 'Whether or not to send user notifications (on Mac OS X). Defaults to enabled.') do |notifications|
64
- Notification.use = notifications
65
- end
66
-
67
- if Kicker.osx?
68
- opt.on('--activate-app [BUNDLE ID]', "The application to activate when a notification is clicked. Defaults to `com.apple.Terminal'.") do |bundle_id|
69
- Kicker::Notification.app_bundle_identifier = bundle_id
70
- end
71
- end
72
-
73
- opt.on('-l', '--latency [FLOAT]', "The time to collect file change events before acting on them. Defaults to #{Kicker.latency} second.") do |latency|
74
- Kicker.latency = Float(latency)
75
- end
76
-
77
- opt.on('-r', '--recipe [NAME]', 'A named recipe to load.') do |name|
78
- recipe(name)
79
- end
80
- end
81
- end
82
-
83
- def self.parse(argv)
84
- parser.parse!(argv)
85
- Kicker.paths = argv unless argv.empty?
86
- end
87
- end
88
- end
89
-
90
- module Kernel
91
- # Returns the global OptionParser instance that recipes can use to add
92
- # options.
93
- def options
94
- Kicker::Options.parser
95
- end
96
- end
@@ -1,98 +0,0 @@
1
- module Kernel
2
- # If only given a <tt>name</tt>, the specified recipe will be loaded. For
3
- # instance, the following, in a <tt>.kick</tt> file, will load the Rails
4
- # recipe:
5
- #
6
- # recipe :rails
7
- #
8
- # However, this same method is used to define a callback that is called _if_
9
- # the recipe is loaded. For instance, the following, in a recipe file, will
10
- # be called if the recipe is actually used:
11
- #
12
- # recipe :rails do
13
- # # Load anything needed for the recipe.
14
- # process do
15
- # # ...
16
- # end
17
- # end
18
- def recipe(name, &block)
19
- Kicker::Recipes.recipe(name, &block)
20
- end
21
- end
22
-
23
- class Kicker
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
-
31
- class << self
32
- def reset!
33
- @recipes = nil
34
- # Always load all the base recipes
35
- load_recipe :execute_cli_command
36
- load_recipe :could_not_handle_file
37
- load_recipe :dot_kick
38
- end
39
-
40
- def recipes
41
- @recipes ||= {}
42
- end
43
-
44
- def recipe_filename(name)
45
- [
46
- USER_RECIPES_DIR,
47
- RECIPES_DIR
48
- ].each do |directory|
49
- filename = directory.join("#{name}.rb")
50
- return filename if filename.exist?
51
- end
52
- end
53
-
54
- def recipe_names
55
- recipe_files.map { |filename| filename.basename('.rb').to_s.to_sym }
56
- end
57
-
58
- def recipe_files
59
- RECIPES_DIRS.map{|dir| Pathname.glob(dir.join('*.rb')) }.flatten.uniq.map(&:expand_path)
60
- end
61
-
62
- def define_recipe(name, &block)
63
- recipes[name] = block
64
- end
65
-
66
- def load_recipe(name)
67
- if recipe_names.include?(name)
68
- load recipe_filename(name)
69
- else
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]}"
71
- end
72
- end
73
-
74
- def activate_recipe(name)
75
- unless recipes.has_key?(name)
76
- load_recipe(name)
77
- end
78
- if recipe = recipes[name]
79
- recipe.call
80
- else
81
- raise ArgumentError, "Can't activate the recipe `#{name}' because it hasn't been defined yet."
82
- end
83
- end
84
-
85
- # See Kernel#recipe for more information about the usage.
86
- def recipe(name, &block)
87
- name = name.to_sym
88
- if block_given?
89
- define_recipe(name, &block)
90
- else
91
- activate_recipe(name)
92
- end
93
- end
94
- end
95
-
96
- reset!
97
- end
98
- end