notiffany 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +3 -0
- data/.travis.yml +13 -0
- data/Gemfile +15 -0
- data/Guardfile +56 -0
- data/LICENSE.txt +22 -0
- data/README.md +69 -0
- data/Rakefile +13 -0
- data/images/failed.png +0 -0
- data/images/guard.png +0 -0
- data/images/pending.png +0 -0
- data/images/success.png +0 -0
- data/lib/notiffany.rb +6 -0
- data/lib/notiffany/notifier.rb +205 -0
- data/lib/notiffany/notifier/base.rb +126 -0
- data/lib/notiffany/notifier/detected.rb +113 -0
- data/lib/notiffany/notifier/emacs.rb +107 -0
- data/lib/notiffany/notifier/file.rb +44 -0
- data/lib/notiffany/notifier/gntp.rb +91 -0
- data/lib/notiffany/notifier/growl.rb +81 -0
- data/lib/notiffany/notifier/libnotify.rb +54 -0
- data/lib/notiffany/notifier/notifysend.rb +84 -0
- data/lib/notiffany/notifier/rb_notifu.rb +89 -0
- data/lib/notiffany/notifier/terminal_notifier.rb +59 -0
- data/lib/notiffany/notifier/terminal_title.rb +39 -0
- data/lib/notiffany/notifier/tmux.rb +355 -0
- data/lib/notiffany/version.rb +3 -0
- data/notiffany.gemspec +28 -0
- metadata +114 -0
@@ -0,0 +1,113 @@
|
|
1
|
+
require "nenv"
|
2
|
+
|
3
|
+
require_relative "emacs"
|
4
|
+
require_relative "file"
|
5
|
+
require_relative "gntp"
|
6
|
+
require_relative "growl"
|
7
|
+
require_relative "libnotify"
|
8
|
+
require_relative "notifysend"
|
9
|
+
require_relative "rb_notifu"
|
10
|
+
require_relative "terminal_notifier"
|
11
|
+
require_relative "terminal_title"
|
12
|
+
require_relative "tmux"
|
13
|
+
|
14
|
+
module Notiffany
|
15
|
+
class Notifier
|
16
|
+
# @private api
|
17
|
+
|
18
|
+
# TODO: use a socket instead of passing env variables to child processes
|
19
|
+
# (currently probably only used by guard-cucumber anyway)
|
20
|
+
YamlEnvStorage = Nenv::Builder.build do
|
21
|
+
create_method(:notifiers=) { |data| YAML::dump(data) }
|
22
|
+
create_method(:notifiers) { |data| data ? YAML::load(data) : [] }
|
23
|
+
end
|
24
|
+
|
25
|
+
# @private api
|
26
|
+
class Detected
|
27
|
+
NO_SUPPORTED_NOTIFIERS = "Notiffany could not detect any of the"\
|
28
|
+
" supported notification libraries."
|
29
|
+
|
30
|
+
class NoneAvailableError < RuntimeError
|
31
|
+
end
|
32
|
+
|
33
|
+
class UnknownNotifier < RuntimeError
|
34
|
+
def initialize(name)
|
35
|
+
super
|
36
|
+
@name = name
|
37
|
+
end
|
38
|
+
|
39
|
+
def name
|
40
|
+
@name
|
41
|
+
end
|
42
|
+
|
43
|
+
def message
|
44
|
+
"Unknown notifier: #{@name.inspect}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def initialize(supported, env_namespace, logger)
|
49
|
+
@supported = supported
|
50
|
+
@environment = YamlEnvStorage.new(env_namespace)
|
51
|
+
@logger = logger
|
52
|
+
end
|
53
|
+
|
54
|
+
def reset
|
55
|
+
@environment.notifiers = nil
|
56
|
+
end
|
57
|
+
|
58
|
+
def detect
|
59
|
+
return unless _data.empty?
|
60
|
+
@supported.each do |group|
|
61
|
+
group.detect do |name, _|
|
62
|
+
begin
|
63
|
+
add(name, {})
|
64
|
+
true
|
65
|
+
rescue Notifier::Base::UnavailableError => e
|
66
|
+
@logger.debug "Notiffany: #{name} not available (#{e.message})."
|
67
|
+
false
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
fail NoneAvailableError, NO_SUPPORTED_NOTIFIERS if _data.empty?
|
73
|
+
end
|
74
|
+
|
75
|
+
def available
|
76
|
+
@available ||= _data.map do |entry|
|
77
|
+
_to_module(entry[:name]).new(entry[:options])
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def add(name, opts)
|
82
|
+
@available = nil
|
83
|
+
all = @environment.notifiers
|
84
|
+
|
85
|
+
# Silently skip if it's already available, because otherwise
|
86
|
+
# we'd have to do :turn_off, then configure, then :turn_on
|
87
|
+
names = all.map(&:first).map(&:last)
|
88
|
+
unless names.include?(name)
|
89
|
+
fail UnknownNotifier, name unless (klass = _to_module(name))
|
90
|
+
|
91
|
+
klass.new(opts) # raises if unavailable
|
92
|
+
@environment.notifiers = all << { name: name, options: opts }
|
93
|
+
end
|
94
|
+
|
95
|
+
# Just overwrite the options (without turning the notifier off or on),
|
96
|
+
# so those options will be passed in next calls to notify()
|
97
|
+
all.each { |item| item[:options] = opts if item[:name] == name }
|
98
|
+
end
|
99
|
+
|
100
|
+
def _to_module(name)
|
101
|
+
@supported.each do |group|
|
102
|
+
next unless (notifier = group.detect { |n, _| n == name })
|
103
|
+
return notifier.last
|
104
|
+
end
|
105
|
+
nil
|
106
|
+
end
|
107
|
+
|
108
|
+
def _data
|
109
|
+
@environment.notifiers || []
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require "notiffany/notifier/base"
|
2
|
+
require "shellany/sheller"
|
3
|
+
|
4
|
+
module Notiffany
|
5
|
+
class Notifier
|
6
|
+
# Send a notification to Emacs with emacsclient
|
7
|
+
# (http://www.emacswiki.org/emacs/EmacsClient).
|
8
|
+
#
|
9
|
+
class Emacs < Base
|
10
|
+
DEFAULTS = {
|
11
|
+
client: "emacsclient",
|
12
|
+
success: "ForestGreen",
|
13
|
+
failed: "Firebrick",
|
14
|
+
default: "Black",
|
15
|
+
fontcolor: "White",
|
16
|
+
}
|
17
|
+
|
18
|
+
class Client
|
19
|
+
def initialize(options)
|
20
|
+
@client = options[:client]
|
21
|
+
end
|
22
|
+
|
23
|
+
def available?
|
24
|
+
emacs_eval("'1'")
|
25
|
+
end
|
26
|
+
|
27
|
+
def notify(color, bgcolor)
|
28
|
+
elisp = <<-EOF.gsub(/\s+/, " ").strip
|
29
|
+
(set-face-attribute 'mode-line nil
|
30
|
+
:background "#{bgcolor}"
|
31
|
+
:foreground "#{color}")
|
32
|
+
EOF
|
33
|
+
emacs_eval(elisp)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def emacs_eval(code)
|
39
|
+
Shellany::Sheller.run(@client, "--eval", code)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def _gem_name
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
49
|
+
def _check_available(options)
|
50
|
+
return if Client.new(options).available?
|
51
|
+
fail UnavailableError, "Emacs client failed"
|
52
|
+
end
|
53
|
+
|
54
|
+
# Shows a system notification.
|
55
|
+
#
|
56
|
+
# @param [String] type the notification type. Either 'success',
|
57
|
+
# 'pending', 'failed' or 'notify'
|
58
|
+
# @param [String] title the notification title
|
59
|
+
# @param [String] message the notification message body
|
60
|
+
# @param [String] image the path to the notification image
|
61
|
+
# @param [Hash] opts additional notification library options
|
62
|
+
# @option opts [String] success the color to use for success
|
63
|
+
# notifications (default is 'ForestGreen')
|
64
|
+
# @option opts [String] failed the color to use for failure
|
65
|
+
# notifications (default is 'Firebrick')
|
66
|
+
# @option opts [String] pending the color to use for pending
|
67
|
+
# notifications
|
68
|
+
# @option opts [String] default the default color to use (default is
|
69
|
+
# 'Black')
|
70
|
+
# @option opts [String] client the client to use for notification
|
71
|
+
# (default is 'emacsclient')
|
72
|
+
# @option opts [String, Integer] priority specify an int or named key
|
73
|
+
# (default is 0)
|
74
|
+
#
|
75
|
+
def _perform_notify(_message, opts = {})
|
76
|
+
color = _emacs_color(opts[:type], opts)
|
77
|
+
fontcolor = _emacs_color(:fontcolor, opts)
|
78
|
+
Client.new(opts).notify(fontcolor, color)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Get the Emacs color for the notification type.
|
82
|
+
# You can configure your own color by overwrite the defaults.
|
83
|
+
#
|
84
|
+
# @param [String] type the notification type
|
85
|
+
# @param [Hash] options aditional notification options
|
86
|
+
#
|
87
|
+
# @option options [String] success the color to use for success
|
88
|
+
# notifications (default is 'ForestGreen')
|
89
|
+
#
|
90
|
+
# @option options [String] failed the color to use for failure
|
91
|
+
# notifications (default is 'Firebrick')
|
92
|
+
#
|
93
|
+
# @option options [String] pending the color to use for pending
|
94
|
+
# notifications
|
95
|
+
#
|
96
|
+
# @option options [String] default the default color to use (default is
|
97
|
+
# 'Black')
|
98
|
+
#
|
99
|
+
# @return [String] the name of the emacs color
|
100
|
+
#
|
101
|
+
def _emacs_color(type, options = {})
|
102
|
+
default = options.fetch(:default, DEFAULTS[:default])
|
103
|
+
options.fetch(type.to_sym, default)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "notiffany/notifier/base"
|
2
|
+
|
3
|
+
module Notiffany
|
4
|
+
class Notifier
|
5
|
+
# Writes notifications to a file.
|
6
|
+
#
|
7
|
+
class File < Base
|
8
|
+
DEFAULTS = { format: "%s\n%s\n%s\n" }
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
# @param [Hash] opts some options
|
13
|
+
# @option opts [Boolean] path the path to a file where notification
|
14
|
+
# message will be written
|
15
|
+
#
|
16
|
+
def _check_available(opts = {})
|
17
|
+
fail UnavailableError, "No :path option given" unless opts[:path]
|
18
|
+
end
|
19
|
+
|
20
|
+
# Writes the notification to a file. By default it writes type, title,
|
21
|
+
# and message separated by newlines.
|
22
|
+
#
|
23
|
+
# @param [String] message the notification message body
|
24
|
+
# @param [Hash] opts additional notification library options
|
25
|
+
# @option opts [String] type the notification type. Either 'success',
|
26
|
+
# 'pending', 'failed' or 'notify'
|
27
|
+
# @option opts [String] title the notification title
|
28
|
+
# @option opts [String] image the path to the notification image
|
29
|
+
# @option opts [String] format printf style format for file contents
|
30
|
+
# @option opts [String] path the path of where to write the file
|
31
|
+
#
|
32
|
+
def _perform_notify(message, opts = {})
|
33
|
+
fail UnavailableError, "No :path option given" unless opts[:path]
|
34
|
+
|
35
|
+
format = opts[:format]
|
36
|
+
::File.write(opts[:path], format % [opts[:type], opts[:title], message])
|
37
|
+
end
|
38
|
+
|
39
|
+
def _gem_name
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require "notiffany/notifier/base"
|
2
|
+
|
3
|
+
module Notiffany
|
4
|
+
class Notifier
|
5
|
+
# System notifications using the
|
6
|
+
# [ruby_gntp](https://github.com/snaka/ruby_gntp) gem.
|
7
|
+
#
|
8
|
+
# This gem is available for OS X, Linux and Windows and sends system
|
9
|
+
# notifications to the following system notification frameworks through the
|
10
|
+
#
|
11
|
+
# [Growl Network Transport
|
12
|
+
# Protocol](http://www.growlforwindows.com/gfw/help/gntp.aspx):
|
13
|
+
#
|
14
|
+
# * [Growl](http://growl.info)
|
15
|
+
# * [Growl for Windows](http://www.growlforwindows.com)
|
16
|
+
# * [Growl for Linux](http://mattn.github.com/growl-for-linux)
|
17
|
+
# * [Snarl](https://sites.google.com/site/snarlapp)
|
18
|
+
class GNTP < Base
|
19
|
+
DEFAULTS = {
|
20
|
+
sticky: false
|
21
|
+
}
|
22
|
+
|
23
|
+
# Default options for the ruby gtnp client.
|
24
|
+
CLIENT_DEFAULTS = {
|
25
|
+
host: "127.0.0.1",
|
26
|
+
password: "",
|
27
|
+
port: 23053
|
28
|
+
}
|
29
|
+
|
30
|
+
def _supported_hosts
|
31
|
+
%w(darwin linux freebsd openbsd sunos solaris mswin mingw cygwin)
|
32
|
+
end
|
33
|
+
|
34
|
+
def _gem_name
|
35
|
+
"ruby_gntp"
|
36
|
+
end
|
37
|
+
|
38
|
+
def _check_available(_opts)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Shows a system notification.
|
42
|
+
#
|
43
|
+
# @param [String] message the notification message body
|
44
|
+
# @param [Hash] opts additional notification library options
|
45
|
+
# @option opts [String] type the notification type. Either 'success',
|
46
|
+
# 'pending', 'failed' or 'notify'
|
47
|
+
# @option opts [String] title the notification title
|
48
|
+
# @option opts [String] image the path to the notification image
|
49
|
+
# @option opts [String] host the hostname or IP address to which to send
|
50
|
+
# a remote notification
|
51
|
+
# @option opts [String] password the password used for remote
|
52
|
+
# notifications
|
53
|
+
# @option opts [Integer] port the port to send a remote notification
|
54
|
+
# @option opts [Boolean] sticky make the notification sticky
|
55
|
+
#
|
56
|
+
def _perform_notify(message, opts = {})
|
57
|
+
opts = {
|
58
|
+
name: opts[:type].to_s,
|
59
|
+
text: message,
|
60
|
+
icon: opts[:image]
|
61
|
+
}.merge(opts)
|
62
|
+
|
63
|
+
_gntp_client(opts).notify(opts)
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def _gntp_client(opts = {})
|
69
|
+
@_client ||= begin
|
70
|
+
gntp = ::GNTP.new(
|
71
|
+
"Notiffany",
|
72
|
+
opts.fetch(:host) { CLIENT_DEFAULTS[:host] },
|
73
|
+
opts.fetch(:password) { CLIENT_DEFAULTS[:password] },
|
74
|
+
opts.fetch(:port) { CLIENT_DEFAULTS[:port] }
|
75
|
+
)
|
76
|
+
|
77
|
+
gntp.register(
|
78
|
+
app_icon: _image_path(:guard),
|
79
|
+
notifications: [
|
80
|
+
{ name: "notify", enabled: true },
|
81
|
+
{ name: "failed", enabled: true },
|
82
|
+
{ name: "pending", enabled: true },
|
83
|
+
{ name: "success", enabled: true }
|
84
|
+
]
|
85
|
+
)
|
86
|
+
gntp
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require "notiffany/notifier/base"
|
2
|
+
|
3
|
+
module Notiffany
|
4
|
+
class Notifier
|
5
|
+
# System notifications using the
|
6
|
+
# [growl](https://github.com/visionmedia/growl) gem.
|
7
|
+
#
|
8
|
+
# This gem is available for OS X and sends system notifications to
|
9
|
+
# [Growl](http://growl.info) through the
|
10
|
+
# [GrowlNotify](http://growl.info/downloads) executable.
|
11
|
+
#
|
12
|
+
# The `growlnotify` executable must be installed manually or by using
|
13
|
+
# [Homebrew](http://mxcl.github.com/homebrew/).
|
14
|
+
#
|
15
|
+
# Sending notifications with this notifier will not show the different
|
16
|
+
# notifications in the Growl preferences. Use the :gntp notifier if you
|
17
|
+
# want to customize each notification type in Growl.
|
18
|
+
#
|
19
|
+
# @example Install `growlnotify` with Homebrew
|
20
|
+
# brew install growlnotify
|
21
|
+
#
|
22
|
+
# @example Add the `growl` gem to your `Gemfile`
|
23
|
+
# group :development
|
24
|
+
# gem 'growl'
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# @example Add the `:growl` notifier to your `Guardfile`
|
28
|
+
# notification :growl
|
29
|
+
#
|
30
|
+
# @example Add the `:growl_notify` notifier with configuration options to
|
31
|
+
# your `Guardfile` notification :growl, sticky: true, host: '192.168.1.5',
|
32
|
+
# password: 'secret'
|
33
|
+
#
|
34
|
+
class Growl < Base
|
35
|
+
INSTALL_GROWLNOTIFY = "Please install the 'growlnotify' executable'\
|
36
|
+
' (available by installing the 'growl' gem)."
|
37
|
+
|
38
|
+
# Default options for the growl notifications.
|
39
|
+
DEFAULTS = {
|
40
|
+
sticky: false,
|
41
|
+
priority: 0
|
42
|
+
}
|
43
|
+
|
44
|
+
def _supported_hosts
|
45
|
+
%w(darwin)
|
46
|
+
end
|
47
|
+
|
48
|
+
def _check_available(_opts = {})
|
49
|
+
fail UnavailableError, INSTALL_GROWLNOTIFY unless ::Growl.installed?
|
50
|
+
end
|
51
|
+
|
52
|
+
# Shows a system notification.
|
53
|
+
#
|
54
|
+
# The documented options are for GrowlNotify 1.3, but the older options
|
55
|
+
# are also supported. Please see `growlnotify --help`.
|
56
|
+
#
|
57
|
+
# Priority can be one of the following named keys: `Very Low`,
|
58
|
+
# `Moderate`, `Normal`, `High`, `Emergency`. It can also be an integer
|
59
|
+
# between -2 and 2.
|
60
|
+
#
|
61
|
+
# @param [String] message the notification message body
|
62
|
+
# @param [Hash] opts additional notification library options
|
63
|
+
# @option opts [String] type the notification type. Either 'success',
|
64
|
+
# 'pending', 'failed' or 'notify'
|
65
|
+
# @option opts [String] title the notification title
|
66
|
+
# @option opts [String] image the path to the notification image
|
67
|
+
# @option opts [Boolean] sticky make the notification sticky
|
68
|
+
# @option opts [String, Integer] priority specify an int or named key
|
69
|
+
# (default is 0)
|
70
|
+
# @option opts [String] host the hostname or IP address to which to
|
71
|
+
# send a remote notification
|
72
|
+
# @option opts [String] password the password used for remote
|
73
|
+
# notifications
|
74
|
+
#
|
75
|
+
def _perform_notify(message, opts = {})
|
76
|
+
opts = { name: "Notiffany" }.merge(opts)
|
77
|
+
::Growl.notify(message, opts)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require "notiffany/notifier/base"
|
2
|
+
|
3
|
+
module Notiffany
|
4
|
+
class Notifier
|
5
|
+
# System notifications using the
|
6
|
+
# [libnotify](https://github.com/splattael/libnotify) gem.
|
7
|
+
#
|
8
|
+
# This gem is available for Linux, FreeBSD, OpenBSD and Solaris and sends
|
9
|
+
# system notifications to
|
10
|
+
# Gnome [libnotify](http://developer.gnome.org/libnotify):
|
11
|
+
#
|
12
|
+
class Libnotify < Base
|
13
|
+
DEFAULTS = {
|
14
|
+
transient: false,
|
15
|
+
append: true,
|
16
|
+
timeout: 3
|
17
|
+
}
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def _supported_hosts
|
22
|
+
%w(linux freebsd openbsd sunos solaris)
|
23
|
+
end
|
24
|
+
|
25
|
+
def _check_available(_opts = {})
|
26
|
+
end
|
27
|
+
|
28
|
+
# Shows a system notification.
|
29
|
+
#
|
30
|
+
# @param [String] message the notification message body
|
31
|
+
# @param [Hash] opts additional notification library options
|
32
|
+
# @option opts [String] type the notification type. Either 'success',
|
33
|
+
# 'pending', 'failed' or 'notify'
|
34
|
+
# @option opts [String] title the notification title
|
35
|
+
# @option opts [String] image the path to the notification image
|
36
|
+
# @option opts [Boolean] transient keep the notifications around after
|
37
|
+
# display
|
38
|
+
# @option opts [Boolean] append append onto existing notification
|
39
|
+
# @option opts [Number, Boolean] timeout the number of seconds to display
|
40
|
+
# (1.5 (s), 1000 (ms), false)
|
41
|
+
#
|
42
|
+
def _perform_notify(message, opts = {})
|
43
|
+
opts = opts.merge(
|
44
|
+
summary: opts[:title],
|
45
|
+
icon_path: opts[:image],
|
46
|
+
body: message,
|
47
|
+
urgency: opts[:urgency] || (opts[:type] == "failed" ? :normal : :low)
|
48
|
+
)
|
49
|
+
|
50
|
+
::Libnotify.show(opts)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|