guard 1.8.3 → 2.0.0.pre

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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +68 -10
  3. data/README.md +54 -33
  4. data/lib/guard.rb +133 -483
  5. data/lib/guard/cli.rb +78 -82
  6. data/lib/guard/commander.rb +121 -0
  7. data/lib/guard/commands/all.rb +1 -1
  8. data/lib/guard/commands/reload.rb +1 -1
  9. data/lib/guard/deprecated_methods.rb +59 -0
  10. data/lib/guard/deprecator.rb +107 -0
  11. data/lib/guard/dsl.rb +143 -329
  12. data/lib/guard/dsl_describer.rb +101 -57
  13. data/lib/guard/group.rb +27 -8
  14. data/lib/guard/guard.rb +25 -150
  15. data/lib/guard/guardfile.rb +35 -85
  16. data/lib/guard/guardfile/evaluator.rb +245 -0
  17. data/lib/guard/guardfile/generator.rb +89 -0
  18. data/lib/guard/interactor.rb +147 -163
  19. data/lib/guard/notifier.rb +83 -137
  20. data/lib/guard/notifiers/base.rb +220 -0
  21. data/lib/guard/notifiers/emacs.rb +39 -37
  22. data/lib/guard/notifiers/file_notifier.rb +29 -25
  23. data/lib/guard/notifiers/gntp.rb +68 -75
  24. data/lib/guard/notifiers/growl.rb +49 -52
  25. data/lib/guard/notifiers/growl_notify.rb +51 -56
  26. data/lib/guard/notifiers/libnotify.rb +41 -48
  27. data/lib/guard/notifiers/notifysend.rb +58 -38
  28. data/lib/guard/notifiers/rb_notifu.rb +54 -54
  29. data/lib/guard/notifiers/terminal_notifier.rb +48 -36
  30. data/lib/guard/notifiers/terminal_title.rb +23 -19
  31. data/lib/guard/notifiers/tmux.rb +110 -93
  32. data/lib/guard/options.rb +21 -0
  33. data/lib/guard/plugin.rb +66 -0
  34. data/lib/guard/plugin/base.rb +178 -0
  35. data/lib/guard/plugin/hooker.rb +123 -0
  36. data/lib/guard/plugin_util.rb +158 -0
  37. data/lib/guard/rake_task.rb +47 -0
  38. data/lib/guard/runner.rb +62 -82
  39. data/lib/guard/setuper.rb +248 -0
  40. data/lib/guard/ui.rb +24 -80
  41. data/lib/guard/ui/colors.rb +60 -0
  42. data/lib/guard/version.rb +1 -2
  43. data/lib/guard/watcher.rb +30 -30
  44. data/man/guard.1 +4 -4
  45. data/man/guard.1.html +6 -4
  46. metadata +25 -11
  47. data/lib/guard/hook.rb +0 -120
@@ -2,9 +2,23 @@ require 'yaml'
2
2
  require 'rbconfig'
3
3
  require 'pathname'
4
4
 
5
+ require 'guard/ui'
6
+ require 'guard/notifiers/emacs'
7
+ require 'guard/notifiers/file_notifier'
8
+ require 'guard/notifiers/gntp'
9
+ require 'guard/notifiers/growl_notify'
10
+ require 'guard/notifiers/growl'
11
+ require 'guard/notifiers/libnotify'
12
+ require 'guard/notifiers/notifysend'
13
+ require 'guard/notifiers/rb_notifu'
14
+ require 'guard/notifiers/terminal_notifier'
15
+ require 'guard/notifiers/terminal_title'
16
+ require 'guard/notifiers/tmux'
17
+
5
18
  module Guard
6
19
 
7
- # The notifier handles sending messages to different notifiers. Currently the following
20
+ # The notifier handles sending messages to different notifiers. Currently the
21
+ # following
8
22
  # libraries are supported:
9
23
  #
10
24
  # * Ruby GNTP
@@ -17,7 +31,8 @@ module Guard
17
31
  # * Terminal Title
18
32
  # * Tmux
19
33
  #
20
- # Please see the documentation of each notifier for more information about the requirements
34
+ # Please see the documentation of each notifier for more information about
35
+ # the requirements
21
36
  # and configuration possibilities.
22
37
  #
23
38
  # Guard knows four different notification types:
@@ -33,80 +48,59 @@ module Guard
33
48
  # `gntp` and `growl_notify` notifiers are able to register these types
34
49
  # at Growl and allows customization of each notification type.
35
50
  #
