guard 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -14,6 +14,7 @@ module Guard
14
14
  # * rb-notifu
15
15
  # * emacs
16
16
  # * Terminal Notifier
17
+ # * Terminal Title
17
18
  # * Tmux
18
19
  #
19
20
  # Please see the documentation of each notifier for more information about the requirements
@@ -46,22 +47,26 @@ module Guard
46
47
  require 'guard/notifiers/rb_notifu'
47
48
  require 'guard/notifiers/emacs'
48
49
  require 'guard/notifiers/terminal_notifier'
50
+ require 'guard/notifiers/terminal_title'
49
51
  require 'guard/notifiers/tmux'
50
52
 
51
53
  extend self
52
54
 
53
- # List of available notifiers. It needs to be a nested hash instead of
55
+ # List of available notifiers, grouped by functionality. It needs to be a nested hash instead of
54
56
  # a simpler Hash, because it maintains its order on Ruby 1.8.7 also.
55
57
  NOTIFIERS = [
56
- [:growl, ::Guard::Notifier::Growl],
57
- [:gntp, ::Guard::Notifier::GNTP],
58
- [:growl_notify, ::Guard::Notifier::GrowlNotify],
59
- [:libnotify, ::Guard::Notifier::Libnotify],
60
- [:notifysend, ::Guard::Notifier::NotifySend],
61
- [:notifu, ::Guard::Notifier::Notifu],
62
- [:emacs, ::Guard::Notifier::Emacs],
63
- [:terminal_notifier, ::Guard::Notifier::TerminalNotifier],
64
- [:tmux, ::Guard::Notifier::Tmux]
58
+ [
59
+ [:gntp, ::Guard::Notifier::GNTP],
60
+ [:growl, ::Guard::Notifier::Growl],
61
+ [:growl_notify, ::Guard::Notifier::GrowlNotify],
62
+ [:terminal_notifier, ::Guard::Notifier::TerminalNotifier],
63
+ [:libnotify, ::Guard::Notifier::Libnotify],
64
+ [:notifysend, ::Guard::Notifier::NotifySend],
65
+ [:notifu, ::Guard::Notifier::Notifu]
66
+ ],
67
+ [[:emacs, ::Guard::Notifier::Emacs]],
68
+ [[:tmux, ::Guard::Notifier::Tmux]],
69
+ [[:terminal_title, ::Guard::Notifier::TerminalTitle]]
65
70
  ]
66
71
 
67
72
  # Get the available notifications.
@@ -110,6 +115,17 @@ module Guard
110
115
  ENV['GUARD_NOTIFY'] = 'false'
111
116
  end
112
117
 
118
+ # Toggle the system notifications on/off
119
+ #
120
+ def toggle
121
+ if ENV['GUARD_NOTIFY'] == 'true'
122
+ ::Guard::UI.info 'Turn off notifications'
123
+ turn_off
124
+ else
125
+ turn_on
126
+ end
127
+ end
128
+
113
129
  # Test if the notifications are on.
114
130
  #
115
131
  # @return [Boolean] whether the notifications are on
@@ -164,20 +180,26 @@ module Guard
164
180
 
165
181
  # Get the notifier module for the given name.
166
182
  #
167
- # @param [Symbol] the notifier name
183
+ # @param [Symbol] name the notifier name
168
184
  # @return [Module] the notifier module
169
185
  #
170
186
  def get_notifier_module(name)
171
- notifier = NOTIFIERS.detect { |n| n.first == name }
187
+ notifier = NOTIFIERS.flatten(1).detect { |n| n.first == name }
172
188
  notifier ? notifier.last : notifier
173
189
  end
174
190
 
175
191
  # Auto detect the available notification library. This goes through
176
192
  # the list of supported notification gems and picks the first that
177
- # is available.
193
+ # is available in each notification group.
178
194
  #
179
195
  def auto_detect_notification
180
- available = NOTIFIERS.map { |n| n.first }.any? { |notifier| add_notification(notifier, { }, true) }
196
+ available = nil
197
+
198
+ NOTIFIERS.each do |group|
199
+ added = group.map { |n| n.first }.find { |notifier| add_notification(notifier, { }, true) }
200
+ available = available || added
201
+ end
202
+
181
203
  ::Guard::UI.info('Guard could not detect any of the supported notification libraries.') unless available
182
204
  end
183
205
 
@@ -3,14 +3,6 @@ require 'rbconfig'
3
3
  module Guard
