notiffany 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,84 @@
1
+ require "notiffany/notifier/base"
2
+
3
+ require "shellany/sheller"
4
+
5
+ module Notiffany
6
+ class Notifier
7
+ # System notifications using notify-send, a binary that ships with
8
+ # the libnotify-bin package on many Debian-based distributions.
9
+ #
10
+ # @example Add the `:notifysend` notifier to your `Guardfile`
11
+ # notification :notifysend
12
+ #
13
+ class NotifySend < Base
14
+ # Default options for the notify-send notifications.
15
+ DEFAULTS = {
16
+ t: 3000, # Default timeout is 3000ms
17
+ h: "int:transient:1" # Automatically close the notification
18
+ }
19
+
20
+ # Full list of options supported by notify-send.
21
+ SUPPORTED = [:u, :t, :i, :c, :h]
22
+
23
+ private
24
+
25
+ def _supported_hosts
26
+ %w(linux freebsd openbsd sunos solaris)
27
+ end
28
+
29
+ def _check_available(_opts = {})
30
+ return true unless Shellany::Sheller.stdout("which notify-send").empty?
31
+
32
+ fail UnavailableError, "libnotify-bin package is not installed"
33
+ end
34
+
35
+ # Shows a system notification.
36
+ #
37
+ # @param [String] message the notification message body
38
+ # @param [Hash] opts additional notification library options
39
+ # @option opts [String] type the notification type. Either 'success',
40
+ # 'pending', 'failed' or 'notify'
41
+ # @option opts [String] title the notification title
42
+ # @option opts [String] image the path to the notification image
43
+ # @option opts [String] c the notification category
44
+ # @option opts [Number] t the number of milliseconds to display (1000,
45
+ # 3000)
46
+ #
47
+ def _perform_notify(message, opts = {})
48
+ command = [opts[:title], message]
49
+ opts = opts.merge(
50
+ i: opts[:i] || opts[:image],
51
+ u: opts[:u] || _notifysend_urgency(opts[:type])
52
+ )
53
+
54
+ Shellany::Sheller.
55
+ run("notify-send", *_to_arguments(command, SUPPORTED, opts))
56
+ end
57
+
58
+ # Converts Guards notification type to the best matching
59
+ # notify-send urgency.
60
+ #
61
+ # @param [String] type the Guard notification type
62
+ # @return [String] the notify-send urgency
63
+ #
64
+ def _notifysend_urgency(type)
65
+ { failed: "normal", pending: "low" }.fetch(type, "low")
66
+ end
67
+
68
+ # Builds a shell command out of a command string and option hash.
69
+ #
70
+ # @param [String] command the command execute
71
+ # @param [Array] supported list of supported option flags
72
+ # @param [Hash] opts additional command options
73
+ #
74
+ # @return [Array<String>] the command and its options converted to a
75
+ # shell command.
76
+ #
77
+ def _to_arguments(command, supported, opts = {})
78
+ opts.inject(command) do |cmd, (flag, value)|
79
+ supported.include?(flag) ? (cmd << "-#{ flag }" << value.to_s) : cmd
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,89 @@
1
+ require "notiffany/notifier/base"
2
+
3
+ module Notiffany
4
+ class Notifier
5
+ # System notifications using the
6
+ # [rb-notifu](https://github.com/stereobooster/rb-notifu) gem.
7
+ #
8
+ # This gem is available for Windows and sends system notifications to
9
+ # [Notifu](http://www.paralint.com/projects/notifu/index.html):
10
+ #
11
+ # @example Add the `rb-notifu` gem to your `Gemfile`
12
+ # group :development
13
+ # gem 'rb-notifu'
14
+ # end
15
+ #
16
+ class Notifu < Base
17
+ # Default options for the rb-notifu notifications.
18
+ DEFAULTS = {
19
+ time: 3,
20
+ icon: false,
21
+ baloon: false,
22
+ nosound: false,
23
+ noquiet: false,
24
+ xp: false
25
+ }
26
+
27
+ private
28
+
29
+ def _supported_hosts
30
+ %w(mswin mingw)
31
+ end
32
+
33
+ def _gem_name
34
+ "rb-notifu"
35
+ end
36
+
37
+ def _check_available(_opts = {})
38
+ end
39
+
40
+ # Shows a system notification.
41
+ #
42
+ # @param [String] message the notification message body
43
+ # @param [Hash] opts additional notification library options
44
+ # @option opts [String] type the notification type. Either 'success',
45
+ # 'pending', 'failed' or 'notify'
46
+ # @option opts [String] title the notification title
47
+ # @option opts [String] image the path to the notification image
48
+ # @option opts [Number] time the number of seconds to display (0 for
49
+ # infinit)
50
+ # @option opts [Boolean] icon specify an icon to use ("parent" uses the
51
+ # icon of the parent process)
52
+ # @option opts [Boolean] baloon enable ballon tips in the registry (for
53
+ # this user only)
54
+ # @option opts [Boolean] nosound do not play a sound when the tooltip is
55
+ # displayed
56
+ # @option opts [Boolean] noquiet show the tooltip even if the user is in
57
+ # the quiet period that follows his very first login (Windows 7 and up)
58
+ # @option opts [Boolean] xp use IUserNotification interface event when
59
+ # IUserNotification2 is available
60
+ #
61
+ def _perform_notify(message, opts = {})
62
+ options = opts.dup
63
+ options[:type] = _notifu_type(opts[:type])
64
+ options[:message] = message
65
+
66
+ # The empty block is needed until
67
+ # https://github.com/stereobooster/rb-notifu/pull/1 is merged
68
+ ::Notifu.show(options) {}
69
+ end
70
+
71
+ # Converts generic notification type to the best matching
72
+ # Notifu type.
73
+ #
74
+ # @param [String] type the generic notification type
75
+ # @return [Symbol] the Notify notification type
76
+ #
77
+ def _notifu_type(type)
78
+ case type.to_sym
79
+ when :failed
80
+ :error
81
+ when :pending
82
+ :warn
83
+ else
84
+ :info
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,59 @@
1
+ require "notiffany/notifier/base"
2
+
3
+ module Notiffany
4
+ class Notifier
5
+ # System notifications using the
6
+ #
7
+ # [terminal-notifier](https://github.com/Springest/terminal-notifier-guard)
8
+ #
9
+ # gem.
10
+ #
11
+ # This gem is available for OS X 10.8 Mountain Lion and sends notifications
12
+ # to the OS X notification center.
13
+ class TerminalNotifier < Base
14
+ DEFAULTS = { app_name: "Notiffany" }
15
+
16
+ ERROR_ONLY_OSX10 = "The :terminal_notifier only runs"\
17
+ " on Mac OS X 10.8 and later."
18
+
19
+ def _supported_hosts
20
+ %w(darwin)
21
+ end
22
+
23
+ def _gem_name
24
+ "terminal-notifier-guard"
25
+ end
26
+
27
+ def _check_available(_opts = {})
28
+ return if ::TerminalNotifier::Guard.available?
29
+ fail UnavailableError, ERROR_ONLY_OSX10
30
+ end
31
+
32
+ # Shows a system notification.
33
+ #
34
+ # @param [String] message the notification message body
35
+ # @param [Hash] opts additional notification library options
36
+ # @option opts [String] type the notification type. Either 'success',
37
+ # 'pending', 'failed' or 'notify'
38
+ # @option opts [String] title the notification title
39
+ # @option opts [String] image the path to the notification image (ignored)
40
+ # @option opts [String] app_name name of your app
41
+ # @option opts [String] execute a command
42
+ # @option opts [String] activate an app bundle
43
+ # @option opts [String] open some url or file
44
+ #
45
+ def _perform_notify(message, opts = {})
46
+ title = [opts[:app_name], opts[:type].downcase.capitalize].join(" ")
47
+ opts = {
48
+ title: title
49
+ }.merge(opts)
50
+ opts[:message] = message
51
+ opts[:title] ||= title
52
+ opts.delete(:image)
53
+ opts.delete(:app_name)
54
+
55
+ ::TerminalNotifier::Guard.execute(false, opts)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,39 @@
1
+ require "notiffany/notifier/base"
2
+
3
+ module Notiffany
4
+ class Notifier
5
+ # Shows system notifications in the terminal title bar.
6
+ #
7
+ class TerminalTitle < Base
8
+ DEFAULTS = {}
9
+
10
+ # Clears the terminal title
11
+ def turn_off
12
+ STDOUT.puts "\e]2;\a"
13
+ end
14
+
15
+ private
16
+
17
+ def _gem_name
18
+ nil
19
+ end
20
+
21
+ def _check_available(_options)
22
+ end
23
+
24
+ # Shows a system notification.
25
+ #
26
+ # @param [Hash] opts additional notification library options
27
+ # @option opts [String] message the notification message body
28
+ # @option opts [String] type the notification type. Either 'success',
29
+ # 'pending', 'failed' or 'notify'
30
+ # @option opts [String] title the notification title
31
+ #
32
+ def _perform_notify(message, opts = {})
33
+ first_line = message.sub(/^\n/, "").sub(/\n.*/m, "")
34
+
35
+ STDOUT.puts "\e]2;[#{ opts[:title] }] #{ first_line }\a"
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,355 @@
1
+ require "notiffany/notifier/base"
2
+ require "shellany/sheller"
3
+
4
+ # TODO: this probably deserves a gem of it's own
5
+ module Notiffany
6
+ class Notifier
7
+ # Changes the color of the Tmux status bar and optionally
8
+ # shows messages in the status bar.
9
+ class Tmux < Base
10
+ @session = nil
11
+
12
+ DEFAULTS = {
13
+ tmux_environment: "TMUX",
14
+ success: "green",
15
+ failed: "red",
16
+ pending: "yellow",
17
+ default: "green",
18
+ timeout: 5,
19
+ display_message: false,
20
+ default_message_format: "%s - %s",
21
+ default_message_color: "white",
22
+ display_on_all_clients: false,
23
+ display_title: false,
24
+ default_title_format: "%s - %s",
25
+ line_separator: " - ",
26
+ change_color: true,
27
+ color_location: "status-left-bg"
28
+ }
29
+
30
+ class Client
31
+ CLIENT = "tmux"
32
+
33
+ class << self
34
+ def version
35
+ Float(_capture("-V")[/\d+\.\d+/])
36
+ end
37
+
38
+ def _capture(*args)
39
+ Shellany::Sheller.stdout(([CLIENT] + args).join(" "))
40
+ end
41
+
42
+ def _run(*args)
43
+ Shellany::Sheller.run(([CLIENT] + args).join(" "))
44
+ end
45
+ end
46
+
47
+ def initialize(client)
48
+ @client = client
49
+ end
50
+
51
+ def clients
52
+ return [@client] unless @client == :all
53
+ ttys = _capture("list-clients", "-F", "'\#{client_tty}'")
54
+ ttys = ttys.split(/\n/)
55
+
56
+ # if user is running 'tmux -C' remove this client from list
57
+ ttys.delete("(null)")
58
+ ttys
59
+ end
60
+
61
+ def set(key, value)
62
+ clients.each do |client|
63
+ args = client ? ["-t", client.strip] : nil
64
+ _run("set", "-q", *args, key, value)
65
+ end
66
+ end
67
+
68
+ def display_message(message)
69
+ clients.each do |client|
70
+ args += ["-c", client.strip] if client
71
+ # TODO: should properly escape message here
72
+ _run("display", *args, "'#{message}'")
73
+ end
74
+ end
75
+
76
+ def unset(key, value)
77
+ clients.each do |client|
78
+ args = client ? ["-t", client.strip] : []
79
+ if value
80
+ _run("set", "-q", *args, key, value)
81
+ else
82
+ _run("set", "-q", "-u", *args, key)
83
+ end
84
+ end
85
+ end
86
+
87
+ def parse_options
88
+ output = _capture("show", "-t", @client)
89
+ Hash[output.lines.map { |line| _parse_option(line) }]
90
+ end
91
+
92
+ def message_fg=(color)
93
+ set("message-fg", color)
94
+ end
95
+
96
+ def message_bg=(color)
97
+ set("message-bg", color)
98
+ end
99
+
100
+ def display_time=(time)
101
+ set("display-time", time)
102
+ end
103
+
104
+ def title=(string)
105
+ # TODO: properly escape?
106
+ set("set-titles-string", "'#{string}'")
107
+ end
108
+
109
+ private
110
+
111
+ def _run(*args)
112
+ self.class._run(*args)
113
+ end
114
+
115
+ def _capture(*args)
116
+ self.class._capture(*args)
117
+ end
118
+
119
+ def _parse_option(line)
120
+ line.partition(" ").map(&:strip).reject(&:empty?)
121
+ end
122
+ end
123
+
124
+ class Session
125
+ def initialize
126
+ @options_store = {}
127
+
128
+ # NOTE: we are reading the settings of all clients
129
+ # - regardless of the :display_on_all_clients option
130
+
131
+ # Ideally, this should be done incrementally (e.g. if we start with
132
+ # "current" client and then override the :display_on_all_clients to
133
+ # true, only then the option store should be updated to contain
134
+ # settings of all clients
135
+ Client.new(:all).clients.each do |client|
136
+ @options_store[client] = {
137
+ "status-left-bg" => nil,
138
+ "status-right-bg" => nil,
139
+ "status-left-fg" => nil,
140
+ "status-right-fg" => nil,
141
+ "message-bg" => nil,
142
+ "message-fg" => nil,
143
+ "display-time" => nil
144
+ }.merge(Client.new(client).parse_options)
145
+ end
146
+ end
147
+
148
+ def close
149
+ @options_store.each do |client, options|
150
+ options.each do |key, value|
151
+ Client.new(client).unset(key, value)
152
+ end
153
+ end
154
+ @options_store = nil
155
+ end
156
+ end
157
+
158
+ class Error < RuntimeError
159
+ end
160
+
161
+ ERROR_NOT_INSIDE_TMUX = ":tmux notifier is only available inside a "\
162
+ "TMux session."
163
+
164
+ ERROR_ANCIENT_TMUX = "Your tmux version is way too old (%s)!"
165
+
166
+ # Notification starting, save the current Tmux settings
167
+ # and quiet the Tmux output.
168
+ #
169
+ def turn_on
170
+ self.class._start_session
171
+ end
172
+
173
+ # Notification stopping. Restore the previous Tmux state
174
+ # if available (existing options are restored, new options
175
+ # are unset) and unquiet the Tmux output.
176
+ #
177
+ def turn_off
178
+ self.class._end_session
179
+ end
180
+
181
+ private
182
+
183
+ def _gem_name
184
+ nil
185
+ end
186
+
187
+ def _check_available(opts = {})
188
+ @session ||= nil # to avoid unitialized error
189
+ fail "PREVIOUS TMUX SESSION NOT CLEARED!" if @session
190
+
191
+ var_name = opts[:tmux_environment]
192
+ fail Error, ERROR_NOT_INSIDE_TMUX unless ENV.key?(var_name)
193
+
194
+ version = Client.version
195
+ fail Error, format(ERROR_ANCIENT_TMUX, version) if version < 1.7
196
+
197
+ true
198
+ rescue Error => e
199
+ fail UnavailableError, e.message
200
+ end
201
+
202
+ # Shows a system notification.
203
+ #
204
+ # By default, the Tmux notifier only makes
205
+ # use of a color based notification, changing the background color of the
206
+ # `color_location` to the color defined in either the `success`,
207
+ # `failed`, `pending` or `default`, depending on the notification type.
208
+ #
209
+ # You may enable an extra explicit message by setting `display_message`
210
+ # to true, and may further disable the colorization by setting
211
+ # `change_color` to false.
212
+ #
213
+ # @param [String] message the notification message
214
+ # @param [Hash] options additional notification library options
215
+ # @option options [String] type the notification type. Either 'success',
216
+ # 'pending', 'failed' or 'notify'
217
+ # @option options [String] message the notification message body
218
+ # @option options [String] image the path to the notification image
219
+ # @option options [Boolean] change_color whether to show a color
220
+ # notification
221
+ # @option options [String,Array] color_location the location where to draw
222
+ # the color notification
223
+ # @option options [Boolean] display_message whether to display a message
224
+ # or not
225
+ # @option options [Boolean] display_on_all_clients whether to display a
226
+ # message on all tmux clients or not
227
+ #
228
+ def _perform_notify(message, options = {})
229
+ change_color = options[:change_color]
230
+ locations = Array(options[:color_location])
231
+ display_the_title = options[:display_title]
232
+ display_message = options[:display_message]
233
+ type = options[:type].to_s
234
+ title = options[:title]
235
+
236
+ if change_color
237
+ color = _tmux_color(type, options)
238
+ locations.each do |location|
239
+ Client.new(client(options)).set(location, color)
240
+ end
241
+ end
242
+
243
+ _display_title(type, title, message, options) if display_the_title
244
+
245
+ return unless display_message
246
+ _display_message(type, title, message, options)
247
+ end
248
+
249
+ # Displays a message in the title bar of the terminal.
250
+ #
251
+ # @param [String] title the notification title
252
+ # @param [String] message the notification message body
253
+ # @param [Hash] options additional notification library options
254
+ # @option options [String] success_message_format a string to use as
255
+ # formatter for the success message.
256
+ # @option options [String] failed_message_format a string to use as
257
+ # formatter for the failed message.
258
+ # @option options [String] pending_message_format a string to use as
259
+ # formatter for the pending message.
260
+ # @option options [String] default_message_format a string to use as
261
+ # formatter when no format per type is defined.
262
+ #
263
+ def _display_title(type, title, message, options = {})
264
+ format = "#{type}_title_format".to_sym
265
+ default_title_format = options[:default_title_format]
266
+ title_format = options.fetch(format, default_title_format)
267
+ teaser_message = message.split("\n").first
268
+ display_title = title_format % [title, teaser_message]
269
+
270
+ Client.new(client(options)).title = display_title
271
+ end
272
+
273
+ # Displays a message in the status bar of tmux.
274
+ #
275
+ # @param [String] type the notification type. Either 'success',
276
+ # 'pending', 'failed' or 'notify'
277
+ # @param [String] title the notification title
278
+ # @param [String] message the notification message body
279
+ # @param [Hash] options additional notification library options
280
+ # @option options [Integer] timeout the amount of seconds to show the
281
+ # message in the status bar
282
+ # @option options [String] success_message_format a string to use as
283
+ # formatter for the success message.
284
+ # @option options [String] failed_message_format a string to use as
285
+ # formatter for the failed message.
286
+ # @option options [String] pending_message_format a string to use as
287
+ # formatter for the pending message.
288
+ # @option options [String] default_message_format a string to use as
289
+ # formatter when no format per type is defined.
290
+ # @option options [String] success_message_color the success notification
291
+ # foreground color name.
292
+ # @option options [String] failed_message_color the failed notification
293
+ # foreground color name.
294
+ # @option options [String] pending_message_color the pending notification
295
+ # foreground color name.
296
+ # @option options [String] default_message_color a notification
297
+ # foreground color to use when no color per type is defined.
298
+ # @option options [String] line_separator a string to use instead of a
299
+ # line-break.
300
+ #
301
+ def _display_message(type, title, message, opts = {})
302
+ default_format = opts[:default_message_format]
303
+ default_color = opts[:default_message_color]
304
+ display_time = opts[:timeout]
305
+ separator = opts[:line_separator]
306
+
307
+ format = "#{type}_message_format".to_sym
308
+ message_format = opts.fetch(format, default_format)
309
+
310
+ color = "#{type}_message_color".to_sym
311
+ message_color = opts.fetch(color, default_color)
312
+
313
+ color = _tmux_color(type, opts)
314
+ formatted_message = message.split("\n").join(separator)
315
+ msg = message_format % [title, formatted_message]
316
+
317
+ cl = Client.new(client(opts))
318
+ cl.display_time = display_time * 1000
319
+ cl.message_fg = message_color
320
+ cl.message_bg = color
321
+ cl.display_message(msg)
322
+ end
323
+
324
+ # Get the Tmux color for the notification type.
325
+ # You can configure your own color by overwriting the defaults.
326
+ #
327
+ # @param [String] type the notification type
328
+ # @return [String] the name of the emacs color
329
+ #
330
+ def _tmux_color(type, opts = {})
331
+ type = type.to_sym
332
+ opts[type] || opts[:default]
333
+ end
334
+
335
+ def self._start_session
336
+ fail "Already turned on!" if @session
337
+ @session = Session.new
338
+ end
339
+
340
+ def self._end_session
341
+ fail "Already turned off!" unless @session || nil
342
+ @session.close
343
+ @session = nil
344
+ end
345
+
346
+ def self._session
347
+ @session
348
+ end
349
+
350
+ def client(options)
351
+ options[:display_on_all_clients] ? :all : nil
352
+ end
353
+ end
354
+ end
355
+ end