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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e249148e6240ebc17cb04185a309de655ad91d40
4
- data.tar.gz: b72a152bc66de17f288d15a5f46a08722813808f
3
+ metadata.gz: 107a426a3f017b084ea3a11ac5a3c951b8569269
4
+ data.tar.gz: f4f275c620a69ff487e61b936cadfd2b1eebcbac
5
5
  SHA512:
6
- metadata.gz: 07ca999abf84cdf878447ee33eae1fcbbf1a25cdb8f712bcac2e6c32dc67eed94bbe1f31c836e2e14c61f08101522230c173076c6b0d1c57cbc89c67057768c6
7
- data.tar.gz: a1de551061d4684420135706a892ca913b60d9bdeb21323ed3fc5b84d8e954bd5e7f5d237bc16941d5d032ac4bb56f7fda3e422ace37c11116ce0518b49c7c5b
6
+ metadata.gz: 394ec7473db4997eaae759be74f386dc9b2acf4a65d60a9fb3196a6a817a7a21803330952701dee5a1327b7db52b30bc4f2fc9722d1320b5199c4e87bdd5b44d
7
+ data.tar.gz: d89a0a76ee60251e5b7c2efd089fa902b0f3974db6637e48d72b88e8b112f045311525415e24da1019783c14e561ec84f34afd78a7a72f7de8dcac2305f72ba0
data/README.md CHANGED
@@ -4,7 +4,7 @@ Notification library supporting popular notifiers, such as:
4
4
  - Growl
5
5
  - libnotify
6
6
  - TMux
7
- - Emacs
7
+ - Emacs (see: https://github.com/guard/notiffany/wiki/Emacs-support)
8
8
  - rb-notifu
9
9
  - notifysend
10
10
  - gntp
@@ -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
- DEFAULTS = { notify: true }
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
- def initialize(opts)
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
- @detected = Detected.new(SUPPORTED, @env_namespace, @logger)
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
- _env.notify_pid = $$
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
- @logger.info e.to_s
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
- silent = options[:silent]
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(@env_namespace)
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
@@ -38,7 +38,7 @@ module Notiffany
38
38
 
39
39
  class UnsupportedPlatform < UnavailableError
40
40
  def initialize
41
- super "Unsupported platform #{RbConfig::CONFIG["host_os"].inspect}"
41
+ super "Unsupported platform #{RbConfig::CONFIG['host_os'].inspect}"
42
42
  end
43
43
  end
44
44
 
@@ -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::dump(data || []) }
23
- create_method(:notifiers) { |data| data ? YAML::load(data) : [] }
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
- def name
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
- add(name, {})
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 "notiffany/notifier/base"
2
- require "shellany/sheller"
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: "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({ '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
- 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(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
- fail UnavailableError, "Emacs client failed"
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(_message, opts = {})
58
+ def _perform_notify(message, opts = {})
76
59
  color = _emacs_color(opts[:type], opts)
77
60
  fontcolor = _emacs_color(:fontcolor, opts)
78
- Client.new(opts).notify(fontcolor, color)
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
- format = opts[:format]
36
- ::File.write(opts[:path], format % [opts[:type], opts[:title], message])
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: 23053
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 cygwin)
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 << "-#{ flag }" << value.to_s) : cmd
84
+ supported.include?(flag) ? (cmd << "-#{flag}" << value.to_s) : cmd
85
85
  end
86
86
  end
87
87
  end
@@ -32,7 +32,7 @@ module Notiffany
32
32
  def _perform_notify(message, opts = {})
33
33
  first_line = message.sub(/^\n/, "").sub(/\n.*/m, "")
34
34
 
35
- STDOUT.puts "\e]2;[#{ opts[:title] }] #{ first_line }\a"
35
+ STDOUT.puts "\e]2;[#{opts[:title]}] #{first_line}\a"
36
36
  end
37
37
  end
38
38
  end
@@ -1,5 +1,8 @@
1
1
  require "notiffany/notifier/base"
2
- require "shellany/sheller"
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
- 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]
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
- 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
114
+ class << self
115
+ def _start_session
116
+ fail "Already turned on!" if @session
117
+ @session = Session.new
118
+ end
345
119
 
346
- def self._session
347
- @session
348
- end
120
+ def _end_session
121
+ fail "Already turned off!" unless @session
122
+ @session.close
123
+ @session = nil
124
+ end
349
125
 
350
- def client(options)
351
- options[:display_on_all_clients] ? :all : nil
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
@@ -1,3 +1,3 @@
1
1
  module Notiffany
2
- VERSION = "0.0.8"
2
+ VERSION = "0.1.0"
3
3
  end
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.8
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: 2015-09-15 00:00:00.000000000 Z
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.4.5
111
+ rubygems_version: 2.5.1
107
112
  signing_key:
108
113
  specification_version: 4
109
114
  summary: Notifier library (extracted from Guard project)