36
- # Guard can be configured to make use of more than one notifier at once, @see Guard::Dsl
51
+ # Guard can be configured to make use of more than one notifier at once.
52
+ #
53
+ # @see Guard::Dsl
37
54
  #
38
55
  module Notifier
39
56
 
40
- require 'guard'
41
- require 'guard/ui'
42
- require 'guard/notifiers/gntp'
43
- require 'guard/notifiers/growl'
44
- require 'guard/notifiers/growl_notify'
45
- require 'guard/notifiers/libnotify'
46
- require 'guard/notifiers/notifysend'
47
- require 'guard/notifiers/rb_notifu'
48
- require 'guard/notifiers/emacs'
49
- require 'guard/notifiers/terminal_notifier'
50
- require 'guard/notifiers/terminal_title'
51
- require 'guard/notifiers/tmux'
52
- require 'guard/notifiers/file_notifier'
53
-
54
57
  extend self
55
58
 
56
- # List of available notifiers, grouped by functionality. It needs to be a nested hash instead of
57
- # a simpler Hash, because it maintains its order on Ruby 1.8.7 also.
59
+ # List of available notifiers, grouped by functionality
58
60
  NOTIFIERS = [
59
- [
60
- [:gntp, ::Guard::Notifier::GNTP],
61
- [:growl, ::Guard::Notifier::Growl],
62
- [:growl_notify, ::Guard::Notifier::GrowlNotify],
63
- [:terminal_notifier, ::Guard::Notifier::TerminalNotifier],
64
- [:libnotify, ::Guard::Notifier::Libnotify],
65
- [:notifysend, ::Guard::Notifier::NotifySend],
66
- [:notifu, ::Guard::Notifier::Notifu]
67
- ],
68
- [[:emacs, ::Guard::Notifier::Emacs]],
69
- [[:tmux, ::Guard::Notifier::Tmux]],
70
- [[:terminal_title, ::Guard::Notifier::TerminalTitle]],
71
- [[:file, ::Guard::Notifier::FileNotifier]]
61
+ {
62
+ gntp: GNTP,
63
+ growl: Growl,
64
+ growl_notify: GrowlNotify,
65
+ terminal_notifier: TerminalNotifier,
66
+ libnotify: Libnotify,
67
+ notifysend: NotifySend,
68
+ notifu: Notifu
69
+ },
70
+ { emacs: Emacs },
71
+ { tmux: Tmux },
72
+ { terminal_title: TerminalTitle },
73
+ { file: FileNotifier }
72
74
  ]
73
75
 
74
- # Get the available notifications.
75
- #
76
- # @return [Hash] the notifications
77
- #
78
- def notifications
79
- ENV['GUARD_NOTIFICATIONS'] ? YAML::load(ENV['GUARD_NOTIFICATIONS']) : []
76
+ def notifiers
77
+ ENV['GUARD_NOTIFIERS'] ? YAML::load(ENV['GUARD_NOTIFIERS']) : []
80
78
  end
81
79
 
82
- # Set the available notifications.
83
- #
84
- # @param [Array<Hash>] notifications the notifications
85
- #
86
- def notifications=(notifications)
87
- ENV['GUARD_NOTIFICATIONS'] = YAML::dump(notifications)
80
+ def notifiers=(notifiers)
81
+ ENV['GUARD_NOTIFIERS'] = YAML::dump(notifiers)
88
82
  end
89
83
 
90
84
  # Clear available notifications.
91
85
  #
92
- def clear_notifications
93
- ENV['GUARD_NOTIFICATIONS'] = nil
86
+ def clear_notifiers
87
+ ENV['GUARD_NOTIFIERS'] = nil
94
88
  end
95
89
 
96
- # Turn notifications on. If no notifications are defined
97
- # in the `Guardfile` Guard auto detects the first available
98
- # library.
90
+ # Turn notifications on. If no notifications are defined in the `Guardfile`
91
+ # Guard auto detects the first available library.
99
92
  #
100
93
  def turn_on
101
- auto_detect_notification if notifications.empty? && (!::Guard.options || ::Guard.options[:notify])
94
+ _auto_detect_notification if notifiers.empty? && (!::Guard.options || ::Guard.options.notify)
102
95
 
103
- if notifications.empty?
104
- ENV['GUARD_NOTIFY'] = 'false'
96
+ if notifiers.empty?
97
+ turn_off
105
98
  else
