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.
- 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
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
|
data/lib/kicker/core_ext.rb
DELETED
@@ -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)
|
data/lib/kicker/fsevents.rb
DELETED
@@ -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
|
data/lib/kicker/job.rb
DELETED
@@ -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
|
data/lib/kicker/notification.rb
DELETED
@@ -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
|
-
|
data/lib/kicker/options.rb
DELETED
@@ -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
|
data/lib/kicker/recipes.rb
DELETED
@@ -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
|