thyme 0.0.14 → 0.0.15

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: 0a34024e5c3c57ea62c834ea83adf0c118b3fad2
4
- data.tar.gz: b0e6459d5fb05917846384d5296c7b4da76d0ce1
3
+ metadata.gz: 2bd7f6b22e1eb3fb5b2cc353f25948207113f722
4
+ data.tar.gz: 9de5a45b3b793806536927db3e4464d157e5ab95
5
5
  SHA512:
6
- metadata.gz: bfd282a9ff8df019c9c58b4add8c98bdc7e08e37a71cf4ea980bdacd366cbc9142450fcd9b062e783a4d4e5c0735c3a3ceb35a2c6b6a0c05c0f1d913a2d8b5b9
7
- data.tar.gz: 82391d021bf3735a7e34be5a354de37cd62e104664886ead5e03af649a5b2849cac58c16226f9ab6d6472a1aea4ecd458a45929c29dc2d790717abe58f23d53c
6
+ metadata.gz: 3d1a24078f5c3d2ce251ef8c2b04010e32140a1206548dfec3d4771179ae2a7bf6c8eb890b1106a0db7c855929afb5e4c1057e4ea4e7fc7c6c9b67bb8fc89bb9
7
+ data.tar.gz: 7167e5a067fc49605767fd18a9d13b05a6d430b56a1b809f6c270894897d300ad216441939bfb66a42300354ad8cfed78e4e5bb5cd6bee5a2a99dadf3c7889ae
data/README.md CHANGED
@@ -1,80 +1,76 @@
1
- Description
2
- ===========
1
+ # Description
3
2
 
4
3
  Thyme is a console pomodoro timer.
5
4
 
6
- Installation
7
- ============
5
+ # Installation
8
6
 
9
7
  $ gem install thyme
10
8
 
11
- Usage
12
- =====
9
+ # Usage
13
10
 
14
11
  Start thyme with:
15
12
 
16
13
  $ thyme
17
14
  [= ] 24:59
18
15
 
19
- You'll have 25 minutes by default. `Ctrl-C` to interrupt. You can also start
16
+ You'll have 25 minutes by default. `Ctrl-C` to interrupt. You can also start
20
17
  it in daemon mode, which is only useful if you've got tmux integration to notify
21
18
  you of the timer:
22
19
 
23
20
  $ thyme -d
24
21
 
25
- To interrupt the timer in daemon mode, run `thyme` again. Or wait 25 minutes
26
- for it to kill itself.
22
+ Some other useful commands:
27
23
 
28
- Configure
29
- =========
24
+ $ thyme # run again to pause/unpause
25
+ $ thyme -s # stops daemon
26
+ $ thyme -d -r # repeats timer until you manually stop it
27
+ $ thyme -d -r 10 # repeats timer exactly 10 times
30
28
 
31
- Thyme is configurable and extensible. All configurations live in the
32
- `~/.thymerc` file:
29
+ # Configure
33
30
 
34
- set :timer, 25*60
35
- set :timer_break, 5*60
36
- set :warning, 5*60
37
- set :warning_color, "red,bold"
38
- set :interval, 1
39
- set :tmux, true
31
+ Configurations live in the `~/.thymerc` file:
32
+
33
+ set :timer, 25*60 # 25 minute pomodoros
34
+ set :timer_break, 5*60 # 5 minute breaks
35
+ set :warning, 5*60 # show warning color in tmux at <5 minutes, 0 to disable
36
+ set :warning_color, 'red,bold' # warning color for tmux is red/bold
37
+ set :break_color, 'blue' # break color is blue
38
+ set :interval, 1 # refresh timer every 1 second
39
+ set :tmux, true # turn on tmux integration
40
40
  set :tmux_theme, "#[fg=mycolor,bg=mycolor]#[fg=%s]%s#[fg=mycolor,bg=mycolor]"
41
41
 
