minmb-capistrano 2.15.4
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/.travis.yml +7 -0
- data/CHANGELOG +1170 -0
- data/Gemfile +13 -0
- data/README.md +94 -0
- data/Rakefile +11 -0
- data/bin/cap +4 -0
- data/bin/capify +92 -0
- data/capistrano.gemspec +40 -0
- data/lib/capistrano.rb +5 -0
- data/lib/capistrano/callback.rb +45 -0
- data/lib/capistrano/cli.rb +47 -0
- data/lib/capistrano/cli/execute.rb +85 -0
- data/lib/capistrano/cli/help.rb +125 -0
- data/lib/capistrano/cli/help.txt +81 -0
- data/lib/capistrano/cli/options.rb +243 -0
- data/lib/capistrano/cli/ui.rb +40 -0
- data/lib/capistrano/command.rb +303 -0
- data/lib/capistrano/configuration.rb +57 -0
- data/lib/capistrano/configuration/actions/file_transfer.rb +50 -0
- data/lib/capistrano/configuration/actions/inspect.rb +46 -0
- data/lib/capistrano/configuration/actions/invocation.rb +329 -0
- data/lib/capistrano/configuration/alias_task.rb +26 -0
- data/lib/capistrano/configuration/callbacks.rb +147 -0
- data/lib/capistrano/configuration/connections.rb +237 -0
- data/lib/capistrano/configuration/execution.rb +142 -0
- data/lib/capistrano/configuration/loading.rb +205 -0
- data/lib/capistrano/configuration/log_formatters.rb +75 -0
- data/lib/capistrano/configuration/namespaces.rb +223 -0
- data/lib/capistrano/configuration/roles.rb +77 -0
- data/lib/capistrano/configuration/servers.rb +116 -0
- data/lib/capistrano/configuration/variables.rb +127 -0
- data/lib/capistrano/errors.rb +19 -0
- data/lib/capistrano/ext/multistage.rb +64 -0
- data/lib/capistrano/ext/string.rb +5 -0
- data/lib/capistrano/extensions.rb +57 -0
- data/lib/capistrano/fix_rake_deprecated_dsl.rb +8 -0
- data/lib/capistrano/logger.rb +166 -0
- data/lib/capistrano/processable.rb +57 -0
- data/lib/capistrano/recipes/compat.rb +32 -0
- data/lib/capistrano/recipes/deploy.rb +625 -0
- data/lib/capistrano/recipes/deploy/assets.rb +201 -0
- data/lib/capistrano/recipes/deploy/dependencies.rb +44 -0
- data/lib/capistrano/recipes/deploy/local_dependency.rb +54 -0
- data/lib/capistrano/recipes/deploy/remote_dependency.rb +117 -0
- data/lib/capistrano/recipes/deploy/scm.rb +19 -0
- data/lib/capistrano/recipes/deploy/scm/accurev.rb +169 -0
- data/lib/capistrano/recipes/deploy/scm/base.rb +200 -0
- data/lib/capistrano/recipes/deploy/scm/bzr.rb +86 -0
- data/lib/capistrano/recipes/deploy/scm/cvs.rb +153 -0
- data/lib/capistrano/recipes/deploy/scm/darcs.rb +96 -0
- data/lib/capistrano/recipes/deploy/scm/git.rb +293 -0
- data/lib/capistrano/recipes/deploy/scm/mercurial.rb +137 -0
- data/lib/capistrano/recipes/deploy/scm/none.rb +55 -0
- data/lib/capistrano/recipes/deploy/scm/perforce.rb +152 -0
- data/lib/capistrano/recipes/deploy/scm/subversion.rb +121 -0
- data/lib/capistrano/recipes/deploy/strategy.rb +19 -0
- data/lib/capistrano/recipes/deploy/strategy/base.rb +92 -0
- data/lib/capistrano/recipes/deploy/strategy/checkout.rb +20 -0
- data/lib/capistrano/recipes/deploy/strategy/copy.rb +338 -0
- data/lib/capistrano/recipes/deploy/strategy/export.rb +20 -0
- data/lib/capistrano/recipes/deploy/strategy/remote.rb +52 -0
- data/lib/capistrano/recipes/deploy/strategy/remote_cache.rb +57 -0
- data/lib/capistrano/recipes/deploy/strategy/unshared_remote_cache.rb +21 -0
- data/lib/capistrano/recipes/standard.rb +37 -0
- data/lib/capistrano/recipes/templates/maintenance.rhtml +53 -0
- data/lib/capistrano/role.rb +102 -0
- data/lib/capistrano/server_definition.rb +56 -0
- data/lib/capistrano/shell.rb +265 -0
- data/lib/capistrano/ssh.rb +95 -0
- data/lib/capistrano/task_definition.rb +77 -0
- data/lib/capistrano/transfer.rb +218 -0
- data/lib/capistrano/version.rb +11 -0
- data/test/cli/execute_test.rb +132 -0
- data/test/cli/help_test.rb +165 -0
- data/test/cli/options_test.rb +329 -0
- data/test/cli/ui_test.rb +28 -0
- data/test/cli_test.rb +17 -0
- data/test/command_test.rb +322 -0
- data/test/configuration/actions/file_transfer_test.rb +61 -0
- data/test/configuration/actions/inspect_test.rb +76 -0
- data/test/configuration/actions/invocation_test.rb +288 -0
- data/test/configuration/alias_task_test.rb +118 -0
- data/test/configuration/callbacks_test.rb +201 -0
- data/test/configuration/connections_test.rb +439 -0
- data/test/configuration/execution_test.rb +175 -0
- data/test/configuration/loading_test.rb +148 -0
- data/test/configuration/namespace_dsl_test.rb +332 -0
- data/test/configuration/roles_test.rb +157 -0
- data/test/configuration/servers_test.rb +183 -0
- data/test/configuration/variables_test.rb +190 -0
- data/test/configuration_test.rb +77 -0
- data/test/deploy/local_dependency_test.rb +76 -0
- data/test/deploy/remote_dependency_test.rb +146 -0
- data/test/deploy/scm/accurev_test.rb +23 -0
- data/test/deploy/scm/base_test.rb +55 -0
- data/test/deploy/scm/bzr_test.rb +51 -0
- data/test/deploy/scm/darcs_test.rb +37 -0
- data/test/deploy/scm/git_test.rb +221 -0
- data/test/deploy/scm/mercurial_test.rb +134 -0
- data/test/deploy/scm/none_test.rb +35 -0
- data/test/deploy/scm/perforce_test.rb +23 -0
- data/test/deploy/scm/subversion_test.rb +40 -0
- data/test/deploy/strategy/copy_test.rb +360 -0
- data/test/extensions_test.rb +69 -0
- data/test/fixtures/cli_integration.rb +5 -0
- data/test/fixtures/config.rb +5 -0
- data/test/fixtures/custom.rb +3 -0
- data/test/logger_formatting_test.rb +149 -0
- data/test/logger_test.rb +134 -0
- data/test/recipes_test.rb +25 -0
- data/test/role_test.rb +11 -0
- data/test/server_definition_test.rb +121 -0
- data/test/shell_test.rb +96 -0
- data/test/ssh_test.rb +113 -0
- data/test/task_definition_test.rb +117 -0
- data/test/transfer_test.rb +168 -0
- data/test/utils.rb +37 -0
- metadata +316 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
require 'thread'
|
|
2
|
+
|
|
3
|
+
module Capistrano
|
|
4
|
+
class Configuration
|
|
5
|
+
module Variables
|
|
6
|
+
def self.included(base) #:nodoc:
|
|
7
|
+
%w(initialize respond_to? method_missing).each do |m|
|
|
8
|
+
base_name = m[/^\w+/]
|
|
9
|
+
punct = m[/\W+$/]
|
|
10
|
+
base.send :alias_method, "#{base_name}_without_variables#{punct}", m
|
|
11
|
+
base.send :alias_method, m, "#{base_name}_with_variables#{punct}"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# The hash of variables that have been defined in this configuration
|
|
16
|
+
# instance.
|
|
17
|
+
attr_reader :variables
|
|
18
|
+
|
|
19
|
+
# Set a variable to the given value.
|
|
20
|
+
def set(variable, *args, &block)
|
|
21
|
+
if variable.to_s !~ /^[_a-z]/
|
|
22
|
+
raise ArgumentError, "invalid variable `#{variable}' (variables must begin with an underscore, or a lower-case letter)"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
if !block_given? && args.empty? || block_given? && !args.empty?
|
|
26
|
+
raise ArgumentError, "you must specify exactly one of either a value or a block"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
if args.length > 1
|
|
30
|
+
raise ArgumentError, "wrong number of arguments (#{args.length} for 1)"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
value = args.empty? ? block : args.first
|
|
34
|
+
sym = variable.to_sym
|
|
35
|
+
protect(sym) { @variables[sym] = value }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
alias :[]= :set
|
|
39
|
+
|
|
40
|
+
# Removes any trace of the given variable.
|
|
41
|
+
def unset(variable)
|
|
42
|
+
sym = variable.to_sym
|
|
43
|
+
protect(sym) do
|
|
44
|
+
@original_procs.delete(sym)
|
|
45
|
+
@variables.delete(sym)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Returns true if the variable has been defined, and false otherwise.
|
|
50
|
+
def exists?(variable)
|
|
51
|
+
@variables.key?(variable.to_sym)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# If the variable was originally a proc value, it will be reset to it's
|
|
55
|
+
# original proc value. Otherwise, this method does nothing. It returns
|
|
56
|
+
# true if the variable was actually reset.
|
|
57
|
+
def reset!(variable)
|
|
58
|
+
sym = variable.to_sym
|
|
59
|
+
protect(sym) do
|
|
60
|
+
if @original_procs.key?(sym)
|
|
61
|
+
@variables[sym] = @original_procs.delete(sym)
|
|
62
|
+
true
|
|
63
|
+
else
|
|
64
|
+
false
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Access a named variable. If the value of the variable responds_to? :call,
|
|
70
|
+
# #call will be invoked (without parameters) and the return value cached
|
|
71
|
+
# and returned.
|
|
72
|
+
def fetch(variable, *args)
|
|
73
|
+
if !args.empty? && block_given?
|
|
74
|
+
raise ArgumentError, "you must specify either a default value or a block, but not both"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
sym = variable.to_sym
|
|
78
|
+
protect(sym) do
|
|
79
|
+
if !@variables.key?(sym)
|
|
80
|
+
return args.first unless args.empty?
|
|
81
|
+
return yield(variable) if block_given?
|
|
82
|
+
raise IndexError, "`#{variable}' not found"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
if @variables[sym].respond_to?(:call)
|
|
86
|
+
@original_procs[sym] = @variables[sym]
|
|
87
|
+
@variables[sym] = @variables[sym].call
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
@variables[sym]
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def [](variable)
|
|
95
|
+
fetch(variable, nil)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def initialize_with_variables(*args) #:nodoc:
|
|
99
|
+
initialize_without_variables(*args)
|
|
100
|
+
@variables = {}
|
|
101
|
+
@original_procs = {}
|
|
102
|
+
@variable_locks = Hash.new { |h,k| h[k] = Mutex.new }
|
|
103
|
+
|
|
104
|
+
set :ssh_options, {}
|
|
105
|
+
set :logger, logger
|
|
106
|
+
end
|
|
107
|
+
private :initialize_with_variables
|
|
108
|
+
|
|
109
|
+
def protect(variable)
|
|
110
|
+
@variable_locks[variable.to_sym].synchronize { yield }
|
|
111
|
+
end
|
|
112
|
+
private :protect
|
|
113
|
+
|
|
114
|
+
def respond_to_with_variables?(sym, include_priv=false) #:nodoc:
|
|
115
|
+
@variables.has_key?(sym.to_sym) || respond_to_without_variables?(sym, include_priv)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def method_missing_with_variables(sym, *args, &block) #:nodoc:
|
|
119
|
+
if args.length == 0 && block.nil? && @variables.has_key?(sym)
|
|
120
|
+
self[sym]
|
|
121
|
+
else
|
|
122
|
+
method_missing_without_variables(sym, *args, &block)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Capistrano
|
|
2
|
+
|
|
3
|
+
Error = Class.new(RuntimeError)
|
|
4
|
+
|
|
5
|
+
CaptureError = Class.new(Capistrano::Error)
|
|
6
|
+
NoSuchTaskError = Class.new(Capistrano::Error)
|
|
7
|
+
NoMatchingServersError = Class.new(Capistrano::Error)
|
|
8
|
+
|
|
9
|
+
class RemoteError < Error
|
|
10
|
+
attr_accessor :hosts
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
ConnectionError = Class.new(Capistrano::RemoteError)
|
|
14
|
+
TransferError = Class.new(Capistrano::RemoteError)
|
|
15
|
+
CommandError = Class.new(Capistrano::RemoteError)
|
|
16
|
+
|
|
17
|
+
LocalArgumentError = Class.new(Capistrano::Error)
|
|
18
|
+
|
|
19
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
|
|
3
|
+
unless Capistrano::Configuration.respond_to?(:instance)
|
|
4
|
+
abort "capistrano/ext/multistage requires Capistrano 2"
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
Capistrano::Configuration.instance.load do
|
|
8
|
+
location = fetch(:stage_dir, "config/deploy")
|
|
9
|
+
|
|
10
|
+
unless exists?(:stages)
|
|
11
|
+
set :stages, Dir["#{location}/*.rb"].map { |f| File.basename(f, ".rb") }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
stages.each do |name|
|
|
15
|
+
desc "Set the target stage to `#{name}'."
|
|
16
|
+
task(name) do
|
|
17
|
+
set :stage, name.to_sym
|
|
18
|
+
|
|
19
|
+
file = "#{location}/#{stage}"
|
|
20
|
+
load file if file_in_load_path?(file)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
on :load do
|
|
25
|
+
if stages.include?(ARGV.first)
|
|
26
|
+
# Execute the specified stage so that recipes required in stage can contribute to task list
|
|
27
|
+
find_and_execute_task(ARGV.first) if ARGV.any?{ |option| option =~ /-T|--tasks|-e|--explain/ }
|
|
28
|
+
else
|
|
29
|
+
# Execute the default stage so that recipes required in stage can contribute tasks
|
|
30
|
+
find_and_execute_task(default_stage) if exists?(:default_stage)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
namespace :multistage do
|
|
35
|
+
desc "[internal] Ensure that a stage has been selected."
|
|
36
|
+
task :ensure do
|
|
37
|
+
if !exists?(:stage)
|
|
38
|
+
if exists?(:default_stage)
|
|
39
|
+
logger.important "Defaulting to `#{default_stage}'"
|
|
40
|
+
find_and_execute_task(default_stage)
|
|
41
|
+
else
|
|
42
|
+
abort "No stage specified. Please specify one of: #{stages.join(', ')} (e.g. `cap #{stages.first} #{ARGV.last}')"
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
desc "Stub out the staging config files."
|
|
48
|
+
task :prepare do
|
|
49
|
+
FileUtils.mkdir_p(location)
|
|
50
|
+
stages.each do |name|
|
|
51
|
+
file = File.join(location, name + ".rb")
|
|
52
|
+
unless File.exists?(file)
|
|
53
|
+
File.open(file, "w") do |f|
|
|
54
|
+
f.puts "# #{name.upcase}-specific deployment configuration"
|
|
55
|
+
f.puts "# please put general deployment config in config/deploy.rb"
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
on :start, "multistage:ensure", :except => stages + ['multistage:prepare']
|
|
63
|
+
|
|
64
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module Capistrano
|
|
2
|
+
class ExtensionProxy #:nodoc:
|
|
3
|
+
def initialize(config, mod)
|
|
4
|
+
@config = config
|
|
5
|
+
extend(mod)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def method_missing(sym, *args, &block)
|
|
9
|
+
@config.send(sym, *args, &block)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Holds the set of registered plugins, keyed by name (where the name is a
|
|
14
|
+
# symbol).
|
|
15
|
+
EXTENSIONS = {}
|
|
16
|
+
|
|
17
|
+
# Register the given module as a plugin with the given name. It will henceforth
|
|
18
|
+
# be available via a proxy object on Configuration instances, accessible by
|
|
19
|
+
# a method with the given name.
|
|
20
|
+
def self.plugin(name, mod)
|
|
21
|
+
name = name.to_sym
|
|
22
|
+
return false if EXTENSIONS.has_key?(name)
|
|
23
|
+
|
|
24
|
+
methods = Capistrano::Configuration.public_instance_methods +
|
|
25
|
+
Capistrano::Configuration.protected_instance_methods +
|
|
26
|
+
Capistrano::Configuration.private_instance_methods
|
|
27
|
+
|
|
28
|
+
if methods.any? { |m| m.to_sym == name }
|
|
29
|
+
raise Capistrano::Error, "registering a plugin named `#{name}' would shadow a method on Capistrano::Configuration with the same name"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
Capistrano::Configuration.class_eval <<-STR, __FILE__, __LINE__+1
|
|
33
|
+
def #{name}
|
|
34
|
+
@__#{name}_proxy ||= Capistrano::ExtensionProxy.new(self, Capistrano::EXTENSIONS[#{name.inspect}])
|
|
35
|
+
end
|
|
36
|
+
STR
|
|
37
|
+
|
|
38
|
+
EXTENSIONS[name] = mod
|
|
39
|
+
return true
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Unregister the plugin with the given name.
|
|
43
|
+
def self.remove_plugin(name)
|
|
44
|
+
name = name.to_sym
|
|
45
|
+
if EXTENSIONS.delete(name)
|
|
46
|
+
Capistrano::Configuration.send(:remove_method, name)
|
|
47
|
+
return true
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
return false
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.configuration(*args) #:nodoc:
|
|
54
|
+
warn "[DEPRECATION] Capistrano.configuration is deprecated. Use Capistrano::Configuration.instance instead"
|
|
55
|
+
Capistrano::Configuration.instance(*args)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
module Capistrano
|
|
2
|
+
class Logger #:nodoc:
|
|
3
|
+
attr_accessor :level, :device, :disable_formatters
|
|
4
|
+
|
|
5
|
+
IMPORTANT = 0
|
|
6
|
+
INFO = 1
|
|
7
|
+
DEBUG = 2
|
|
8
|
+
TRACE = 3
|
|
9
|
+
|
|
10
|
+
MAX_LEVEL = 3
|
|
11
|
+
|
|
12
|
+
COLORS = {
|
|
13
|
+
:none => "0",
|
|
14
|
+
:black => "30",
|
|
15
|
+
:red => "31",
|
|
16
|
+
:green => "32",
|
|
17
|
+
:yellow => "33",
|
|
18
|
+
:blue => "34",
|
|
19
|
+
:magenta => "35",
|
|
20
|
+
:cyan => "36",
|
|
21
|
+
:white => "37"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
STYLES = {
|
|
25
|
+
:bright => 1,
|
|
26
|
+
:dim => 2,
|
|
27
|
+
:underscore => 4,
|
|
28
|
+
:blink => 5,
|
|
29
|
+
:reverse => 7,
|
|
30
|
+
:hidden => 8
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
# Set up default formatters
|
|
34
|
+
@default_formatters = [
|
|
35
|
+
# TRACE
|
|
36
|
+
{ :match => /command finished/, :color => :white, :style => :dim, :level => 3, :priority => -10 },
|
|
37
|
+
{ :match => /executing locally/, :color => :yellow, :level => 3, :priority => -20 },
|
|
38
|
+
|
|
39
|
+
# DEBUG
|
|
40
|
+
{ :match => /executing `.*/, :color => :green, :level => 2, :priority => -10, :timestamp => true },
|
|
41
|
+
{ :match => /.*/, :color => :yellow, :level => 2, :priority => -30 },
|
|
42
|
+
|
|
43
|
+
# INFO
|
|
44
|
+
{ :match => /.*out\] (fatal:|ERROR:).*/, :color => :red, :level => 1, :priority => -10 },
|
|
45
|
+
{ :match => /Permission denied/, :color => :red, :level => 1, :priority => -20 },
|
|
46
|
+
{ :match => /sh: .+: command not found/, :color => :magenta, :level => 1, :priority => -30 },
|
|
47
|
+
|
|
48
|
+
# IMPORTANT
|
|
49
|
+
{ :match => /^err ::/, :color => :red, :level => 0, :priority => -10 },
|
|
50
|
+
{ :match => /.*/, :color => :blue, :level => 0, :priority => -20 }
|
|
51
|
+
]
|
|
52
|
+
@formatters = @default_formatters
|
|
53
|
+
|
|
54
|
+
class << self
|
|
55
|
+
def default_formatters
|
|
56
|
+
@default_formatters
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def default_formatters=(defaults=nil)
|
|
60
|
+
@default_formatters = [defaults].flatten
|
|
61
|
+
|
|
62
|
+
# reset the formatters
|
|
63
|
+
@formatters = @default_formatters
|
|
64
|
+
@sorted_formatters = nil
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def add_formatter(options) #:nodoc:
|
|
68
|
+
@formatters.push(options)
|
|
69
|
+
@sorted_formatters = nil
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def sorted_formatters
|
|
73
|
+
# Sort matchers in reverse order so we can break if we found a match.
|
|
74
|
+
@sorted_formatters ||= @formatters.sort_by { |i| -(i[:priority] || i[:prio] || 0) }
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def initialize(options={})
|
|
79
|
+
output = options[:output] || $stderr
|
|
80
|
+
if output.respond_to?(:puts)
|
|
81
|
+
@device = output
|
|
82
|
+
else
|
|
83
|
+
@device = File.open(output.to_str, "a")
|
|
84
|
+
@needs_close = true
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
@options = options
|
|
88
|
+
@level = options[:level] || 0
|
|
89
|
+
@disable_formatters = options[:disable_formatters]
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def close
|
|
93
|
+
device.close if @needs_close
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def log(level, message, line_prefix=nil)
|
|
97
|
+
if level <= self.level
|
|
98
|
+
# Only format output if device is a TTY or formatters are not disabled
|
|
99
|
+
if device.tty? && !@disable_formatters
|
|
100
|
+
color = :none
|
|
101
|
+
style = nil
|
|
102
|
+
|
|
103
|
+
Logger.sorted_formatters.each do |formatter|
|
|
104
|
+
if (formatter[:level] == level || formatter[:level].nil?)
|
|
105
|
+
if message =~ formatter[:match] || line_prefix =~ formatter[:match]
|
|
106
|
+
color = formatter[:color] if formatter[:color]
|
|
107
|
+
style = formatter[:style] || formatter[:attribute] # (support original cap colors)
|
|
108
|
+
message.gsub!(formatter[:match], formatter[:replace]) if formatter[:replace]
|
|
109
|
+
message = formatter[:prepend] + message unless formatter[:prepend].nil?
|
|
110
|
+
message = message + formatter[:append] unless formatter[:append].nil?
|
|
111
|
+
message = Time.now.strftime('%Y-%m-%d %T') + ' ' + message if formatter[:timestamp]
|
|
112
|
+
break unless formatter[:replace]
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
if color == :hide
|
|
118
|
+
# Don't do anything if color is set to :hide
|
|
119
|
+
return false
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
term_color = COLORS[color]
|
|
123
|
+
term_style = STYLES[style]
|
|
124
|
+
|
|
125
|
+
# Don't format message if no color or style
|
|
126
|
+
unless color == :none and style.nil?
|
|
127
|
+
unless line_prefix.nil?
|
|
128
|
+
line_prefix = format(line_prefix, term_color, term_style, nil)
|
|
129
|
+
end
|
|
130
|
+
message = format(message, term_color, term_style)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
indent = "%*s" % [MAX_LEVEL, "*" * (MAX_LEVEL - level)]
|
|
135
|
+
(RUBY_VERSION >= "1.9" ? message.lines : message).each do |line|
|
|
136
|
+
if line_prefix
|
|
137
|
+
device.puts "#{indent} [#{line_prefix}] #{line.strip}\n"
|
|
138
|
+
else
|
|
139
|
+
device.puts "#{indent} #{line.strip}\n"
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def important(message, line_prefix=nil)
|
|
146
|
+
log(IMPORTANT, message, line_prefix)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def info(message, line_prefix=nil)
|
|
150
|
+
log(INFO, message, line_prefix)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def debug(message, line_prefix=nil)
|
|
154
|
+
log(DEBUG, message, line_prefix)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def trace(message, line_prefix=nil)
|
|
158
|
+
log(TRACE, message, line_prefix)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def format(message, color, style, nl = "\n")
|
|
162
|
+
style = "#{style};" if style
|
|
163
|
+
"\e[#{style}#{color}m" + message.to_s.strip + "\e[0m#{nl}"
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|