4
4
  module Notifier
5
5
 
6
- # Default options for EmacsClient
7
- DEFAULTS = {
8
- :client => 'emacsclient',
9
- :success => 'ForestGreen',
10
- :failed => 'Firebrick',
11
- :default => 'Black',
12
- }
13
-
14
6
  # Send a notification to Emacs with emacsclient (http://www.emacswiki.org/emacs/EmacsClient).
15
7
  #
16
8
  # @example Add the `:emacs` notifier to your `Guardfile`
@@ -19,6 +11,13 @@ module Guard
19
11
  module Emacs
20
12
  extend self
21
13
 
14
+ DEFAULTS = {
15
+ :client => 'emacsclient',
16
+ :success => 'ForestGreen',
17
+ :failed => 'Firebrick',
18
+ :default => 'Black',
19
+ }
20
+
22
21
  # Test if Emacs with running server is available.
23
22
  #
24
23
  # @param [Boolean] silent true if no error messages should be shown
@@ -41,28 +40,33 @@ module Guard
41
40
  # @param [String] message the notification message body
42
41
  # @param [String] image the path to the notification image
43
42
  # @param [Hash] options additional notification library options
44
- # @option options [Boolean] sticky make the notification sticky
43
+ # @option options [String] success the color to use for success notifications (default is 'ForestGreen')
44
+ # @option options [String] failed the color to use for failure notifications (default is 'Firebrick')
45
+ # @option options [String] pending the color to use for pending notifications
46
+ # @option options [String] default the default color to use (default is 'Black')
47
+ # @option options [String] client the client to use for notification (default is 'emacsclient')
45
48
  # @option options [String, Integer] priority specify an int or named key (default is 0)
46
49
  #
47
50
  def notify(type, title, message, image, options = { })