42
+ # adds `-t --today` option, which opens a text file in vim
42
43
  option :t, :today, 'open today sheet' do
43
44
  `vim -O ~/.thyme-today.md ~/.thyme-records.md < \`tty\` > \`tty\``
44
45
  end
45
46
 
47
+ # adds `-s --seconds num` option, which allows on the fly timer
46
48
  option :s, 'seconds num', 'run with custom seconds' do |num|
47
- @timer = num.to_i
49
+ set :timer, num.to_i
48
50
  run
49
51
  end
50
52
 
51
- before do
53
+ # execute hook before thyme program starts
54
+ before(:all) do
52
55
  `mplayer ~/music/flight-of-the-bumble-bee.mp3 &`
53
56
  end
54
57
 
55
- after do |seconds_left|
56
- `notify-send -u critical "Thyme's Up!"` if seconds_left == 0
58
+ # execute hook before each pomodoro
59
+ before do
60
+ `terminal-notifier -message "Let's get started!"`
57
61
  end
58
62
 
59
- The `set` method sets different configurations.
60
-
61
- * `:timer` seconds to countdown from
62
- * `:timer_break` seconds to countdown from in break mode
63
- * `:warning` seconds threshold before tmux timer turns red (use 0 to disable)
64
- * `:warning_color` color of the tmux timer during the warning period
65
- * `:interval` refresh rate of the progress bar and tmux status in seconds
66
- * `:tmux` whether or not you want tmux integration on (false by default)
67
- * `:tmux_theme` optionally lets you format the tmux status
68
-
69
- The `option` method adds new options to the `thyme` command. In the above
70
- example, we can now execute `thyme -b` or `thyme -t`. Use `thyme -h` to see
71
- available options.
63
+ # execute hook after each pomodoro
64
+ after do |seconds_left|
65
+ `terminal-notifier -message "Thyme's Up!"` if seconds_left == 0
66
+ end
72
67
 
73
- The `before` and `after` adds hooks to our timer. Now before the timer starts,
74
- an mp3 will play. After the timer ends, a notification will be sent.
68
+ # execute hook after thyme program quits
69
+ after(:all) do
70
+ `mplayer ~/music/victory.mp3 &`
71
+ end
75
72
 
76
- Integration
77
- ===========
73
+ # Tmux
78
74
 
79
75
  For tmux integration, make sure to set the `:tmux` option in `~/.thymerc`:
80
76
 
@@ -85,25 +81,28 @@ Then in your `.tmux.conf` file:
85
81
  set-option -g status-right '#(cat ~/.thyme-tmux)'
86
82
  set-option -g status-interval 1
87
83
 
88
- For vim integration, I like to execute `thyme -d` to toggle the timer. This only
84
+ For vim integration, I like to execute `thyme -d` to toggle the timer. This only
89
85
  works if you have tmux integration setup for the countdown:
90
86
 
91
87
  nmap <leader>t :!thyme -d<cr>
92
88
 
93
- Plugins
94
- =======
89
+ # Plugins
95
90
 
96
- Thyme's functionality can also be extended with plugins. They'll usually follow this
97
- format:
91
+ Thyme's functionality can also be extended with plugins. They'll usually be installed
92
+ in `~/.thymerc` like this:
98
93
 
99
- require "thyme_growl"
100
- use ThymeGrowl, text: "Go take a break!"
94
+ require 'thyme_growl'
95
+ use ThymeGrowl, text: 'Go take a break!'
101
96
 
102
- You can create your own plugins. They implement these methods:
97
+ You can create your own plugins. They implement these methods:
103
98
 
104
99
  class MyThymePlugin
105
100
  def initialize(thyme, options={})
106
- # `thyme` is an instance of Thyme (see lib/thyme.rb)
101
+ # `thyme` is an instance of Thyme::Config (see lib/thyme/config.rb)
102
+ end
103
+
104
+ def before_all
105
+ # code to run when thyme starts up
107
106
  end