106
- notifications.each do |notification|
107
- ::Guard::UI.info "Guard is using #{ get_notifier_module(notification[:name]).to_s.split('::').last } to send notifications."
108
- notifier = get_notifier_module(notification[:name])
109
- notifier.turn_on(notification[:options]) if notifier.respond_to?(:turn_on)
99
+ notifiers.each do |notifier|
100
+ notifier_class = _get_notifier_module(notifier[:name])
101
+ ::Guard::UI.info "Guard is using #{ notifier_class.title } to send notifications."
102
+
103
+ notifier_class.turn_on if notifier_class.respond_to?(:turn_on)
110
104
  end
111
105
 
112
106
  ENV['GUARD_NOTIFY'] = 'true'
@@ -116,9 +110,10 @@ module Guard
116
110
  # Turn notifications off.
117
111
  #
118
112
  def turn_off
119
- notifications.each do |notification|
120
- notifier = get_notifier_module(notification[:name])
121
- notifier.turn_off(notification[:options]) if notifier.respond_to?(:turn_off)
113
+ notifiers.each do |notifier|
114
+ notifier_class = _get_notifier_module(notifier[:name])
115
+
116
+ notifier_class.turn_off if notifier_class.respond_to?(:turn_off)
122
117
  end
123
118
 
124
119
  ENV['GUARD_NOTIFY'] = 'false'
@@ -127,7 +122,7 @@ module Guard
127
122
  # Toggle the system notifications on/off
128
123
  #
129
124
  def toggle
130
- if ENV['GUARD_NOTIFY'] == 'true'
125
+ if enabled?
131
126
  ::Guard::UI.info 'Turn off notifications'
132
127
  turn_off
133
128
  else
@@ -146,17 +141,17 @@ module Guard
146
141
  # Add a notification library to be used.
147
142
  #
148
143
  # @param [Symbol] name the name of the notifier to use
149
- # @param [Boolean] silent disable any error message
150
144
  # @param [Hash] options the notifier options
145
+ # @option options [String] silent disable any error message
151
146
  # @return [Boolean] if the notification could be added
152
147
  #
153
- def add_notification(name, options = { }, silent = false)
148
+ def add_notifier(name, opts = {})
154
149
  return turn_off if name == :off
155
150
 
156
- notifier = get_notifier_module(name)
151
+ notifier_class = _get_notifier_module(name)
157
152
 
158
- if notifier && notifier.available?(silent, options)
159
- self.notifications = notifications << { :name => name, :options => options }
153
+ if notifier_class && notifier_class.available?(opts)
154
+ self.notifiers = notifiers << { name: name, options: opts }
160
155
  true
161
156
  else
162
157
  false
@@ -166,21 +161,19 @@ module Guard
166
161
  # Show a system notification with all configured notifiers.
167
162
  #
168
163
  # @param [String] message the message to show
169
- # @option options [Symbol, String] image the image symbol or path to an image
170
- # @option options [String] title the notification title
164
+ # @option opts [Symbol, String] image the image symbol or path to an image
165
+ # @option opts [String] title the notification title
171
166
  #
172
- def notify(message, options = { })
173
- if enabled?
174
- type = notification_type(options[:image] || :success)
175
- image = image_path(options.delete(:image) || :success)
176
- title = options.delete(:title) || 'Guard'
167
+ def notify(message, opts = {})
168
+ return unless enabled?
169
+
170
+ notifiers.each do |notifier|
171
+ notifier = _get_notifier_module(notifier[:name]).new(notifier[:options])
177
172
 
178
- notifications.each do |notification|
179
- begin
180
- get_notifier_module(notification[:name]).notify(type, title, message, image, options.merge(notification[:options]))
181
- rescue Exception => e
182
- ::Guard::UI.error "Error sending notification with #{ notification[:name] }: #{ e.message }"
183
- end
173
+ begin
174
+ notifier.notify(message, opts)
175
+ rescue Exception => e
176
+ ::Guard::UI.error "Error sending notification with #{ notifier.name }: #{ e.message }"
184
177
  end
185
178
  end
186
179
  end
@@ -192,78 +185,31 @@ module Guard
192
185
  # @param [Symbol] name the notifier name
193
186
  # @return [Module] the notifier module
194
187
  #
195
- def get_notifier_module(name)
196
- notifier = NOTIFIERS.flatten(1).detect { |n| n.first == name }
197
- notifier ? notifier.last : notifier
188
+ def _get_notifier_module(name)
189
+ NOTIFIERS.each do |group|
190
+ if notifier = group.find { |n, _| n == name }
191
+ return notifier.last
192
+ end
193
+ end
194
+
195
+ nil
198
196
  end
199
197
 
200
198
  # Auto detect the available notification library. This goes through
201
199
  # the list of supported notification gems and picks the first that
202
200
  # is available in each notification group.
203
201
  #