48
- system(%(#{ DEFAULTS[:client] } --eval "(set-face-background 'modeline \\"#{ emacs_color(type) }\\")"))
51
+ options = DEFAULTS.merge options
52
+ color = emacs_color type, options
53
+ system(%(#{ options[:client] } --eval "(set-face-background 'modeline \\"#{ color }\\")"))
49
54
  end
50
55
 
51
56
  # Get the Emacs color for the notification type.
52
57
  # You can configure your own color by overwrite the defaults.
53
58
  #
54
59
  # @param [String] type the notification type
60
+ # @param [Hash] options aditional notification options
61
+ # @option options [String] success the color to use for success notifications (default is 'ForestGreen')
62
+ # @option options [String] failed the color to use for failure notifications (default is 'Firebrick')
63
+ # @option options [String] pending the color to use for pending notifications
64
+ # @option options [String] default the default color to use (default is 'Black')
55
65
  # @return [String] the name of the emacs color
56
66
  #
57
- def emacs_color(type)
58
- case type
59
- when 'success'
60
- DEFAULTS[:success]
61
- when 'failed'
62
- DEFAULTS[:failed]
63
- else
64
- DEFAULTS[:default]
65
- end
67
+ def emacs_color(type, options = {})
68
+ default = options[:default] || DEFAULTS[:default]
69
+ options.fetch(type.to_sym, default)
66
70
  end
67
71
  end
68
72
  end
@@ -0,0 +1,35 @@
1
+ # Module for notifying test result to terminal title
2
+ module Guard
3
+ module Notifier
4
+ module TerminalTitle
5
+ extend self
6
+
7
+ # Test if the notification library is available.
8
+ #
9
+ # @param [Boolean] silent true if no error messages should be shown
10
+ # @return [Boolean] the availability status
11
+ #
12
+ def available?(silent = false)
13
+ true
14
+ end
15
+
16
+ # Show a system notification.
17
+ #
18
+ # @param [String] type the notification type. Either 'success', 'pending', 'failed' or 'notify'
19
+ # @param [String] title the notification title
20
+ # @param [String] message the notification message body
21
+ # @param [String] image the path to the notification image
22
+ # @param [Hash] options additional notification library options
23
+ #
24
+ def notify(type, title, message, image, options = { })
25
+ message.sub!(/^\n/, '')
26
+ message.sub!(/\n.*/m, '')
27
+ set_terminal_title("[#{title}] #{message}")
28
+ end
29
+
30
+ def set_terminal_title(text)
31
+ puts "\e]2;#{text}\a"
32
+ end
33
+ end
34
+ end
35
+ end
@@ -3,20 +3,34 @@ module Guard
3
3
 
4
4
  # Default options for Tmux
5
5
 
6
- # Changes the color of the Tmux status bar
6
+ # Changes the color of the Tmux status bar and optionally
7
+ # shows messages in the status bar.
7
8
  #
8
9
  # @example Add the `:tmux` notifier to your `Guardfile`
9
10
  # notification :tmux
10
11
  #
12
+ # @example Enable text messages
13
+ # notification :tmux, :display_message => true
14
+ #
15
+ # @example Customize the tmux status colored for notifications
16
+ # notification :tmux, :color_location => 'status-right-bg'
17
+ #
11
18
  module Tmux
12
19
  extend self
13
20
 
14
21
  DEFAULTS = {
15
- :client => 'tmux',
16
- :tmux_environment => 'TMUX',
17
- :success => 'green',
18
- :failed => 'red',
19
- :default => 'green'
22
+ :client => 'tmux',
23
+ :tmux_environment => 'TMUX',
24
+ :success => 'green',
25
+ :failed => 'red',
26
+ :pending => 'yellow',
27
+ :default => 'green',
28
+ :timeout => 5,
29
+ :display_message => false,
30
+ :default_message_format => '%s - %s',
31
+ :default_message_color => 'white',
32
+ :line_separator => ' - ',
33
+ :color_location => 'status-left-bg'
20
34
  }
21
35
 
22
36
  # Test if currently running in a Tmux session
@@ -33,19 +47,60 @@ module Guard
33
47
  end
34
48
  end
35
49
 
36
- # Show a system notification.
50
+ # Show a system notification. By default, the Tmux notifier only makes use of a color based
51
+ # notification, changing the background color of the `color_location` to the color defined
52
+ # in either the `success`, `failed`, `pending` or `default`, depending on the notification type.
53
+ # If you also want display a text message, you have to enable it explicit by setting `display_message`
54
+ # to `true`.
37
55
  #
38
56
  # @param [String] type the notification type. Either 'success', 'pending', 'failed' or 'notify'
39
57
  # @param [String] title the notification title
40
58
  # @param [String] message the notification message body
41
59
  # @param [String] image the path to the notification image
42
60
  # @param [Hash] options additional notification library options
43
- # @option options [Boolean] sticky make the notification sticky
44
- # @option options [String, Integer] priority specify an int or named key (default is 0)
61
+ # @option options [String] color_location the location where to draw the color notification
62
+ # @option options [Boolean] display_message whether to display a message or not
45
63
  #
46
64
  def notify(type, title, message, image, options = { })
47
65
  color = tmux_color type, options
48
- system("#{ DEFAULTS[:client] } set -g status-left-bg #{ color }")
66
+ color_location = options[:color_location] || DEFAULTS[:color_location]
67
+ system("#{ DEFAULTS[:client] } set -g #{ color_location } #{ color }")
68
+
69
+ show_message = options[:display_message] || DEFAULTS[:display_message]
70
+ display_message(type, title, message, options) if show_message
71
+ end
72
+
73
+ # Display a message in the status bar of tmux.
74
+ #
75
+ # @param [String] type the notification type. Either 'success', 'pending', 'failed' or 'notify'
76
+ # @param [String] title the notification title
77
+ # @param [String] message the notification message body
78
+ # @param [Hash] options additional notification library options
79
+ # @option options [Integer] timeout the amount of seconds to show the message in the status bar
80
+ # @option options [String] success_message_format a string to use as formatter for the success message.
81
+ # @option options [String] failed_message_format a string to use as formatter for the failed message.
82
+ # @option options [String] pending_message_format a string to use as formatter for the pending message.
83
+ # @option options [String] default_message_format a string to use as formatter when no format per type is defined.
84
+ # @option options [String] success_message_color the success notification foreground color name.
85
+ # @option options [String] failed_message_color the failed notification foreground color name.
86
+ # @option options [String] pending_message_color the pending notification foreground color name.
87
+ # @option options [String] default_message_color a notification foreground color to use when no color per type is defined.
88
+ # @option options [String] line_separator a string to use instead of a line-break.
89
+ #
90
+ def display_message(type, title, message, options = { })
91
+ message_format = options["#{ type }_message_format".to_sym] || options[:default_message_format] || DEFAULTS[:default_message_format]
92
+ message_color = options["#{ type }_message_color".to_sym] || options[:default_message_color] || DEFAULTS[:default_message_color]
93
+ display_time = options[:timeout] || DEFAULTS[:timeout]
94
+ separator = options[:line_separator] || DEFAULTS[:line_separator]
95
+
96
+ color = tmux_color type, options
97
+ formatted_message = message.split("\n").join(separator)
98
+ display_message = message_format % [title, formatted_message]
99
+
100
+ system("#{ DEFAULTS[:client] } set display-time #{ display_time * 1000 }")
101
+ system("#{ DEFAULTS[:client] } set message-fg #{ message_color }")
102
+ system("#{ DEFAULTS[:client] } set message-bg #{ color }")
103
+ system("#{ DEFAULTS[:client] } display-message '#{ display_message }'")
49
104
  end
50
105
 
51
106
  # Get the Tmux color for the notification type.
@@ -60,6 +115,8 @@ module Guard
60
115
  options[:success] || DEFAULTS[:success]
61
116
  when 'failed'
62
117
  options[:failed] || DEFAULTS[:failed]
118
+ when 'pending'
119
+ options[:pending] || DEFAULTS[:pending]
63
120
  else
64
121
  options[:default] || DEFAULTS[:default]
65
122
  end
data/lib/guard/runner.rb CHANGED
@@ -7,6 +7,8 @@ module Guard
7
7
  require 'guard'
8
8
  require 'guard/ui'
9
9
  require 'guard/watcher'
10
+
11
+ require 'lumberjack'
10
12
 
11
13
  # Deprecation message for the `run_on_change` method
12
14
  RUN_ON_CHANGE_DEPRECATION = <<-EOS.gsub(/^\s*/, '')
@@ -47,8 +49,10 @@ module Guard
47
49
  # @see self.run_supervised_task
48
50
  #
49
51
  def run(task, scopes = {})
50
- scoped_guards(scopes) do |guard|
51
- run_supervised_task(guard, task)
52
+ Lumberjack.unit_of_work do
53
+ scoped_guards(scopes) do |guard|
54
+ run_supervised_task(guard, task)
55
+ end
52
56
  end
53
57
  end
54
58
 
data/lib/guard/ui.rb CHANGED
@@ -1,26 +1,53 @@
1
+ require 'lumberjack'
2
+
1
3
  module Guard
2
4
 
3
5
  # The UI class helps to format messages for the user. Everything that is logged
4
6
  # through this class is considered either as an error message or a diagnostic
5
- # message and is written to standard error (STDERR).
7
+ # message and is written to standard error ($stderr).
6
8
  #
7
9
  # If your Guard plugin does some output that is piped into another process for further
8
10
  # processing, please just write it to STDOUT with `puts`.
9
11
  #
10
12
  module UI
13
+
11
14
  class << self
12
15
 
13
- color_enabled = nil
16
+ # Get the Guard::UI logger instance
17
+ #
18
+ def logger
19
+ @logger ||= Lumberjack::Logger.new($stderr, self.options)
20
+ end
21
+
22
+ # Get the logger options
23
+ #
24
+ # @return [Hash] the logger options
25
+ #
26
+ def options
27
+ @options ||= { :level => :info, :template => ':time - :severity - :message', :time_format => '%H:%M:%S' }
28
+ end
29
+
30
+ # Set the logger options
31
+ #
32
+ # @param [Hash] options the logger options
33
+ # @option options [Symbol] level the log level
34
+ # @option options [String] template the logger template
35
+ # @option options [String] time_format the time format
36
+ #
37
+ def options=(options)
38
+ @options = options
39
+ end
14
40
 
15
41
  # Show an info message.
16
42
  #
17
43
  # @param [String] message the message to show
18
44
  # @option options [Boolean] reset whether to clean the output before
45
+ # @option options [String] plugin manually define the calling plugin
19
46
  #
20
47
  def info(message, options = { })
21
- unless ENV['GUARD_ENV'] == 'test'
48
+ filter(options[:plugin]) do |plugin|
22
49
  reset_line if options[:reset]
23
- STDERR.puts color(message) if message != ''
50
+ self.logger.info(message, plugin)
24
51
  end
25
52
  end
26
53
 
@@ -28,11 +55,12 @@ module Guard
28
55
  #
29
56
  # @param [String] message the message to show
30
57
  # @option options [Boolean] reset whether to clean the output before
58
+ # @option options [String] plugin manually define the calling plugin
31
59
  #
32
60
  def warning(message, options = { })
33
- unless ENV['GUARD_ENV'] == 'test'
61
+ filter(options[:plugin]) do |plugin|
34
62
  reset_line if options[:reset]
35
- STDERR.puts color('WARNING: ', :yellow) + message
63
+ self.logger.warn(color(message, :yellow), plugin)
36
64
  end
37
65
  end
38
66
 
@@ -40,23 +68,26 @@ module Guard
40
68
  #
41
69
  # @param [String] message the message to show
42
70
  # @option options [Boolean] reset whether to clean the output before
71
+ # @option options [String] plugin manually define the calling plugin
43
72
  #
44
73
  def error(message, options = { })
45
- unless ENV['GUARD_ENV'] == 'test'
74
+ filter(options[:plugin]) do |plugin|
46
75
  reset_line if options[:reset]
47
- STDERR.puts color('ERROR: ', :red) + message
76
+ self.logger.error(color(message, :red), plugin)
48
77
  end
49
78
  end
50
79
 
51
80
  # Show a red deprecation message that is prefixed with DEPRECATION.
81
+ # It has a log level of `warn`.
52
82
  #
53
83
  # @param [String] message the message to show
54
84
  # @option options [Boolean] reset whether to clean the output before
85
+ # @option options [String] plugin manually define the calling plugin
55
86
  #
56
87
  def deprecation(message, options = { })
57
- unless ENV['GUARD_ENV'] == 'test'
88
+ filter(options[:plugin]) do |plugin|
58
89
  reset_line if options[:reset]
59
- STDERR.puts color('DEPRECATION: ', :red) + message
90
+ self.logger.warn(color(message, :yellow), plugin)
60
91
  end
61
92
  end
62
93
 
@@ -64,18 +95,19 @@ module Guard
64
95
  #
65
96
  # @param [String] message the message to show
66
97
  # @option options [Boolean] reset whether to clean the output before
98
+ # @option options [String] plugin manually define the calling plugin
67
99
  #
68
100
  def debug(message, options = { })
69
- unless ENV['GUARD_ENV'] == 'test'
101
+ filter(options[:plugin]) do |plugin|
70
102
  reset_line if options[:reset]
71
- STDERR.puts color("DEBUG (#{Time.now.strftime('%T')}): ", :yellow) + message if ::Guard.options && ::Guard.options[:debug]
103
+ self.logger.debug(color(message, :yellow), plugin)
72
104
  end
73
105
  end
74
106
 
75
107
  # Reset a line.
76
108
  #
77
109
  def reset_line
78
- STDERR.print(color_enabled? ? "\r\e[0m" : "\r\n")
110
+ $stderr.print(color_enabled? ? "\r\e[0m" : "\r\n")
79
111
  end
80
112
 
81
113
  # Clear the output if clearable.
@@ -107,6 +139,34 @@ module Guard
107
139
  end
108
140
 
109
141
  private
142
+
143
+ # Filters log messages depending on either the
144
+ # `:only`` or `:except` option.
145
+ #
146
+ # @param [String] plugin the calling plugin name
147
+ # @yield When the message should be logged
148
+ # @yieldparam [String] param the calling plugin name
149
+ #
150
+ def filter(plugin)
151
+ only = self.options[:only]
152
+ except = self.options[:except]
153
+ plugin = plugin || calling_plugin_name
154
+
155
+ if (!only && !except) || (only && only.match(plugin)) || (except && !except.match(plugin))
156
+ yield plugin
157
+ end
158
+ end
159
+
160
+ # Tries to extract the calling Guard plugin name
161
+ # from the call stack.
162
+ #
163
+ # @param [Integer] depth the stack depth
164
+ # @return [String] the Guard plugin name
165
+ #
166
+ def calling_plugin_name(depth = 2)
167
+ name = /(guard\/[a-z_]*)(\/[a-z_]*)?.rb:/i.match(caller[depth])
168
+ name ? name[1].split('/').map { |part| part.split(/[^a-z0-9]/i).map { |word| word.capitalize }.join }.join('::') : 'Guard'
169
+ end
110
170
 
111
171
  # Reset a color sequence.
112
172
  #
@@ -161,7 +221,7 @@ module Guard
161
221
  color_options.each do |color_option|
162
222
  color_option = color_option.to_s
163
223
  if color_option != ''
164
- if !(color_option =~ /\d+/)
224
+ unless color_option =~ /\d+/
165
225
  color_option = const_get("ANSI_ESCAPE_#{ color_option.upcase }")
166
226
  end
167
227
  color_code += ';' + color_option