notiffany 0.0.8 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/notiffany/notifier.rb +42 -37
- data/lib/notiffany/notifier/base.rb +1 -1
- data/lib/notiffany/notifier/config.rb +34 -0
- data/lib/notiffany/notifier/detected.rb +14 -8
- data/lib/notiffany/notifier/emacs.rb +26 -36
- data/lib/notiffany/notifier/emacs/client.rb +52 -0
- data/lib/notiffany/notifier/file.rb +2 -2
- data/lib/notiffany/notifier/gntp.rb +3 -2
- data/lib/notiffany/notifier/notifysend.rb +2 -2
- data/lib/notiffany/notifier/terminal_title.rb +1 -1
- data/lib/notiffany/notifier/tmux.rb +21 -244
- data/lib/notiffany/notifier/tmux/client.rb +103 -0
- data/lib/notiffany/notifier/tmux/notification.rb +62 -0
- data/lib/notiffany/notifier/tmux/session.rb +40 -0
- data/lib/notiffany/version.rb +1 -1
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 107a426a3f017b084ea3a11ac5a3c951b8569269
|
4
|
+
data.tar.gz: f4f275c620a69ff487e61b936cadfd2b1eebcbac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 394ec7473db4997eaae759be74f386dc9b2acf4a65d60a9fb3196a6a817a7a21803330952701dee5a1327b7db52b30bc4f2fc9722d1320b5199c4e87bdd5b44d
|
7
|
+
data.tar.gz: d89a0a76ee60251e5b7c2efd089fa902b0f3974db6637e48d72b88e8b112f045311525415e24da1019783c14e561ec84f34afd78a7a72f7de8dcac2305f72ba0
|
data/README.md
CHANGED
data/lib/notiffany/notifier.rb
CHANGED
@@ -2,9 +2,9 @@ require "yaml"
|
|
2
2
|
require "rbconfig"
|
3
3
|
require "pathname"
|
4
4
|
require "nenv"
|
5
|
-
require "logger"
|
6
5
|
|
7
6
|
require "notiffany/notifier/detected"
|
7
|
+
require "notiffany/notifier/config"
|
8
8
|
|
9
9
|
module Notiffany
|
10
10
|
# The notifier handles sending messages to different notifiers. Currently the
|
@@ -43,9 +43,7 @@ module Notiffany
|
|
43
43
|
end
|
44
44
|
|
45
45
|
class Notifier
|
46
|
-
|
47
|
-
|
48
|
-
NOTIFICATIONS_DISABLED = "Notifications disabled by GUARD_NOTIFY" +
|
46
|
+
NOTIFICATIONS_DISABLED = "Notifications disabled by GUARD_NOTIFY" \
|
49
47
|
" environment variable"
|
50
48
|
|
51
49
|
USING_NOTIFIER = "Notiffany is using %s to send notifications."
|
@@ -79,35 +77,16 @@ module Notiffany
|
|
79
77
|
class NotServer < RuntimeError
|
80
78
|
end
|
81
79
|
|
82
|
-
|
83
|
-
@env_namespace = opts.fetch(:namespace, "notiffany")
|
84
|
-
@logger = opts.fetch(:logger) do
|
85
|
-
Logger.new($stderr).tap { |l| l.level = Logger::WARN }
|
86
|
-
end
|
80
|
+
attr_reader :config
|
87
81
|
|
88
|
-
|
89
|
-
@
|
82
|
+
def initialize(opts)
|
83
|
+
@config = Config.new(opts)
|
84
|
+
@detected = Detected.new(SUPPORTED, config.env_namespace, config.logger)
|
90
85
|
return if _client?
|
91
86
|
|
92
|
-
|
93
|
-
|
94
|
-
fail "Already connected" if active?
|
95
|
-
|
96
|
-
options = DEFAULTS.merge(opts)
|
97
|
-
return unless enabled? && options[:notify]
|
98
|
-
|
99
|
-
notifiers = opts.fetch(:notifiers, {})
|
100
|
-
if notifiers.any?
|
101
|
-
notifiers.each do |name, notifier_options|
|
102
|
-
@detected.add(name, notifier_options)
|
103
|
-
end
|
104
|
-
else
|
105
|
-
@detected.detect
|
106
|
-
end
|
107
|
-
|
108
|
-
turn_on
|
87
|
+
_activate
|
109
88
|
rescue Detected::NoneAvailableError => e
|
110
|
-
|
89
|
+
config.logger.info e.to_s
|
111
90
|
end
|
112
91
|
|
113
92
|
def disconnect
|
@@ -133,13 +112,7 @@ module Notiffany
|
|
133
112
|
|
134
113
|
fail "Already active!" if active?
|
135
114
|
|
136
|
-
|
137
|
-
|
138
|
-
@detected.available.each do |obj|
|
139
|
-
@logger.debug(format(USING_NOTIFIER, obj.title)) unless silent
|
140
|
-
obj.turn_on if obj.respond_to?(:turn_on)
|
141
|
-
end
|
142
|
-
|
115
|
+
_turn_on_notifiers(options)
|
143
116
|
_env.notify_active = true
|
144
117
|
end
|
145
118
|
|
@@ -191,7 +164,7 @@ module Notiffany
|
|
191
164
|
private
|
192
165
|
|
193
166
|
def _env
|
194
|
-
@environment ||= Env.new(
|
167
|
+
@environment ||= Env.new(config.env_namespace)
|
195
168
|
end
|
196
169
|
|
197
170
|
def _check_server!
|
@@ -201,5 +174,37 @@ module Notiffany
|
|
201
174
|
def _client?
|
202
175
|
(pid = _env.notify_pid) && (pid != $$)
|
203
176
|
end
|
177
|
+
|
178
|
+
def _detect_or_add_notifiers
|
179
|
+
notifiers = config.notifiers
|
180
|
+
return @detected.detect if notifiers.empty?
|
181
|
+
|
182
|
+
notifiers.each do |name, notifier_options|
|
183
|
+
@detected.add(name, notifier_options)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def _notification_wanted?
|
188
|
+
enabled? && config.notify?
|
189
|
+
end
|
190
|
+
|
191
|
+
def _activate
|
192
|
+
_env.notify_pid = $$
|
193
|
+
|
194
|
+
fail "Already connected" if active?
|
195
|
+
|
196
|
+
return unless _notification_wanted?
|
197
|
+
|
198
|
+
_detect_or_add_notifiers
|
199
|
+
turn_on
|
200
|
+
end
|
201
|
+
|
202
|
+
def _turn_on_notifiers(options)
|
203
|
+
silent = options[:silent]
|
204
|
+
@detected.available.each do |obj|
|
205
|
+
config.logger.debug(format(USING_NOTIFIER, obj.title)) unless silent
|
206
|
+
obj.turn_on if obj.respond_to?(:turn_on)
|
207
|
+
end
|
208
|
+
end
|
204
209
|
end
|
205
210
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require "logger"
|
2
|
+
|
3
|
+
module Notiffany
|
4
|
+
class Notifier
|
5
|
+
# Configuration class for Notifier
|
6
|
+
class Config
|
7
|
+
DEFAULTS = { notify: true }.freeze
|
8
|
+
|
9
|
+
attr_reader :env_namespace
|
10
|
+
attr_reader :logger
|
11
|
+
attr_reader :notifiers
|
12
|
+
|
13
|
+
def initialize(opts)
|
14
|
+
options = DEFAULTS.merge(opts)
|
15
|
+
@env_namespace = opts.fetch(:namespace, "notiffany")
|
16
|
+
@logger = _setup_logger(options)
|
17
|
+
@notify = options[:notify]
|
18
|
+
@notifiers = opts.fetch(:notifiers, {})
|
19
|
+
end
|
20
|
+
|
21
|
+
def notify?
|
22
|
+
@notify
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def _setup_logger(opts)
|
28
|
+
opts.fetch(:logger) do
|
29
|
+
Logger.new($stderr).tap { |l| l.level = Logger::WARN }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -19,8 +19,8 @@ module Notiffany
|
|
19
19
|
# TODO: use a socket instead of passing env variables to child processes
|
20
20
|
# (currently probably only used by guard-cucumber anyway)
|
21
21
|
YamlEnvStorage = Nenv::Builder.build do
|
22
|
-
create_method(:notifiers=) { |data| YAML
|
23
|
-
create_method(:notifiers) { |data| data ? YAML
|
22
|
+
create_method(:notifiers=) { |data| YAML.dump(data || []) }
|
23
|
+
create_method(:notifiers) { |data| data ? YAML.load(data) : [] }
|
24
24
|
end
|
25
25
|
|
26
26
|
# @private api
|
@@ -37,9 +37,7 @@ module Notiffany
|
|
37
37
|
@name = name
|
38
38
|
end
|
39
39
|
|
40
|
-
|
41
|
-
@name
|
42
|
-
end
|
40
|
+
attr_reader :name
|
43
41
|
|
44
42
|
def message
|
45
43
|
"Unknown notifier: #{@name.inspect}"
|
@@ -61,7 +59,7 @@ module Notiffany
|
|
61
59
|
@supported.each do |group|
|
62
60
|
group.detect do |name, _|
|
63
61
|
begin
|
64
|
-
|
62
|
+
_add(name, {})
|
65
63
|
true
|
66
64
|
rescue Notifier::Base::UnavailableError => e
|
67
65
|
@logger.debug "Notiffany: #{name} not available (#{e.message})."
|
@@ -79,7 +77,17 @@ module Notiffany
|
|
79
77
|
end
|
80
78
|
end
|
81
79
|
|
80
|
+
# Called when user has notifier-specific config.
|
81
|
+
# Honor the config by warning if something is wrong
|
82
82
|
def add(name, opts)
|
83
|
+
_add(name, opts)
|
84
|
+
rescue Notifier::Base::UnavailableError => e
|
85
|
+
@logger.warning("Notiffany: #{name} not available (#{e.message}).")
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def _add(name, opts)
|
83
91
|
@available = nil
|
84
92
|
all = _notifiers
|
85
93
|
|
@@ -98,8 +106,6 @@ module Notiffany
|
|
98
106
|
all.each { |item| item[:options] = opts if item[:name] == name }
|
99
107
|
end
|
100
108
|
|
101
|
-
private
|
102
|
-
|
103
109
|
def _to_module(name)
|
104
110
|
@supported.each do |group|
|
105
111
|
next unless (notifier = group.detect { |n, _| n == name })
|
@@ -1,5 +1,7 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'notiffany/notifier/base'
|
2
|
+
require 'shellany/sheller'
|
3
|
+
|
4
|
+
require 'notiffany/notifier/emacs/client'
|
3
5
|
|
4
6
|
module Notiffany
|
5
7
|
class Notifier
|
@@ -8,37 +10,18 @@ module Notiffany
|
|
8
10
|
#
|
9
11
|
class Emacs < Base
|
10
12
|
DEFAULTS = {
|
11
|
-
client:
|
12
|
-
success:
|
13
|
-
failed:
|
14
|
-
default:
|
15
|
-
fontcolor:
|
16
|
-
}
|
17
|
-
|
18
|
-
class Client
|
19
|
-
def initialize(options)
|
20
|
-
@client = options[:client]
|
21
|
-
end
|
22
|
-
|
23
|
-
def available?
|
24
|
-
emacs_eval({ 'ALTERNATE_EDITOR' =>'false' }, "'1'")
|
25
|
-
end
|
13
|
+
client: 'emacsclient',
|
14
|
+
success: 'ForestGreen',
|
15
|
+
failed: 'Firebrick',
|
16
|
+
default: 'Black',
|
17
|
+
fontcolor: 'White'
|
18
|
+
}.freeze
|
26
19
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
EOF
|
33
|
-
emacs_eval(elisp)
|
34
|
-
end
|
35
|
-
|
36
|
-
private
|
37
|
-
|
38
|
-
def emacs_eval(env={}, code)
|
39
|
-
Shellany::Sheller.run(env, @client, "--eval", code)
|
40
|
-
end
|
41
|
-
end
|
20
|
+
DEFAULT_ELISP_ERB = <<EOF.freeze
|
21
|
+
(set-face-attribute 'mode-line nil
|
22
|
+
:background "<%= bgcolor %>"
|
23
|
+
:foreground "<%= color %>")
|
24
|
+
EOF
|
42
25
|
|
43
26
|
private
|
44
27
|
|
@@ -47,8 +30,8 @@ module Notiffany
|
|
47
30
|
end
|
48
31
|
|
49
32
|
def _check_available(options)
|
50
|
-
return if Client.new(options).available?
|
51
|
-
|
33
|
+
return if Client.new(options.merge(elisp_erb: "'1'")).available?
|
34
|
+
raise UnavailableError, 'Emacs client failed'
|
52
35
|
end
|
53
36
|
|
54
37
|
# Shows a system notification.
|
@@ -72,10 +55,12 @@ module Notiffany
|
|
72
55
|
# @option opts [String, Integer] priority specify an int or named key
|
73
56
|
# (default is 0)
|
74
57
|
#
|
75
|
-
def _perform_notify(
|
58
|
+
def _perform_notify(message, opts = {})
|
76
59
|
color = _emacs_color(opts[:type], opts)
|
77
60
|
fontcolor = _emacs_color(:fontcolor, opts)
|
78
|
-
|
61
|
+
|
62
|
+
opts = opts.merge(elisp_erb: _erb_for(opts[:elisp_file]))
|
63
|
+
Client.new(opts).notify(fontcolor, color, message)
|
79
64
|
end
|
80
65
|
|
81
66
|
# Get the Emacs color for the notification type.
|
@@ -102,6 +87,11 @@ module Notiffany
|
|
102
87
|
default = options.fetch(:default, DEFAULTS[:default])
|
103
88
|
options.fetch(type.to_sym, default)
|
104
89
|
end
|
90
|
+
|
91
|
+
def _erb_for(filename)
|
92
|
+
return DEFAULT_ELISP_ERB unless filename
|
93
|
+
IO.read(::File.expand_path(filename))
|
94
|
+
end
|
105
95
|
end
|
106
96
|
end
|
107
97
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'erb'
|
2
|
+
module Notiffany
|
3
|
+
class Notifier
|
4
|
+
class Emacs < Base
|
5
|
+
# Handles evaluating ELISP code in Emacs via Erb
|
6
|
+
class Client
|
7
|
+
attr_reader :elisp_erb
|
8
|
+
|
9
|
+
# Creates a safe binding with local variables for ERB
|
10
|
+
class Elisp < ERB
|
11
|
+
attr_reader :color
|
12
|
+
attr_reader :bgcolor
|
13
|
+
attr_reader :message
|
14
|
+
|
15
|
+
def initialize(code, color, bgcolor, message)
|
16
|
+
@color = color
|
17
|
+
@bgcolor = bgcolor
|
18
|
+
@message = message
|
19
|
+
@code = code
|
20
|
+
super(@code)
|
21
|
+
end
|
22
|
+
|
23
|
+
def result
|
24
|
+
super(binding)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(options)
|
29
|
+
@client = options[:client]
|
30
|
+
@elisp_erb = options[:elisp_erb]
|
31
|
+
raise ArgumentError, 'No :elisp_erb option given!' unless elisp_erb
|
32
|
+
end
|
33
|
+
|
34
|
+
def available?
|
35
|
+
script = Elisp.new(@elisp_erb, nil, nil, nil).result
|
36
|
+
_emacs_eval({ 'ALTERNATE_EDITOR' => 'false' }, script)
|
37
|
+
end
|
38
|
+
|
39
|
+
def notify(color, bgcolor, message = nil)
|
40
|
+
elisp = Elisp.new(elisp_erb, color, bgcolor, message).result
|
41
|
+
_emacs_eval({ 'ALTERNATE_EDITOR' => 'false' }, elisp)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def _emacs_eval(env, code)
|
47
|
+
Shellany::Sheller.run(env, @client, '--eval', code)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -32,8 +32,8 @@ module Notiffany
|
|
32
32
|
def _perform_notify(message, opts = {})
|
33
33
|
fail UnavailableError, "No :path option given" unless opts[:path]
|
34
34
|
|
35
|
-
|
36
|
-
::File.write(opts[:path],
|
35
|
+
str = format(opts[:format], opts[:type], opts[:title], message)
|
36
|
+
::File.write(opts[:path], str)
|
37
37
|
end
|
38
38
|
|
39
39
|
def _gem_name
|
@@ -24,11 +24,12 @@ module Notiffany
|
|
24
24
|
CLIENT_DEFAULTS = {
|
25
25
|
host: "127.0.0.1",
|
26
26
|
password: "",
|
27
|
-
port:
|
27
|
+
port: 23_053
|
28
28
|
}
|
29
29
|
|
30
30
|
def _supported_hosts
|
31
|
-
%w(darwin linux linux-gnu freebsd openbsd sunos solaris mswin mingw
|
31
|
+
%w(darwin linux linux-gnu freebsd openbsd sunos solaris mswin mingw
|
32
|
+
cygwin)
|
32
33
|
end
|
33
34
|
|
34
35
|
def _gem_name
|
@@ -26,7 +26,7 @@ module Notiffany
|
|
26
26
|
def _gem_name
|
27
27
|
nil
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
def _supported_hosts
|
31
31
|
%w(linux linux-gnu freebsd openbsd sunos solaris)
|
32
32
|
end
|
@@ -81,7 +81,7 @@ module Notiffany
|
|
81
81
|
#
|
82
82
|
def _to_arguments(command, supported, opts = {})
|
83
83
|
opts.inject(command) do |cmd, (flag, value)|
|
84
|
-
supported.include?(flag) ? (cmd << "-#{
|
84
|
+
supported.include?(flag) ? (cmd << "-#{flag}" << value.to_s) : cmd
|
85
85
|
end
|
86
86
|
end
|
87
87
|
end
|
@@ -1,5 +1,8 @@
|
|
1
1
|
require "notiffany/notifier/base"
|
2
|
-
|
2
|
+
|
3
|
+
require "notiffany/notifier/tmux/client"
|
4
|
+
require "notiffany/notifier/tmux/session"
|
5
|
+
require "notiffany/notifier/tmux/notification"
|
3
6
|
|
4
7
|
# TODO: this probably deserves a gem of it's own
|
5
8
|
module Notiffany
|
@@ -27,134 +30,6 @@ module Notiffany
|
|
27
30
|
color_location: "status-left-bg"
|
28
31
|
}
|
29
32
|
|
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
33
|
class Error < RuntimeError
|
159
34
|
end
|
160
35
|
|
@@ -226,129 +101,31 @@ module Notiffany
|
|
226
101
|
# message on all tmux clients or not
|
227
102
|
#
|
228
103
|
def _perform_notify(message, options = {})
|
229
|
-
change_color = options[:change_color]
|
230
104
|
locations = Array(options[:color_location])
|
231
|
-
display_the_title = options[:display_title]
|
232
|
-
display_message = options[:display_message]
|
233
105
|
type = options[:type].to_s
|
234
106
|
title = options[:title]
|
235
107
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
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]
|
108
|
+
tmux = Notification.new(type, options)
|
109
|
+
tmux.colorize(locations) if options[:change_color]
|
110
|
+
tmux.display_title(title, message) if options[:display_title]
|
111
|
+
tmux.display_message(title, message) if options[:display_message]
|
333
112
|
end
|
334
113
|
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
def self._end_session
|
341
|
-
fail "Already turned off!" unless @session || nil
|
342
|
-
@session.close
|
343
|
-
@session = nil
|
344
|
-
end
|
114
|
+
class << self
|
115
|
+
def _start_session
|
116
|
+
fail "Already turned on!" if @session
|
117
|
+
@session = Session.new
|
118
|
+
end
|
345
119
|
|
346
|
-
|
347
|
-
|
348
|
-
|
120
|
+
def _end_session
|
121
|
+
fail "Already turned off!" unless @session
|
122
|
+
@session.close
|
123
|
+
@session = nil
|
124
|
+
end
|
349
125
|
|
350
|
-
|
351
|
-
|
126
|
+
def _session
|
127
|
+
@session
|
128
|
+
end
|
352
129
|
end
|
353
130
|
end
|
354
131
|
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require "shellany/sheller"
|
2
|
+
|
3
|
+
module Notiffany
|
4
|
+
class Notifier
|
5
|
+
class Tmux < Base
|
6
|
+
# Class for actually calling TMux to run commands
|
7
|
+
class Client
|
8
|
+
CLIENT = "tmux".freeze
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def version
|
12
|
+
Float(_capture("-V")[/\d+\.\d+/])
|
13
|
+
end
|
14
|
+
|
15
|
+
def _capture(*args)
|
16
|
+
Shellany::Sheller.stdout(([CLIENT] + args).join(" "))
|
17
|
+
end
|
18
|
+
|
19
|
+
def _run(*args)
|
20
|
+
Shellany::Sheller.run(([CLIENT] + args).join(" "))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(client)
|
25
|
+
@client = client
|
26
|
+
end
|
27
|
+
|
28
|
+
def clients
|
29
|
+
return [@client] unless @client == :all
|
30
|
+
ttys = _capture("list-clients", "-F", "'\#{client_tty}'")
|
31
|
+
ttys = ttys.split(/\n/)
|
32
|
+
|
33
|
+
# if user is running 'tmux -C' remove this client from list
|
34
|
+
ttys.delete("(null)")
|
35
|
+
ttys
|
36
|
+
end
|
37
|
+
|
38
|
+
def set(key, value)
|
39
|
+
clients.each do |client|
|
40
|
+
args = client ? ["-t", client.strip] : nil
|
41
|
+
_run("set", "-q", *args, key, value)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def display_message(message)
|
46
|
+
clients.each do |client|
|
47
|
+
args = ["-c", client.strip] if client
|
48
|
+
# TODO: should properly escape message here
|
49
|
+
_run("display", *args, "'#{message}'")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def unset(key, value)
|
54
|
+
clients.each do |client|
|
55
|
+
_run(*_all_args_for(key, value, client))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def parse_options
|
60
|
+
output = _capture("show", "-t", @client)
|
61
|
+
Hash[output.lines.map { |line| _parse_option(line) }]
|
62
|
+
end
|
63
|
+
|
64
|
+
def message_fg=(color)
|
65
|
+
set("message-fg", color)
|
66
|
+
end
|
67
|
+
|
68
|
+
def message_bg=(color)
|
69
|
+
set("message-bg", color)
|
70
|
+
end
|
71
|
+
|
72
|
+
def display_time=(time)
|
73
|
+
set("display-time", time)
|
74
|
+
end
|
75
|
+
|
76
|
+
def title=(string)
|
77
|
+
# TODO: properly escape?
|
78
|
+
set("set-titles-string", "'#{string}'")
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def _run(*args)
|
84
|
+
self.class._run(*args)
|
85
|
+
end
|
86
|
+
|
87
|
+
def _capture(*args)
|
88
|
+
self.class._capture(*args)
|
89
|
+
end
|
90
|
+
|
91
|
+
def _parse_option(line)
|
92
|
+
line.partition(" ").map(&:strip).reject(&:empty?)
|
93
|
+
end
|
94
|
+
|
95
|
+
def _all_args_for(key, value, client)
|
96
|
+
unset = value ? [] : %w(-u)
|
97
|
+
args = client ? ["-t", client.strip] : []
|
98
|
+
["set", "-q", *unset, *args, key, *[value].compact]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Notiffany
|
2
|
+
class Notifier
|
3
|
+
class Tmux < Base
|
4
|
+
# Wraps a notification with it's options
|
5
|
+
class Notification
|
6
|
+
def initialize(type, options)
|
7
|
+
@type = type
|
8
|
+
@options = options
|
9
|
+
@color = options[type.to_sym] || options[:default]
|
10
|
+
@separator = options[:line_separator]
|
11
|
+
@message_color = _value_for(:message_color)
|
12
|
+
@client = Client.new(options[:display_on_all_clients] ? :all : nil)
|
13
|
+
end
|
14
|
+
|
15
|
+
def display_title(title, message)
|
16
|
+
title_format = _value_for(:title_format)
|
17
|
+
|
18
|
+
teaser_message = message.split("\n").first
|
19
|
+
display_title = format(title_format, title, teaser_message)
|
20
|
+
|
21
|
+
client.title = display_title
|
22
|
+
end
|
23
|
+
|
24
|
+
def display_message(title, message)
|
25
|
+
message = _message_for(title, message)
|
26
|
+
|
27
|
+
client.display_time = options[:timeout] * 1000
|
28
|
+
client.message_fg = message_color
|
29
|
+
client.message_bg = color
|
30
|
+
client.display_message(message)
|
31
|
+
end
|
32
|
+
|
33
|
+
def colorize(locations)
|
34
|
+
locations.each do |location|
|
35
|
+
client.set(location, color)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
attr_reader :type
|
42
|
+
attr_reader :options
|
43
|
+
attr_reader :color
|
44
|
+
attr_reader :message_color
|
45
|
+
attr_reader :client
|
46
|
+
attr_reader :separator
|
47
|
+
|
48
|
+
def _value_for(field)
|
49
|
+
format = "#{type}_#{field}".to_sym
|
50
|
+
default = options["default_#{field}".to_sym]
|
51
|
+
options.fetch(format, default)
|
52
|
+
end
|
53
|
+
|
54
|
+
def _message_for(title, message)
|
55
|
+
message_format = _value_for(:message_format)
|
56
|
+
formatted_message = message.split("\n").join(separator)
|
57
|
+
format(message_format, title, formatted_message)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Notiffany
|
2
|
+
class Notifier
|
3
|
+
class Tmux < Base
|
4
|
+
# Preserves TMux settings for all tmux sessions
|
5
|
+
class Session
|
6
|
+
def initialize
|
7
|
+
@options_store = {}
|
8
|
+
|
9
|
+
# NOTE: we are reading the settings of all clients
|
10
|
+
# - regardless of the :display_on_all_clients option
|
11
|
+
|
12
|
+
# Ideally, this should be done incrementally (e.g. if we start with
|
13
|
+
# "current" client and then override the :display_on_all_clients to
|
14
|
+
# true, only then the option store should be updated to contain
|
15
|
+
# settings of all clients
|
16
|
+
Client.new(:all).clients.each do |client|
|
17
|
+
@options_store[client] = {
|
18
|
+
"status-left-bg" => nil,
|
19
|
+
"status-right-bg" => nil,
|
20
|
+
"status-left-fg" => nil,
|
21
|
+
"status-right-fg" => nil,
|
22
|
+
"message-bg" => nil,
|
23
|
+
"message-fg" => nil,
|
24
|
+
"display-time" => nil
|
25
|
+
}.merge(Client.new(client).parse_options)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def close
|
30
|
+
@options_store.each do |client, options|
|
31
|
+
options.each do |key, value|
|
32
|
+
Client.new(client).unset(key, value)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
@options_store = nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/notiffany/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: notiffany
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cezary Baginski
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2016-05-18 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: nenv
|
@@ -71,8 +71,10 @@ files:
|
|
71
71
|
- lib/notiffany.rb
|
72
72
|
- lib/notiffany/notifier.rb
|
73
73
|
- lib/notiffany/notifier/base.rb
|
74
|
+
- lib/notiffany/notifier/config.rb
|
74
75
|
- lib/notiffany/notifier/detected.rb
|
75
76
|
- lib/notiffany/notifier/emacs.rb
|
77
|
+
- lib/notiffany/notifier/emacs/client.rb
|
76
78
|
- lib/notiffany/notifier/file.rb
|
77
79
|
- lib/notiffany/notifier/gntp.rb
|
78
80
|
- lib/notiffany/notifier/growl.rb
|
@@ -82,6 +84,9 @@ files:
|
|
82
84
|
- lib/notiffany/notifier/terminal_notifier.rb
|
83
85
|
- lib/notiffany/notifier/terminal_title.rb
|
84
86
|
- lib/notiffany/notifier/tmux.rb
|
87
|
+
- lib/notiffany/notifier/tmux/client.rb
|
88
|
+
- lib/notiffany/notifier/tmux/notification.rb
|
89
|
+
- lib/notiffany/notifier/tmux/session.rb
|
85
90
|
- lib/notiffany/version.rb
|
86
91
|
homepage: https://github.com/guard/notiffany
|
87
92
|
licenses:
|
@@ -103,7 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
103
108
|
version: '0'
|
104
109
|
requirements: []
|
105
110
|
rubyforge_project:
|
106
|
-
rubygems_version: 2.
|
111
|
+
rubygems_version: 2.5.1
|
107
112
|
signing_key:
|
108
113
|
specification_version: 4
|
109
114
|
summary: Notifier library (extracted from Guard project)
|