204
- def auto_detect_notification
202
+ def _auto_detect_notification
203
+ self.notifiers = []
205
204
  available = nil
206
- self.notifications = []
207
205
 
208
206
  NOTIFIERS.each do |group|
209
- added = group.map { |n| n.first }.find { |notifier| add_notification(notifier, { }, true) }
210
- available = available || added
207
+ notifier_added = group.find { |name, klass| add_notifier(name, silent: true) }
208
+ available ||= notifier_added
211
209
  end
212
210
 
213
211
  ::Guard::UI.info('Guard could not detect any of the supported notification libraries.') unless available
214
212
  end
215
-
216
- # Get the image path for an image symbol for the following
217
- # known image types:
218
- #
219
- # - failed
220
- # - pending
221
- # - success
222
- #
223
- # If the image is not a known symbol, it will be returned unmodified.
224
- #
225
- # @param [Symbol, String] image the image symbol or path to an image
226
- # @return [String] the image path
227
- #
228
- def image_path(image)
229
- case image
230
- when :failed
231
- images_path.join('failed.png').to_s
232
- when :pending
233
- images_path.join('pending.png').to_s
234
- when :success
235
- images_path.join('success.png').to_s
236
- else
237
- image
238
- end
239
- end
240
-
241
- # Paths where all Guard images are located
242
- #
243
- # @return [Pathname] the path to the images directory
244
- #
245
- def images_path
246
- @images_path ||= Pathname.new(File.dirname(__FILE__)).join('../../images')
247
- end
248
-
249
- # Get the notification type depending on the
250
- # image that has been selected for the notification.
251
- #
252
- # @param [Symbol, String] image the image symbol or path to an image
253
- # @return [String] the notification type
254
- #
255
- def notification_type(image)
256
- case image
257
- when :failed
258
- 'failed'
259
- when :pending
260
- 'pending'
261
- when :success
262
- 'success'
263
- else
264
- 'notify'
265
- end
266
- end
267
213
  end
268
214
 
269
215
  end
