kicker 2.0.0
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/.gitignore +10 -0
- data/.kick +20 -0
- data/LICENSE +54 -0
- data/README.rdoc +141 -0
- data/Rakefile +37 -0
- data/TODO.rdoc +1 -0
- data/VERSION.yml +4 -0
- data/bin/kicker +5 -0
- data/html/images/kikker.jpg +0 -0
- data/kicker.gemspec +95 -0
- data/lib/kicker.rb +135 -0
- data/lib/kicker/callback_chain.rb +77 -0
- data/lib/kicker/core_ext.rb +30 -0
- data/lib/kicker/growl.rb +24 -0
- data/lib/kicker/options.rb +49 -0
- data/lib/kicker/recipes/could_not_handle_file.rb +5 -0
- data/lib/kicker/recipes/dot_kick.rb +35 -0
- data/lib/kicker/recipes/execute_cli_command.rb +6 -0
- data/lib/kicker/recipes/ignore.rb +39 -0
- data/lib/kicker/recipes/jstest.rb +8 -0
- data/lib/kicker/recipes/rails.rb +54 -0
- data/lib/kicker/utils.rb +71 -0
- data/lib/kicker/validate.rb +24 -0
- data/test/callback_chain_test.rb +150 -0
- data/test/core_ext_test.rb +28 -0
- data/test/filesystem_change_test.rb +100 -0
- data/test/fixtures/a_file_thats_reloaded.rb +2 -0
- data/test/initialization_test.rb +165 -0
- data/test/options_test.rb +30 -0
- data/test/recipes/could_not_handle_file_test.rb +11 -0
- data/test/recipes/dot_kick_test.rb +26 -0
- data/test/recipes/execute_cli_command_test.rb +32 -0
- data/test/recipes/ignore_test.rb +29 -0
- data/test/recipes/jstest_test.rb +31 -0
- data/test/recipes/rails_test.rb +73 -0
- data/test/test_helper.rb +6 -0
- data/test/utils_test.rb +123 -0
- data/vendor/growlnotifier/growl.rb +170 -0
- data/vendor/growlnotifier/growl_helpers.rb +25 -0
- data/vendor/rucola/fsevents.rb +136 -0
- metadata +110 -0
@@ -0,0 +1,77 @@
|
|
1
|
+
class Kicker
|
2
|
+
class CallbackChain < Array
|
3
|
+
alias_method :append_callback, :push
|
4
|
+
alias_method :prepend_callback, :unshift
|
5
|
+
|
6
|
+
def call(files)
|
7
|
+
each do |callback|
|
8
|
+
break if files.empty?
|
9
|
+
callback.call(files)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
attr_writer :pre_process_chain
|
16
|
+
def pre_process_chain
|
17
|
+
@pre_process_chain ||= CallbackChain.new
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_writer :process_chain
|
21
|
+
def process_chain
|
22
|
+
@process_chain ||= CallbackChain.new
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_writer :post_process_chain
|
26
|
+
def post_process_chain
|
27
|
+
@post_process_chain ||= CallbackChain.new
|
28
|
+
end
|
29
|
+
|
30
|
+
attr_writer :full_chain
|
31
|
+
def full_chain
|
32
|
+
@full_chain ||= CallbackChain.new([pre_process_chain, process_chain, post_process_chain])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def pre_process_chain
|
37
|
+
self.class.pre_process_chain
|
38
|
+
end
|
39
|
+
|
40
|
+
def process_chain
|
41
|
+
self.class.process_chain
|
42
|
+
end
|
43
|
+
|
44
|
+
def post_process_chain
|
45
|
+
self.class.post_process_chain
|
46
|
+
end
|
47
|
+
|
48
|
+
def full_chain
|
49
|
+
self.class.full_chain
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
module Kernel
|
54
|
+
# Adds a handler to the pre_process chain. This chain is ran before the
|
55
|
+
# process chain and is processed from first to last.
|
56
|
+
#
|
57
|
+
# Takes a +callback+ object that responds to <tt>#call</tt>, or a block.
|
58
|
+
def pre_process(callback = nil, &block)
|
59
|
+
Kicker.pre_process_chain.append_callback(block ? block : callback)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Adds a handler to the process chain. This chain is ran in between the
|
63
|
+
# pre_process and post_process chains. It is processed from first to last.
|
64
|
+
#
|
65
|
+
# Takes a +callback+ object that responds to <tt>#call</tt>, or a block.
|
66
|
+
def process(callback = nil, &block)
|
67
|
+
Kicker.process_chain.append_callback(block ? block : callback)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Adds a handler to the post_process chain. This chain is ran after the
|
71
|
+
# process chain and is processed from last to first.
|
72
|
+
#
|
73
|
+
# Takes a +callback+ object that responds to <tt>#call</tt>, or a block.
|
74
|
+
def post_process(callback = nil, &block)
|
75
|
+
Kicker.post_process_chain.prepend_callback(block ? block : callback)
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,30 @@
|
|
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 +flatten_and_compact+ is +true+, the result array will be flattened
|
13
|
+
# and compacted. The default is +true+.
|
14
|
+
def take_and_map(flatten_and_compact = true)
|
15
|
+
took = []
|
16
|
+
reject! do |x|
|
17
|
+
if result = yield(x)
|
18
|
+
took << result
|
19
|
+
end
|
20
|
+
end
|
21
|
+
if flatten_and_compact
|
22
|
+
took.flatten!
|
23
|
+
took.compact!
|
24
|
+
end
|
25
|
+
took
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
Array.send(:include, Kicker::ArrayExt)
|
data/lib/kicker/growl.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'growlnotifier/growl_helpers'
|
2
|
+
|
3
|
+
class Kicker
|
4
|
+
class << self
|
5
|
+
include Growl
|
6
|
+
attr_accessor :use_growl, :growl_command
|
7
|
+
end
|
8
|
+
|
9
|
+
GROWL_NOTIFICATIONS = {
|
10
|
+
:change => 'Change occured',
|
11
|
+
:succeeded => 'Command succeeded',
|
12
|
+
:failed => 'Command failed'
|
13
|
+
}
|
14
|
+
|
15
|
+
GROWL_DEFAULT_CALLBACK = lambda do
|
16
|
+
OSX::NSWorkspace.sharedWorkspace.launchApplication('Terminal')
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def start_growl!
|
22
|
+
Growl::Notifier.sharedInstance.register('Kicker', Kicker::GROWL_NOTIFICATIONS.values)
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
class Kicker
|
4
|
+
DONT_SHOW_RECIPES = %w{ could_not_handle_file execute_cli_command dot_kick }
|
5
|
+
|
6
|
+
def self.recipes_for_display
|
7
|
+
[RECIPES_DIR, USER_RECIPES_DIR].map do |dir|
|
8
|
+
Dir.glob("#{dir}/*.rb").map { |f| File.basename(f, '.rb') }
|
9
|
+
end.flatten - DONT_SHOW_RECIPES
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.option_parser
|
13
|
+
@option_parser ||= OptionParser.new do |opt|
|
14
|
+
opt.banner = "Usage: #{$0} [options] [paths to watch]"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
OPTION_PARSER_CALLBACK = lambda do |options|
|
19
|
+
option_parser.on('--[no-]growl', 'Whether or not to use Growl. Default is to use growl.') do |growl|
|
20
|
+
options[:growl] = growl
|
21
|
+
end
|
22
|
+
|
23
|
+
option_parser.on('--growl-command [COMMAND]', 'The command to execute when the Growl succeeded message is clicked.') do |command|
|
24
|
+
options[:growl_command] = command
|
25
|
+
end
|
26
|
+
|
27
|
+
option_parser.on('-l', '--latency [FLOAT]', "The time to collect file change events before acting on them. Defaults to 1 second.") do |latency|
|
28
|
+
options[:latency] = Float(latency)
|
29
|
+
end
|
30
|
+
|
31
|
+
option_parser.on('-r', '--recipe [NAME]', 'A named recipe to load.') do |recipe|
|
32
|
+
(options[:recipes] ||= []) << recipe
|
33
|
+
end
|
34
|
+
|
35
|
+
option_parser.separator " "
|
36
|
+
option_parser.separator " Available recipes:"
|
37
|
+
Kicker.recipes_for_display.each { |recipe| option_parser.separator " - #{recipe}" }
|
38
|
+
|
39
|
+
option_parser
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.parse_options(argv)
|
43
|
+
argv = argv.dup
|
44
|
+
options = { :growl => true }
|
45
|
+
OPTION_PARSER_CALLBACK.call(options).parse!(argv)
|
46
|
+
options[:paths] = argv unless argv.empty?
|
47
|
+
options
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module ReloadDotKick #:nodoc
|
2
|
+
class << self
|
3
|
+
def save_state
|
4
|
+
@features_before_dot_kick = $LOADED_FEATURES.dup
|
5
|
+
@chains_before_dot_kick = Kicker.full_chain.map { |c| c.dup }
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(files)
|
9
|
+
if files.delete('.kick')
|
10
|
+
reset!
|
11
|
+
load '.kick'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def reset!
|
16
|
+
remove_loaded_features!
|
17
|
+
reset_chains!
|
18
|
+
end
|
19
|
+
|
20
|
+
def reset_chains!
|
21
|
+
Kicker.full_chain = nil
|
22
|
+
|
23
|
+
chains = @chains_before_dot_kick.map { |c| c.dup }
|
24
|
+
Kicker.pre_process_chain, Kicker.process_chain, Kicker.post_process_chain = *chains
|
25
|
+
end
|
26
|
+
|
27
|
+
def remove_loaded_features!
|
28
|
+
($LOADED_FEATURES - @features_before_dot_kick).each do |feat|
|
29
|
+
$LOADED_FEATURES.delete(feat)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
process ReloadDotKick
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# A recipe which removes files from the files array, thus “ignoring” them.
|
2
|
+
#
|
3
|
+
# By default ignores logs, tmp, and svn and git files.
|
4
|
+
#
|
5
|
+
# See Kernel#ignore for info on how to ignore files.
|
6
|
+
module Ignore
|
7
|
+
def self.call(files) #:nodoc:
|
8
|
+
files.reject! { |file| ignores.any? { |ignore| file =~ ignore } }
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.ignores #:nodoc:
|
12
|
+
@ignores ||= []
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.ignore(regexp_or_string) #:nodoc:
|
16
|
+
ignores << (regexp_or_string.is_a?(Regexp) ? regexp_or_string : /^#{regexp_or_string}$/)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module Kernel
|
21
|
+
# Adds +regexp_or_string+ as an ignore rule.
|
22
|
+
#
|
23
|
+
# require 'ignore'
|
24
|
+
#
|
25
|
+
# ignore /^data\//
|
26
|
+
# ignore 'Rakefile'
|
27
|
+
#
|
28
|
+
# <em>Only available if the `ignore' recipe is required.</em>
|
29
|
+
def ignore(regexp_or_string)
|
30
|
+
Ignore.ignore(regexp_or_string)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
pre_process Ignore
|
35
|
+
|
36
|
+
ignore("tmp")
|
37
|
+
ignore(/\w+\.log/)
|
38
|
+
ignore(/\.(svn|git)\//)
|
39
|
+
ignore("svn-commit.tmp")
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Rails
|
2
|
+
# Maps +type+, for instance `models', to a test directory.
|
3
|
+
def self.type_to_test_dir(type)
|
4
|
+
case type
|
5
|
+
when "models"
|
6
|
+
"unit"
|
7
|
+
when "concerns"
|
8
|
+
"unit/concerns"
|
9
|
+
when "controllers", "views"
|
10
|
+
"functional"
|
11
|
+
when "helpers"
|
12
|
+
"unit/helpers"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns an array consiting of all functional tests.
|
17
|
+
def self.all_functional_tests
|
18
|
+
Dir.glob("test/functional/**/*_test.rb")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
process do |files|
|
23
|
+
test_files = files.take_and_map do |file|
|
24
|
+
case file
|
25
|
+
# Match any ruby test file and run it
|
26
|
+
when /^test\/.+_test\.rb$/
|
27
|
+
file
|
28
|
+
|
29
|
+
# Run all functional tests when routes.rb is saved
|
30
|
+
when 'config/routes.rb'
|
31
|
+
Rails.all_functional_tests
|
32
|
+
|
33
|
+
# Match lib/*
|
34
|
+
when /^(lib\/.+)\.rb$/
|
35
|
+
"test/#{$1}_test.rb"
|
36
|
+
|
37
|
+
# Match any file in app/ and map it to a test file
|
38
|
+
when %r{^app/(\w+)([\w/]*)/([\w\.]+)\.\w+$}
|
39
|
+
type, namespace, file = $1, $2, $3
|
40
|
+
|
41
|
+
if dir = Rails.type_to_test_dir(type)
|
42
|
+
if type == "views"
|
43
|
+
namespace = namespace.split('/')[1..-1]
|
44
|
+
file = "#{namespace.pop}_controller"
|
45
|
+
end
|
46
|
+
|
47
|
+
test_file = File.join("test", dir, namespace, "#{file}_test.rb")
|
48
|
+
test_file if File.exist?(test_file)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
run_ruby_tests test_files
|
54
|
+
end
|
data/lib/kicker/utils.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
class Kicker
|
2
|
+
module Utils #:nodoc:
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def execute(command)
|
6
|
+
@last_command = command
|
7
|
+
|
8
|
+
log "Change occured, executing command: #{command}"
|
9
|
+
Kicker.growl(GROWL_NOTIFICATIONS[:change], 'Kicker: Change occured, executing command:', command) if Kicker.use_growl
|
10
|
+
|
11
|
+
output = `#{command}`
|
12
|
+
output.strip.split("\n").each { |line| log " #{line}" }
|
13
|
+
|
14
|
+
log "Command #{last_command_succeeded? ? 'succeeded' : "failed (#{last_command_status})"}"
|
15
|
+
|
16
|
+
if Kicker.use_growl
|
17
|
+
if last_command_succeeded?
|
18
|
+
callback = Kicker.growl_command.nil? ? GROWL_DEFAULT_CALLBACK : lambda { system(Kicker.growl_command) }
|
19
|
+
Kicker.growl(GROWL_NOTIFICATIONS[:succeeded], "Kicker: Command succeeded", output, &callback)
|
20
|
+
else
|
21
|
+
Kicker.growl(GROWL_NOTIFICATIONS[:failed], "Kicker: Command failed (#{last_command_status})", output, &GROWL_DEFAULT_CALLBACK)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def last_command
|
27
|
+
@last_command
|
28
|
+
end
|
29
|
+
|
30
|
+
def log(message)
|
31
|
+
puts "[#{Time.now}] #{message}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def run_ruby_tests(tests)
|
35
|
+
execute "ruby -r #{tests.join(' -r ')} -e ''" unless tests.empty?
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def last_command_succeeded?
|
41
|
+
$?.success?
|
42
|
+
end
|
43
|
+
|
44
|
+
def last_command_status
|
45
|
+
$?.to_i
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
module Kernel
|
51
|
+
# Prints a +message+ with timestamp to stdout.
|
52
|
+
def log(message)
|
53
|
+
Kicker::Utils.log(message)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Executes the +command+, logs the output, and optionally growls.
|
57
|
+
def execute(command)
|
58
|
+
Kicker::Utils.execute(command)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns the last executed command.
|
62
|
+
def last_command
|
63
|
+
Kicker::Utils.last_command
|
64
|
+
end
|
65
|
+
|
66
|
+
# A convenience method that takes an array of Ruby test files and runs them
|
67
|
+
# collectively.
|
68
|
+
def run_ruby_tests(tests)
|
69
|
+
Kicker::Utils.run_ruby_tests(tests)
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Kicker
|
2
|
+
private
|
3
|
+
|
4
|
+
def validate_options!
|
5
|
+
validate_paths_and_command!
|
6
|
+
validate_paths_exist!
|
7
|
+
end
|
8
|
+
|
9
|
+
def validate_paths_and_command!
|
10
|
+
if process_chain.empty? && pre_process_chain.empty?
|
11
|
+
puts OPTION_PARSER_CALLBACK.call(nil).help
|
12
|
+
exit
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def validate_paths_exist!
|
17
|
+
@paths.each do |path|
|
18
|
+
unless File.exist?(path)
|
19
|
+
puts "The given path `#{path}' does not exist"
|
20
|
+
exit 1
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
|
3
|
+
describe "Kicker, concerning its callback chains" do
|
4
|
+
before do
|
5
|
+
@chains = [:pre_process_chain, :process_chain, :post_process_chain, :full_chain]
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should return the callback chain instances" do
|
9
|
+
@chains.each do |chain|
|
10
|
+
Kicker.send(chain).should.be.instance_of Kicker::CallbackChain
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should be accessible by an instance" do
|
15
|
+
kicker = Kicker.new({})
|
16
|
+
|
17
|
+
@chains.each do |chain|
|
18
|
+
kicker.send(chain).should == Kicker.send(chain)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should provide a shortcut method which appends a callback to the pre-process chain" do
|
23
|
+
Kicker.pre_process_chain.expects(:append_callback).with do |callback|
|
24
|
+
callback.call == :from_callback
|
25
|
+
end
|
26
|
+
|
27
|
+
pre_process { :from_callback }
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should provide a shortcut method which appends a callback to the process chain" do
|
31
|
+
Kicker.process_chain.expects(:append_callback).with do |callback|
|
32
|
+
callback.call == :from_callback
|
33
|
+
end
|
34
|
+
|
35
|
+
process { :from_callback }
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should provide a shortcut method which prepends a callback to the post-process chain" do
|
39
|
+
Kicker.post_process_chain.expects(:prepend_callback).with do |callback|
|
40
|
+
callback.call == :from_callback
|
41
|
+
end
|
42
|
+
|
43
|
+
post_process { :from_callback }
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should have assigned the chains to the `full_chain'" do
|
47
|
+
Kicker.full_chain.length.should == 3
|
48
|
+
Kicker.full_chain.each_with_index do |chain, index|
|
49
|
+
chain.should.be Kicker.send(@chains[index])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "Kicker::CallbackChain" do
|
55
|
+
it "should be a subclass of Array" do
|
56
|
+
Kicker::CallbackChain.superclass.should.be Array
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "An instance of Kicker::CallbackChain, concerning it's API" do
|
61
|
+
before do
|
62
|
+
@chain = Kicker::CallbackChain.new
|
63
|
+
|
64
|
+
@callback1 = lambda {}
|
65
|
+
@callback2 = lambda {}
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should append a callback" do
|
69
|
+
@chain << @callback1
|
70
|
+
@chain.append_callback(@callback2)
|
71
|
+
|
72
|
+
@chain.should == [@callback1, @callback2]
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should prepend a callback" do
|
76
|
+
@chain << @callback1
|
77
|
+
@chain.prepend_callback(@callback2)
|
78
|
+
|
79
|
+
@chain.should == [@callback2, @callback1]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "An instance of Kicker::CallbackChain, when calling the chain" do
|
84
|
+
before do
|
85
|
+
@chain = Kicker::CallbackChain.new
|
86
|
+
@result = []
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should call the callbacks from first to last" do
|
90
|
+
@chain.append_callback lambda { @result << 1 }
|
91
|
+
@chain.append_callback lambda { @result << 2 }
|
92
|
+
@chain.call(%w{ file })
|
93
|
+
@result.should == [1, 2]
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should pass the files array given to #call to each callback in the chain" do
|
97
|
+
array = %w{ /file/1 }
|
98
|
+
|
99
|
+
@chain.append_callback lambda { |files|
|
100
|
+
files.should.be array
|
101
|
+
files.concat(%w{ /file/2 })
|
102
|
+
}
|
103
|
+
|
104
|
+
@chain.append_callback lambda { |files|
|
105
|
+
files.should.be array
|
106
|
+
@result.concat(files)
|
107
|
+
}
|
108
|
+
|
109
|
+
@chain.call(array)
|
110
|
+
@result.should == %w{ /file/1 /file/2 }
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should halt the callback chain once the given array is empty" do
|
114
|
+
@chain.append_callback lambda { |files| @result << 1; files.clear }
|
115
|
+
@chain.append_callback lambda { |files| @result << 2 }
|
116
|
+
@chain.call(%w{ /file/1 /file/2 })
|
117
|
+
@result.should == [1]
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should not call any callback if the given array is empty" do
|
121
|
+
@chain.append_callback lambda { |files| @result << 1 }
|
122
|
+
@chain.call([])
|
123
|
+
@result.should == []
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should work with a chain of chains as well" do
|
127
|
+
array = %w{ file }
|
128
|
+
|
129
|
+
kicker_and_files = lambda do |kicker, files|
|
130
|
+
kicker.should.be @kicker
|
131
|
+
files.should.be array
|
132
|
+
end
|
133
|
+
|
134
|
+
chain1 = Kicker::CallbackChain.new([
|
135
|
+
lambda { |files| files.should.be array; @result << 1 },
|
136
|
+
lambda { |files| files.should.be array; @result << 2 }
|
137
|
+
])
|
138
|
+
|
139
|
+
chain2 = Kicker::CallbackChain.new([
|
140
|
+
lambda { |files| files.should.be array; @result << 3 },
|
141
|
+
lambda { |files| files.should.be array; @result << 4 }
|
142
|
+
])
|
143
|
+
|
144
|
+
@chain.append_callback chain1
|
145
|
+
@chain.append_callback chain2
|
146
|
+
|
147
|
+
@chain.call(array)
|
148
|
+
@result.should == [1, 2, 3, 4]
|
149
|
+
end
|
150
|
+
end
|