notiffany 0.0.8 → 0.1.0

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