108
107
 
109
108
  def before
@@ -117,12 +116,15 @@ You can create your own plugins. They implement these methods:
117
116
  def after(seconds_left)
118
117
  # code to run when timer stops
119
118
  end
119
+
120
+ def after_all
121
+ # code to run when thyme program ends
122
+ end
120
123
  end
121
124
 
122
- The `before`, `tick`, and `after` methods are all optional.
125
+ The `before_all`, `before`, `tick`, `after`, and `after_all` methods are all optional.
123
126
 
124
- License
125
- =======
127
+ # License
126
128
 
127
129
  Copyright Hugh Bien - http://hughbien.com.
128
130
  Released under BSD License, see LICENSE.md for more info.
data/bin/thyme CHANGED
@@ -1,17 +1,19 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'optparse'
3
- require File.expand_path('thyme', File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require_relative '../lib/thyme'
4
4
 
5
5
  $0 = 'thyme'
6
6
  ARGV.options do |o|
7
- thyme = Thyme.new
7
+ thyme = Thyme::Console.new
8
8
  o.set_summary_indent(' ')
9
- o.banner = "Usage: #{File.basename($0)} [OPTION]"
9
+ o.banner = "Usage: #{File.basename($0)} [OPTION]"
10
10
  o.define_head "Timer for Pomodoro Technique"
11
11
  o.on('-b', '--break', 'run break timer') { thyme.break! }
12
12
  o.on('-d', '--daemon', 'run in background') { thyme.daemonize! }
13
13
  o.on('-h', '--help', 'show this help message') { puts o; exit }
14
- thyme.load_config(o)
14
+ o.on('-r', '--repeat [COUNT]', 'repeat timer') { |count| thyme.repeat!(count) }
15
+ o.on('-s', '--stop', 'stops running timer') { thyme.stop; exit }
16
+ thyme.load(o)
15
17
  o.parse!
16
- thyme.run(true)
18
+ thyme.run
17
19
  end
@@ -1,205 +1,10 @@
1
- require 'ruby-progressbar'
2
1
  require 'date'
3
-
4
- class Thyme
5
- VERSION = '0.0.14'
6
- CONFIG_FILE = "#{ENV['HOME']}/.thymerc"
7
- PID_FILE = "#{ENV['HOME']}/.thyme-pid"
8
- TMUX_FILE = "#{ENV['HOME']}/.thyme-tmux"
9
- OPTIONS = [:interval, :timer, :timer_break, :tmux, :tmux_theme, :warning, :warning_color]
10
-
11
- def initialize
12
- @break = false
13
- @interval = 1
14
- @timer = 25 * 60
15
- @timer_break = 5 * 60
16
- @tmux = false
17
- @tmux_theme = "#[default]#[fg=%s]%s#[default]"
18
- @warning = 5 * 60
19
- @warning_color = "red,bold"
20
- @plugins = []
21
- end
22
-
23
- def use(plugin_class, *args, &block)
24
- @plugins << plugin_class.new(self, *args, &block)
25
- end
26
-
27
- def run(force=false)
28
- if force
29
- running? ? stop_timer : start_timer
30
- else
31
- @run = true
32
- end
33
- end
34
-
35
- def break!
36
- @break = true
37
- end
38
-
39
- def daemonize!
40
- @daemon = true
41
- Process.daemon
42
- end
43
-
44
- def daemon?
45
- !!@daemon
46
- end
47
-
48
- def set(opt, val)
49
- raise ThymeError.new("Invalid option: #{opt}") if !OPTIONS.include?(opt.to_sym)
50
- self.instance_variable_set("@#{opt}", val)
51
- end
52
-
53
- def before(&block)
54
- hooks_plugin.add(:before, &block)
55
- end
56
-
57
- def after(&block)
58
- hooks_plugin.add(:after, &block)
59
- end
60
-
61
- def tick(&block)
62
- hooks_plugin.add(:tick, &block)
63
- end
64
-
65
- def option(optparse, short, long, desc, &block)
66
- optparse.on("-#{short}", "--#{long}", desc) do |*args|
67
- self.instance_exec(*args, &block)
68
- exit if !@run
69
- end
70
- end
71
-
72
- def load_config(optparse)
73
- return if !File.exists?(CONFIG_FILE)
74
- app = self
75
- environment = Class.new do
76
- define_method(:set) { |opt,val| app.set(opt,val) }
77
- define_method(:use) { |plugin,*args,&b| app.use(plugin,*args,&b) }
78
- define_method(:before) { |&block| app.before(&block) }
79
- define_method(:after) { |&block| app.after(&block) }
80
- define_method(:tick) { |&block| app.tick(&block) }
81
- define_method(:option) { |sh,lo,desc,&b| app.option(optparse,sh,lo,desc,&b) }
82
- end.new
83
- environment.instance_eval(File.read(CONFIG_FILE), CONFIG_FILE)
84
- end
85
-
86
- def running?
87
- File.exists?(PID_FILE)
88
- end
89
-
90
- private
91
-
92
- def start_timer
93
- File.open(PID_FILE, "w") { |f| f.print(Process.pid) }
94
- seconds_start = @break ? @timer_break : @timer
95
- seconds_left = seconds_start + 1
96
- start_time = DateTime.now
97
- min_length = (seconds_left / 60).floor.to_s.length
98
- tmux_file = File.open(TMUX_FILE, "w") if @tmux
99
- started = false
100
- bar = ENV['THYME_TEST'].nil? && !daemon? ?
101
- ProgressBar.create(
102
- title: format(seconds_left-1, min_length),
103
- total: seconds_start,
104
- length: 50,
105
- format: '[%B] %t') : nil
106
- while seconds_left > 0
107
- seconds_passed = seconds_since(start_time)
108
- seconds_left = [seconds_start - seconds_passed, 0].max
109
- title = format(seconds_left, min_length)
110
- fg = color(seconds_left)
111
- if bar
112
- bar.title = title
113
- bar.progress = seconds_passed
114
- end
115
- if @tmux
116
- tmux_file.truncate(0)
117
- tmux_file.rewind
118
- tmux_file.write(@tmux_theme % [fg, title])
119
- tmux_file.flush
120
- end
121
- unless started
122
- started = true
123
- send_to_plugin :before
124
- end
125
- send_to_plugin :tick, seconds_left
126
- sleep(@interval)
127
- end
128
- rescue SignalException => e
129
- puts ""
130
- ensure
131
- tmux_file.close if tmux_file
132
- File.delete(TMUX_FILE) if File.exists?(TMUX_FILE)
133
- File.delete(PID_FILE) if File.exists?(PID_FILE)
134
- seconds_left = [seconds_start - seconds_since(start_time), 0].max
135
- send_to_plugin :after, seconds_left
136
- end
137
-
138
- def stop_timer
139
- pid = File.read(PID_FILE).to_i
140
- Process.kill('TERM', pid) if pid > 1
141
- rescue Errno::ESRCH # process is already dead, cleanup files and restart
142
- File.delete(TMUX_FILE) if File.exists?(TMUX_FILE)
143
- File.delete(PID_FILE) if File.exists?(PID_FILE)
144
- end
145
-
146
- def send_to_plugin(message, *args)
147
- @plugins.each do |plugin|
148
- begin
149
- plugin.public_send(message, *args) if plugin.respond_to?(message)
150
- rescue
151
- $stderr.puts "Exception raised from #{plugin.class}:", $!, $@
152
- end
153
- end
154
- end
155
-
156
- def hooks_plugin
157
- @hooks_plugin ||= begin
158
- use ThymeHooksPlugin
159
- @plugins.last
160
- end
161
- end
162
-
163
- def seconds_since(time)
164
- ((DateTime.now - time) * 24 * 60 * 60).to_i
165
- end
166
-
167
- def format(seconds, min_length)
168
- min = (seconds / 60).floor
169
- lead = ' ' * (min_length - min.to_s.length)
170
- sec = (seconds % 60).floor
171
- sec = "0#{sec}" if sec.to_s.length == 1
172
- @interval < 60 ?
173
- "#{lead}#{min}:#{sec}" :
174
- "#{lead}#{min}m"
175
- end
176
-
177
- def color(seconds)
178
- !@break && seconds < @warning ? @warning_color : 'default'
179
- end
180
- end
181
-
182
- class ThymeError < StandardError; end;
183
-
184
- class ThymeHooksPlugin
185
- def initialize(app)
186
- @app = app
187
- @hooks = {before: [], tick: [], after: []}
188
- end
189
-
190
- def add(type, &block)
191
- @hooks[type] << block
192
- end
193
-
194
- def before
195
- @hooks[:before].each { |b| @app.instance_exec(&b) }
196
- end
197
-
198
- def tick(seconds_left)
199
- @hooks[:tick].each { |t| @app.instance_exec(seconds_left, &t) }
200
- end
201
-
202
- def after(seconds_left)
203
- @hooks[:after].each { |a| @app.instance_exec(seconds_left, &a) }
204
- end
205
- end
2
+ require 'ruby-progressbar'
3
+ require_relative 'thyme/config'
4
+ require_relative 'thyme/console'
5
+ require_relative 'thyme/error'
6
+ require_relative 'thyme/format'
7
+ require_relative 'thyme/hooks_plugin'
8
+ require_relative 'thyme/timer'
9
+ require_relative 'thyme/tmux'
10
+ require_relative 'thyme/version'
@@ -0,0 +1,74 @@
1
+ module Thyme
2
+ class Config
3
+ CONFIG_FILE = "#{ENV['HOME']}/.thymerc"
4
+ PID_FILE = "#{ENV['HOME']}/.thyme-pid"
5
+ TMUX_FILE = "#{ENV['HOME']}/.thyme-tmux"
6
+ OPTIONS = [:break_color, :interval, :timer, :timer_break, :tmux, :tmux_theme, :warning, :warning_color]
7
+ OPTIONS.each { |opt| attr_reader(opt) }
8
+ attr_accessor :break, :daemon, :repeat, :repeat_index
9
+
10
+ def initialize
11
+ # options set via config file
12
+ @break_color = 'default'
13
+ @interval = 1
14
+ @timer = 25 * 60
15
+ @timer_break = 5 * 60
16
+ @tmux = false
17
+ @tmux_theme = "#[default]#[fg=%s]%s#[default]"
18
+ @warning = 5 * 60
19
+ @warning_color = 'red,bold'
20
+
21
+ # plugins set via config file
22
+ @plugins = []
23
+ @hooks_plugin = use(Thyme::HooksPlugin)
24
+
25
+ # settings via command line
26
+ @break = false
27
+ @daemon = false
28
+ @repeat = 1
29
+ @repeat_index = 1
30
+ end
31
+
32
+ def set(opt, val)
33
+ raise Thyme::Error.new("Invalid option: #{opt}") if !OPTIONS.include?(opt.to_sym)
34
+ self.instance_variable_set("@#{opt}", val)
35
+ end
36
+
37
+ def use(plugin_class, *args, &block)
38
+ plugin = plugin_class.new(self, *args, &block)
39
+ @plugins << plugin
40
+ plugin
41
+ end
42
+
43
+ def before(kind = :each, &block)
44
+ type = kind == :all ? :before_all : :before
45
+ @hooks_plugin.add(type, &block)
46
+ end
47
+
48
+ def after(kind = :each, &block)
49
+ type = kind == :all ? :after_all : :after
50
+ @hooks_plugin.add(type, &block)
51
+ end
52
+
53
+ def tick(&block)
54
+ @hooks_plugin.add(:tick, &block)
55
+ end
56
+
57
+ def option(optparse, short, long, desc, &block)
58
+ optparse.on("-#{short}", "--#{long}", desc) do |*args|
59
+ self.instance_exec(*args, &block)
60
+ exit if !@run
61
+ end
62
+ end
63
+
64
+ def send_to_plugin(message, *args)
65
+ @plugins.each do |plugin|
66
+ begin
67
+ plugin.public_send(message, *args) if plugin.respond_to?(message)
68
+ rescue
69
+ $stderr.puts "Exception raised from #{plugin.class}:", $!, $@
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,55 @@
1
+ module Thyme
2
+ class Console
3
+ attr_accessor :config
4
+
5
+ def initialize
6
+ @config = Config.new
7
+ end
8
+
9
+ def break!
10
+ @config.break = true
11
+ end
12
+
13
+ def daemonize!
14
+ @config.daemon = true
15
+ Process.daemon if !ENV['THYME_TEST']
16
+ end
17
+
18
+ def repeat!(count = 0)
19
+ @config.repeat = count.to_i
20
+ end
21
+
22
+ def stop
23
+ timer.stop
24
+ end
25
+
26
+ def run
27
+ timer.run
28
+ end
29
+
30
+ def load(optparse, &block)
31
+ return if block.nil? && !File.exists?(Config::CONFIG_FILE)
32
+ config = @config
33
+ environment = Class.new do
34
+ define_method(:set) { |opt,val| config.set(opt,val) }
35
+ define_method(:use) { |plugin,*args,&b| config.use(plugin,*args,&b) }
36
+ define_method(:before) { |*args,&block| config.before(*args,&block) }
37
+ define_method(:after) { |*args,&block| config.after(*args,&block) }
38
+ define_method(:tick) { |&block| config.tick(&block) }
39
+ define_method(:option) { |sh,lo,desc,&b| config.option(optparse,sh,lo,desc,&b) }
40
+ end.new
41
+
42
+ if block # for test environment
43
+ environment.instance_eval(&block)
44
+ else
45
+ environment.instance_eval(File.read(Config::CONFIG_FILE), Config::CONFIG_FILE)
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def timer
52
+ Timer.new(@config)
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,4 @@
1
+ module Thyme
2
+ class Error < StandardError; end
3
+ class StopTimer < StandardError; end
4
+ end
@@ -0,0 +1,41 @@
1
+ module Thyme
2
+ class Format
3
+ def initialize(config)
4
+ @config = config
5
+ end
6
+
7
+ def seconds_since(time)
8
+ ((DateTime.now - time) * 24 * 60 * 60).to_i
9
+ end
10
+
11
+ def time_left(seconds, min_length)
12
+ min = (seconds / 60).floor
13
+ lead = ' ' * [0, min_length - min.to_s.length].max
14
+ sec = (seconds % 60).floor
15
+ sec = "0#{sec}" if sec.to_s.length == 1
16
+ @config.interval < 60 ?
17
+ "#{lead}#{min}:#{sec} #{repeat_subtitle}".sub(/\s*$/, '') :
18
+ "#{lead}#{min}m #{repeat_subtitle}".sub(/\s*$/, '')
19
+ end
20
+
21
+ def repeat_subtitle
22
+ if @config.repeat == 1
23
+ ''
24
+ elsif @config.repeat == 0
25
+ "(#{@config.repeat_index})"
26
+ else
27
+ "(#{@config.repeat_index}/#{@config.repeat})"
28
+ end
29
+ end
30
+
31
+ def tmux_color(seconds)
32
+ if @config.break
33
+ @config.break_color
34
+ elsif seconds < @config.warning
35
+ @config.warning_color
36
+ else
37
+ 'default'
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,32 @@
1
+ module Thyme
2
+ class HooksPlugin
3
+ def initialize(config)
4
+ @config = config
5
+ @hooks = {before_all: [], before: [], tick: [], after: [], after_all: []}
6
+ end
7
+
8
+ def add(type, &block)
9
+ @hooks[type] << block
10
+ end
11
+
12
+ def before_all
13
+ @hooks[:before_all].each { |b| @config.instance_exec(&b) }
14
+ end
15
+
16
+ def before
17
+ @hooks[:before].each { |b| @config.instance_exec(&b) }
18
+ end
19
+
20
+ def tick(seconds_left)
21
+ @hooks[:tick].each { |t| @config.instance_exec(seconds_left, &t) }
22
+ end
23
+
24
+ def after(seconds_left)
25
+ @hooks[:after].each { |a| @config.instance_exec(seconds_left, &a) }
26
+ end
27
+
28
+ def after_all
29
+ @hooks[:after_all].each { |a| @config.instance_exec(&a) }
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,120 @@
1
+ module Thyme
2
+ class Timer
3
+ def initialize(config)
4
+ @config = config
5
+ @format = Format.new(config)
6
+ @tmux = Tmux.new(config)
7
+ end
8
+
9
+ def stop
10
+ send_signal('TERM')
11
+ end
12
+
13
+ def run
14
+ # pause/unpause timer if it's already running
15
+ send_signal('USR1') and return if File.exists?(Config::PID_FILE)
16
+
17
+ begin
18
+ File.open(Config::PID_FILE, "w") { |f| f.print(Process.pid) }
19
+ @tmux.open
20
+ if @config.repeat == 1
21
+ run_single
22
+ else
23
+ while @config.repeat_index <= @config.repeat || @config.repeat == 0
24
+ @config.break = false
25
+ run_single
26
+ if @config.repeat_index < @config.repeat || @config.repeat == 0
27
+ @config.break = true
28
+ run_single
29
+ end
30
+ @config.repeat_index += 1
31
+ end
32
+ end
33
+ rescue Thyme::StopTimer
34
+ # stop signal received
35
+ ensure
36
+ @tmux.close
37
+ File.delete(Config::PID_FILE) if File.exists?(Config::PID_FILE)
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def run_single
44
+ seconds_total = @config.break ? @config.timer_break : @config.timer
45
+ seconds_left = seconds_total + 1
46
+ start_time = DateTime.now
47
+ paused_time = nil
48
+ min_length = (seconds_left / 60).floor.to_s.length
49
+ started = false
50
+ @bar ||= ENV['THYME_TEST'].nil? && !@config.daemon ?
51
+ ProgressBar.create(
52
+ title: @format.time_left(seconds_left-1, min_length),
53
+ total: seconds_total,
54
+ length: 50,
55
+ format: '[%B] %t') : nil
56
+ @bar.reset if @bar
57
+ while seconds_left > 0
58
+ begin
59
+ if paused_time
60
+ sleep(@config.interval)
61
+ next
62
+ end
63
+ seconds_passed = @format.seconds_since(start_time)
64
+ seconds_left = [seconds_total - seconds_passed, 0].max
65
+ title = @format.time_left(seconds_left, min_length)
66
+ if @bar
67
+ @bar.title = title
68
+ if seconds_left == 0 && !last?
69
+ @bar.progress = seconds_passed - 0.01 # prevent bar from finishing
70
+ else
71
+ @bar.progress = seconds_passed
72
+ end
73
+ end
74
+ @tmux.tick(@format.tmux_color(seconds_left), title)
75
+ unless started
76
+ started = true
77
+ @config.send_to_plugin(:before_all) if first?
78
+ @config.send_to_plugin(:before)
79
+ end
80
+ @config.send_to_plugin(:tick, seconds_left)
81
+ sleep(@config.interval)
82
+ rescue SignalException => e
83
+ if e.signm == 'SIGUSR1' && paused_time.nil?
84
+ paused_time = DateTime.now
85
+ elsif e.signm == 'SIGUSR1'
86
+ delta = DateTime.now - paused_time
87
+ start_time += delta
88
+ paused_time = nil
89
+ else
90
+ puts ""
91
+ @interrupted = true
92
+ raise Thyme::StopTimer
93
+ end
94
+ end
95
+ end
96
+ ensure
97
+ seconds_left = [seconds_total - @format.seconds_since(start_time), 0].max
98
+ @config.send_to_plugin(:after, seconds_left)
99
+ @config.send_to_plugin(:after_all) if @interrupted || last?
100
+ end
101
+
102
+ def first?
103
+ @config.repeat == 1 || (!@config.break && @config.repeat_index == 1)
104
+ end
105
+
106
+ def last?
107
+ @config.repeat == @config.repeat_index
108
+ end
109
+
110
+ def send_signal(signal)
111
+ pid = File.read(Config::PID_FILE).to_i
112
+ Process.kill(signal, pid) if pid > 1
113
+ rescue Errno::ESRCH, Errno::ENOENT # process is already dead, cleanup files
114
+ File.delete(Config::TMUX_FILE) if File.exists?(Config::TMUX_FILE)
115
+ File.delete(Config::PID_FILE) if File.exists?(Config::PID_FILE)
116
+ ensure
117
+ true
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,29 @@
1
+ module Thyme
2
+ class Tmux
3
+ def initialize(config)
4
+ @config = config
5
+ end
6
+
7
+ def open
8
+ return if !@config.tmux
9
+ @tmux_file = File.open(Config::TMUX_FILE, "w")
10
+ @tmux_file.truncate(0)
11
+ @tmux_file.rewind
12
+ @tmux_file.write('')
13
+ @tmux_file.flush
14
+ end
15
+
16
+ def tick(color, title)
17
+ return if !@tmux_file
18
+ @tmux_file.truncate(0)
19
+ @tmux_file.rewind
20
+ @tmux_file.write(@config.tmux_theme % [color, title])
21
+ @tmux_file.flush
22
+ end
23
+
24
+ def close
25
+ @tmux_file.close if @tmux_file
26
+ File.delete(Config::TMUX_FILE) if File.exists?(Config::TMUX_FILE)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,3 @@
1
+ module Thyme
2
+ VERSION = '0.0.15'
3
+ end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thyme
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.14
4
+ version: 0.0.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hugh Bien
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-19 00:00:00.000000000 Z
11
+ date: 2015-11-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-progressbar
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: '1.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: '1.0'
27
27
  description: Extensible and configurable timer for Pomodoro Technique.
28
28
  email:
29
29
  - hugh@hughbien.com
@@ -36,8 +36,17 @@ files:
36
36
  - README.md
37
37
  - bin/thyme
38
38
  - lib/thyme.rb
39
- homepage: http://thymerb.com
40
- licenses: []
39
+ - lib/thyme/config.rb
40
+ - lib/thyme/console.rb
41
+ - lib/thyme/error.rb
42
+ - lib/thyme/format.rb
43
+ - lib/thyme/hooks_plugin.rb
44
+ - lib/thyme/timer.rb
45
+ - lib/thyme/tmux.rb
46
+ - lib/thyme/version.rb
47
+ homepage: http://hughbien.com/thyme/
48
+ licenses:
49
+ - BSD
41
50
  metadata: {}
42
51
  post_install_message:
43
52
  rdoc_options: []
@@ -55,7 +64,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
55
64
  version: 1.3.6
56
65
  requirements: []
57
66
  rubyforge_project:
58
- rubygems_version: 2.2.2
67
+ rubygems_version: 2.4.5
59
68
  signing_key:
60
69
  specification_version: 4
61
70
  summary: Timer for Pomodoro Technique