notiffany 0.0.2

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.
@@ -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