@@ -0,0 +1,220 @@
1
+ require 'rbconfig'
2
+ require 'guard/ui'
3
+
4
+ module Guard
5
+ module Notifier
6
+
7
+ # Base class for all notifiers.
8
+ #
9
+ class Base
10
+
11
+ HOSTS = {
12
+ darwin: 'Mac OS X',
13
+ linux: 'Linux',
14
+ freebsd: 'FreeBSD',
15
+ openbsd: 'OpenBSD',
16
+ sunos: 'SunOS',
17
+ solaris: 'Solaris',
18
+ mswin: 'Windows',
19
+ mingw: 'Windows',
20
+ cygwin: 'Windows'
21
+ }
22
+
23
+ attr_reader :options
24
+
25
+ def initialize(opts = {})
26
+ @options = opts
27
+ end
28
+
29
+ # This method should be overriden by subclasses and return an array of
30
+ # OSes the notifier supports. By default, it returns :all which mean
31
+ # there's no check against the current OS.
32
+ #
33
+ # @see HOSTS for the list of possible OSes
34
+ #
35
+ def self.supported_hosts
36
+ :all
37
+ end
38
+
39
+ # Test if the notifier can be used.
40
+ #
41
+ # @param [Hash] opts notifier options
42
+ # @option opts [Boolean] silent true if no error messages should be shown
43
+ # @return [Boolean] the availability status
44
+ #
45
+ def self.available?(opts = {})
46
+ options = { silent: false }.merge(opts)
47
+
48
+ unless _supported_host?
49
+ hosts = supported_hosts.map { |host| HOSTS[host.to_sym] }.join(', ')
50
+ ::Guard::UI.error "The :#{name} notifier runs only on #{hosts}." unless options[:silent]
51
+ return false
52
+ end
53
+
54
+ true
55
+ end
56
+
57
+ # This method must be implemented.
58
+ #
59
+ def notify(message, opts = {})
60
+ raise NotImplementedError
61
+ end
62
+
63
+ # Returns the title of the notifier.
64
+ #
65
+ # @example Un-modulize the class name
66
+ # Guard::Notifier::FileNotifier.title
67
+ # #=> 'FileNotifier'
68
+ #
69
+ # @return [String] the title of the notifier
70
+ #
71
+ def self.title
72
+ self.to_s.sub(/.+::(\w+)$/, '\1')
73
+ end
74
+
75
+ # Returns the name of the notifier.
76
+ #
77
+ # @example Un-modulize, underscorize and downcase the class name
78
+ # Guard::Notifier::FileNotifier.name
79
+ # #=> 'file_notifier'
80
+ #
81
+ # @return [String] the name of the notifier
82
+ #
83
+ def self.name
84
+ title.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
85
+ end
86
+
87
+ # Returns the name of the notifier's gem. By default it returns the
88
+ # notifier name. This method can be overriden by subclasses.
89
+ #
90
+ # @example Un-modulize, underscorize and downcase the class name
91
+ # Guard::Notifier::FileNotifier.gem_name
92
+ # #=> 'file_notifier'
93
+ #
94
+ # @return [String] the name of the notifier's gem
95
+ #
96
+ def self.gem_name
97
+ name
98
+ end
99
+
100
+ # This method tries to require the gem whose name is returned by
101
+ # `.gem_name`. If a LoadError or NameError occurs, it displays an error
102
+ # message (unless opts[:silent] is true) and returns false.
103
+ #
104
+ # @param [Hash] opts some options
105
+ # @option opts [Boolean] silent true if no error messages should be shown
106
+ #
107
+ # @return [Boolean] whether or not the gem is loaded
108
+ #
109
+ def self.require_gem_safely(opts = {})
110
+ require gem_name
111
+ true
112
+ rescue LoadError, NameError
113
+ unless opts[:silent]
114
+ ::Guard::UI.error "Please add \"gem '#{gem_name}'\" to your Gemfile and run Guard with \"bundle exec\"."
115
+ end
116
+ false
117
+ end
118
+
119
+ # Returns the title of the notifier.
120
+ #
121
+ # @example Un-modulize the class name
122
+ # Guard::Notifier::FileNotifier.new.title
123
+ # #=> 'FileNotifier'
124
+ #
125
+ # @return [String] the title of the notifier
126
+ #
127
+ def title
128
+ self.class.title
129
+ end
130
+
131
+ # Returns the name of the notifier.
132
+ #
133
+ # @example Un-modulize, underscorize and downcase the class name
134
+ # Guard::Notifier::FileNotifier.new.name
135
+ # #=> 'file_notifier'
136
+ #
137
+ # @return [String] the name of the notifier
138
+ #
139
+ def name
140
+ self.class.name
141
+ end
142
+
143
+ # Paths where all Guard images are located
144
+ #
145
+ # @return [Pathname] the path to the images directory
146
+ #
147
+ def images_path
148
+ @images_path ||= Pathname.new(File.dirname(__FILE__)).join('../../../images')
149
+ end
150
+
151
+ # @private
152
+ #
153
+ # Checks if the current OS is supported by the notifier.
154
+ #
155
+ # @see .supported_hosts
156
+ #
157
+ def self._supported_host?
158
+ supported_hosts == :all ||
159
+ RbConfig::CONFIG['host_os'] =~ /#{supported_hosts.join('|')}/
160
+ end
161
+
162
+ # Set or modify the `:title`, `:type` and `:image` options for a
163
+ # notification. Should be used in `#notify`.
164
+ #
165
+ # @param [Hash] opts additional notification library options
166
+ # @option opts [String] type the notification type. Either 'success',
167
+ # 'pending', 'failed' or 'notify'
168
+ # @option opts [String] title the notification title
169
+ # @option opts [String] image the path to the notification image
170
+ #
171
+ def normalize_standard_options!(opts)
172
+ opts[:title] ||= 'Guard'
173
+ opts[:type] ||= _notification_type(opts.fetch(:image, :success))
174
+ opts[:image] = _image_path(opts.delete(:image) { :success })
175
+ end
176
+
177
+ private
178
+
179
+ # Get the image path for an image symbol for the following
180
+ # known image types:
181
+ #
182
+ # - failed
183
+ # - pending
184
+ # - success
185
+ #
186
+ # If the image is not a known symbol, it will be returned unmodified.
187
+ #
188
+ # @param [Symbol, String] image the image symbol or path to an image
189
+ #
190
+ # @return [String] the image path
191
+ #
192
+ def _image_path(image)
193
+ case image.to_sym
194
+ when :failed, :pending, :success
195
+ images_path.join("#{image.to_s}.png").to_s
196
+ else
197
+ image
198
+ end
199
+ end
200
+
201
+ # Get the notification type depending on the
202
+ # image that has been selected for the notification.
203
+ #
204
+ # @param [Symbol, String] image the image symbol or path to an image
205
+ #
206
+ # @return [String] the notification type
207
+ #
208
+ def _notification_type(image)
209
+ case image.to_sym
210
+ when :failed, :pending, :success
211
+ image.to_sym
212
+ else
213
+ :notify
214
+ end
215
+ end
216
+
217
+ end
218
+
219
+ end
220
+ end