slimtimercli 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/bin/slimtimer +4 -6
- data/lib/slimtimercli.rb +214 -112
- data/lib/slimtimercli/version.rb +1 -1
- data/spec/slimtimercli_spec.rb +71 -19
- data/spec/spec_helper.rb +13 -3
- metadata +2 -2
data/History.txt
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
== 0.1.3 2008-04-07
|
2
|
+
* fixed bug that allows starting a nonexisting task
|
3
|
+
* refactored options to start slimtimer
|
4
|
+
|
1
5
|
== 0.1.2 2008-04-06
|
2
6
|
* time recording can only be stopped if a task was started
|
3
7
|
* time recording can only be started if no other task was started
|
data/bin/slimtimer
CHANGED
data/lib/slimtimercli.rb
CHANGED
@@ -6,7 +6,9 @@ require 'net/http'
|
|
6
6
|
require 'rubygems'
|
7
7
|
require 'active_record'
|
8
8
|
require 'active_support'
|
9
|
-
require 'yaml'
|
9
|
+
require 'yaml'
|
10
|
+
require 'optparse'
|
11
|
+
require 'ostruct'
|
10
12
|
|
11
13
|
require "slimtimercli/entities"
|
12
14
|
require "slimtimercli/slim_timer"
|
@@ -14,7 +16,7 @@ require "slimtimercli/version"
|
|
14
16
|
|
15
17
|
module Slimtimercli
|
16
18
|
module Helper
|
17
|
-
def
|
19
|
+
def login
|
18
20
|
config = Helper::load_config
|
19
21
|
st = SlimTimer.new(config["email"], config["password"],
|
20
22
|
config["api_key"])
|
@@ -23,23 +25,23 @@ module Slimtimercli
|
|
23
25
|
st
|
24
26
|
end
|
25
27
|
|
26
|
-
def
|
28
|
+
def root
|
27
29
|
File.join(ENV["HOME"], ".slimtimer")
|
28
30
|
end
|
29
31
|
|
30
|
-
def
|
32
|
+
def config_file
|
31
33
|
File.join(root, "config.yml")
|
32
34
|
end
|
33
35
|
|
34
|
-
def
|
36
|
+
def tasks_file
|
35
37
|
File.join(root, "tasks.yml")
|
36
38
|
end
|
37
39
|
|
38
|
-
def
|
40
|
+
def current_file
|
39
41
|
File.join(root, "current.yml")
|
40
42
|
end
|
41
43
|
|
42
|
-
def
|
44
|
+
def check_and_create_dir
|
43
45
|
raise "Home DIR not set!" unless ENV["HOME"]
|
44
46
|
|
45
47
|
unless File.directory?(root)
|
@@ -47,7 +49,7 @@ module Slimtimercli
|
|
47
49
|
end
|
48
50
|
end
|
49
51
|
|
50
|
-
def
|
52
|
+
def load_config
|
51
53
|
check_and_create_dir
|
52
54
|
|
53
55
|
unless File.exists?(File.join(root, "config.yml"))
|
@@ -58,89 +60,76 @@ module Slimtimercli
|
|
58
60
|
load_file("config.yml")
|
59
61
|
end
|
60
62
|
|
61
|
-
def
|
63
|
+
def save_config(config)
|
62
64
|
dump_to_file(config, "config.yml")
|
63
65
|
end
|
64
66
|
|
65
|
-
def
|
67
|
+
def load_file(file)
|
66
68
|
File.open( File.join(root, file) ) { |yf| YAML::load( yf ) }
|
67
69
|
end
|
68
70
|
|
69
|
-
def
|
71
|
+
def dump_to_file(object, file)
|
70
72
|
check_and_create_dir
|
71
73
|
File.open( File.join(root, file), 'w' ) do |out|
|
72
74
|
YAML.dump(object, out )
|
73
75
|
end
|
74
76
|
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def self.create_task
|
78
|
-
name = ARGV[1]
|
79
|
-
|
80
|
-
st = Helper::login
|
81
|
-
if st.create_task(name)
|
82
|
-
Helper::dump_to_file(st.tasks, "tasks.yml")
|
83
|
-
puts "Task #{name} successfully created."
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def self.tasks(show= true)
|
88
|
-
config = Helper::load_config
|
89
|
-
st = SlimTimer.new(config["email"], config["password"],
|
90
|
-
config["api_key"])
|
91
|
-
|
92
|
-
if !File.exists?(Helper::tasks_file) ||
|
93
|
-
File.mtime(Helper::tasks_file) < (Time.now - 60 * 60 *24)
|
94
|
-
|
95
|
-
st.login
|
96
|
-
Helper::dump_to_file(st.tasks, "tasks.yml")
|
97
|
-
end
|
98
77
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
tasks.each do |t|
|
104
|
-
puts t.name
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
def self.force_reload
|
109
|
-
config = Helper::load_config
|
110
|
-
st = SlimTimer.new(config["email"], config["password"],
|
111
|
-
config["api_key"])
|
112
|
-
|
113
|
-
st.login
|
114
|
-
Helper::dump_to_file(st.tasks, "tasks.yml")
|
115
|
-
tasks = Helper::load_file("tasks.yml")
|
116
|
-
|
117
|
-
tasks.each do |t|
|
118
|
-
puts t.name
|
78
|
+
def rm_current
|
79
|
+
FileUtils.rm(current_file) if
|
80
|
+
File.exists?(current_file)
|
119
81
|
end
|
120
|
-
end
|
121
82
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
83
|
+
def parse(args)
|
84
|
+
|
85
|
+
if !args || args.empty?
|
86
|
+
raise "Need to specify arguments, run slimtimer -h for help"
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
options = OpenStruct.new
|
91
|
+
options.force = false
|
92
|
+
|
93
|
+
opts = OptionParser.new do |opts|
|
94
|
+
|
95
|
+
opts.banner = "Usage: slimtimer [options]"
|
96
|
+
|
97
|
+
opts.on("-s TASK", "--start TASK",
|
98
|
+
"Start a TASK given by the task name") do |t|
|
99
|
+
|
100
|
+
options.run = "start"
|
101
|
+
options.task_name = t
|
102
|
+
end
|
103
|
+
|
104
|
+
opts.on("-c TASK", "--create TASK",
|
105
|
+
"Create a ne task by the given name") do |t|
|
106
|
+
options.run = "create"
|
107
|
+
options.task_name = t
|
108
|
+
end
|
109
|
+
|
110
|
+
opts.on("-e", "--end" ,"Stops time recording for the given task") do
|
111
|
+
options.run = "stop"
|
112
|
+
end
|
113
|
+
|
114
|
+
opts.on("-t", "--tasks", "Prints all available tasks") do
|
115
|
+
options.run = "tasks"
|
116
|
+
end
|
117
|
+
|
118
|
+
opts.on("-f", "--force", "Force deletion of tasks") do
|
119
|
+
options.force = true
|
120
|
+
end
|
121
|
+
|
122
|
+
opts.on("--setup", "Setup your account") do
|
123
|
+
options.run = "setup"
|
124
|
+
end
|
125
|
+
|
126
|
+
opts.on_tail("-h", "Shows this note") do
|
127
|
+
puts opts
|
128
|
+
exit
|
129
|
+
end
|
130
|
+
|
131
|
+
opts.on("--help", "Show verbose help") do
|
132
|
+
@out.puts <<-HELP
|
144
133
|
SlimTimer is a tool to record your time spend on a
|
145
134
|
task. SlimTimer CLI allows you to controll your
|
146
135
|
SlimTimer directly from where you spend most of your
|
@@ -174,53 +163,166 @@ Finally you can run
|
|
174
163
|
|
175
164
|
To show all your tasks available.
|
176
165
|
HELP
|
166
|
+
exit
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
begin
|
171
|
+
opts.parse!(args)
|
172
|
+
rescue
|
173
|
+
puts $!.message
|
174
|
+
exit
|
175
|
+
end
|
176
|
+
options
|
177
|
+
end
|
177
178
|
end
|
178
179
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
180
|
+
class CommandLine
|
181
|
+
|
182
|
+
# Include Helper module
|
183
|
+
include Helper
|
184
|
+
|
185
|
+
def initialize(args, output = $stdout)
|
186
|
+
@args = args
|
187
|
+
@out = output
|
188
|
+
|
189
|
+
deprecated_calls
|
190
|
+
|
191
|
+
@options = parse(args)
|
183
192
|
end
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
193
|
+
|
194
|
+
def create
|
195
|
+
st = login
|
196
|
+
if st.create_task(@options.task_name)
|
197
|
+
dump_to_file(st.tasks, "tasks.yml")
|
198
|
+
@out.puts "Task #{name} successfully created."
|
199
|
+
end
|
188
200
|
end
|
189
201
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
# dum curent task to file
|
194
|
-
Helper::dump_to_file(info, "current.yml")
|
195
|
-
return true
|
196
|
-
end
|
202
|
+
def tasks(show = true)
|
203
|
+
tasks = load_tasks
|
204
|
+
return tasks unless show
|
197
205
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
rescue
|
202
|
-
puts "You must start a task before you finish it"
|
203
|
-
return false
|
206
|
+
tasks.each do |t|
|
207
|
+
@out.puts t.name
|
208
|
+
end
|
204
209
|
end
|
210
|
+
|
211
|
+
def setup
|
212
|
+
config = load_config
|
213
|
+
|
214
|
+
@out.puts "Slimtimer Login Credentials\n"
|
215
|
+
@out.print "E-Mail: "
|
216
|
+
config["email"] = STDIN.gets.gsub("\n", "")
|
217
|
+
|
218
|
+
@out.print "Password: "
|
219
|
+
config["password"] = STDIN.gets.gsub("\n", "")
|
205
220
|
|
221
|
+
@out.print "API Key: "
|
222
|
+
config["api_key"] = STDIN.gets.gsub("\n", "")
|
206
223
|
|
207
|
-
|
208
|
-
t = tasks(false).find {|t| t.name == info["task"]}
|
224
|
+
save_config(config)
|
209
225
|
|
210
|
-
|
226
|
+
# clear the screen
|
227
|
+
system("clear")
|
228
|
+
end
|
229
|
+
|
230
|
+
def start
|
231
|
+
if File.exists?(current_file)
|
232
|
+
@out.puts "Need to stop the other task first"
|
233
|
+
return false
|
234
|
+
end
|
235
|
+
|
236
|
+
info = {"task" => @options.task_name,
|
237
|
+
"start_time" => Time.now}
|
238
|
+
|
239
|
+
#Find task in tasks yml
|
240
|
+
t = load_tasks.find {|t| t.name == info["task"]}
|
241
|
+
unless t
|
242
|
+
@out.puts "Task not found in list. Reload List?"
|
243
|
+
return false
|
244
|
+
end
|
211
245
|
|
212
|
-
|
213
|
-
|
214
|
-
|
246
|
+
dump_to_file(info, "current.yml")
|
247
|
+
return true
|
248
|
+
end
|
249
|
+
|
250
|
+
def stop
|
215
251
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
252
|
+
if @options.force
|
253
|
+
rm_current
|
254
|
+
@out.puts "Forced ending of task, no entry to slimtimer.com written"
|
255
|
+
return true
|
256
|
+
end
|
257
|
+
|
258
|
+
|
259
|
+
begin
|
260
|
+
info = load_file("current.yml")
|
261
|
+
rescue
|
262
|
+
puts "You must start a task before you finish it"
|
263
|
+
return false
|
264
|
+
end
|
220
265
|
|
221
|
-
|
222
|
-
|
223
|
-
|
266
|
+
#Find task in tasks yml
|
267
|
+
t = load_tasks.find {|t| t.name == info["task"]}
|
268
|
+
unless t
|
269
|
+
@out.puts "Task not found in list. Reload List?"
|
270
|
+
return false
|
271
|
+
end
|
272
|
+
raise unless t
|
273
|
+
|
274
|
+
st = login
|
275
|
+
result = st.create_time_entry(t, info["start_time"],
|
276
|
+
(Time.now - info["start_time"]).to_i)
|
277
|
+
|
278
|
+
# Delete yml file
|
279
|
+
if result
|
280
|
+
rm_current
|
281
|
+
|
282
|
+
# Output
|
283
|
+
@out.puts "Wrote new Entry for #{t.name}, duration #{result["duration_in_seconds"] / 60}m"
|
284
|
+
return true
|
285
|
+
else
|
286
|
+
@out.puts "Coult not write new entry, please try again"
|
287
|
+
return false
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
def run
|
292
|
+
send(@options.run.to_sym)
|
293
|
+
end
|
294
|
+
|
295
|
+
alias_method :end, :stop
|
296
|
+
|
297
|
+
private
|
298
|
+
|
299
|
+
# This method checks if the first parameter in args needs to
|
300
|
+
# be transformed to the new one
|
301
|
+
def deprecated_calls
|
302
|
+
case @args[0]
|
303
|
+
when "start": @args[0] = "-s"
|
304
|
+
when "end": @args[0] = "-e"
|
305
|
+
when "create_task": @args[0] = "-c"
|
306
|
+
when "tasks": @args[0] = "-t"
|
307
|
+
when "setup": @args[0] = "--setup"
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
def load_tasks(force = false)
|
312
|
+
config = load_config
|
313
|
+
st = SlimTimer.new(config["email"], config["password"],
|
314
|
+
config["api_key"])
|
315
|
+
|
316
|
+
tasks = []
|
317
|
+
if !File.exists?(tasks_file) ||
|
318
|
+
File.mtime(tasks_file) < (Time.now - 60 * 60 *24) || force
|
319
|
+
st.login
|
320
|
+
tasks = st.tasks
|
321
|
+
dump_to_file(tasks, "tasks.yml")
|
322
|
+
else
|
323
|
+
tasks = load_file("tasks.yml")
|
324
|
+
end
|
325
|
+
tasks
|
326
|
+
end
|
224
327
|
end
|
225
|
-
|
226
328
|
end
|
data/lib/slimtimercli/version.rb
CHANGED
data/spec/slimtimercli_spec.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/spec_helper.rb'
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
2
|
|
3
|
+
include Slimtimercli::Helper
|
3
4
|
# Time to add your specs!
|
4
5
|
# http://rspec.rubyforge.org/
|
5
6
|
describe "SlimTimer" do
|
@@ -7,8 +8,11 @@ describe "SlimTimer" do
|
|
7
8
|
describe "Helper" do
|
8
9
|
|
9
10
|
it "should return the path to the config files" do
|
10
|
-
|
11
|
-
Slimtimercli::
|
11
|
+
|
12
|
+
st = Slimtimercli::CommandLine.new(["-e"])
|
13
|
+
|
14
|
+
st.config_file.should =~ /.slimtimer\/config.yml/
|
15
|
+
st.tasks_file.should =~ /.slimtimer\/tasks.yml/
|
12
16
|
end
|
13
17
|
|
14
18
|
end
|
@@ -49,44 +53,92 @@ describe "SlimTimer" do
|
|
49
53
|
describe "command line interface" do
|
50
54
|
|
51
55
|
before do
|
52
|
-
Slimtimercli::
|
56
|
+
Slimtimercli::CommandLine.
|
57
|
+
any_instance.stubs(:root).returns(File.dirname(__FILE__))
|
53
58
|
|
54
59
|
@c = File.join(File.dirname(__FILE__), "current.yml")
|
55
60
|
FileUtils.rm(@c) if File.exists?(@c)
|
56
61
|
|
57
62
|
@d = File.join(File.dirname(__FILE__), "config.yml")
|
58
63
|
FileUtils.rm(@d) if File.exists?(@d)
|
64
|
+
|
59
65
|
end
|
60
66
|
|
61
67
|
it "should start a task" do
|
62
|
-
|
63
|
-
|
64
|
-
Slimtimercli.
|
68
|
+
# Manipulate ARGV
|
69
|
+
|
70
|
+
lambda { Slimtimercli::CommandLine.new([]) }.should
|
71
|
+
raise_error(RuntimeError)
|
65
72
|
File.exists?(@c).should be_false
|
66
|
-
|
73
|
+
|
74
|
+
Slimtimercli::CommandLine.
|
75
|
+
any_instance.stubs(:load_tasks).
|
76
|
+
returns(stub("task", :find => stub("task", :name => "test")))
|
77
|
+
|
67
78
|
# Set a task
|
68
79
|
ARGV[1] = "test"
|
69
|
-
|
80
|
+
|
81
|
+
st = Slimtimercli::CommandLine.new(ARGV)
|
82
|
+
st.start
|
70
83
|
File.exists?(@c).should be_true
|
71
84
|
|
72
85
|
# no double start
|
73
|
-
|
74
|
-
|
75
|
-
|
86
|
+
st.start.should be_false
|
76
87
|
end
|
77
88
|
|
78
89
|
it "should stop a task" do
|
79
|
-
Slimtimercli.
|
80
|
-
|
81
|
-
|
90
|
+
Slimtimercli::CommandLine.
|
91
|
+
any_instance.stubs(:load_tasks).
|
92
|
+
returns(stub("task", :find => stub("task", :name => "test")))
|
93
|
+
|
94
|
+
Slimtimercli::CommandLine.any_instance.stubs(:login).
|
95
|
+
returns(stub("slimtimer",
|
96
|
+
:create_time_entry => {"duration_in_seconds" => 10}))
|
82
97
|
|
83
|
-
ARGV[1] = "test"
|
84
|
-
Slimtimercli.
|
85
|
-
|
98
|
+
ARGV[1] = "test"
|
99
|
+
st = Slimtimercli::CommandLine.new(ARGV)
|
100
|
+
st.start.should be_true
|
101
|
+
st.end.should be_true
|
102
|
+
|
103
|
+
File.exists?(@c).should be_false
|
86
104
|
end
|
87
105
|
|
88
106
|
it "should not stop a task if none is running" do
|
89
|
-
Slimtimercli.
|
107
|
+
st = Slimtimercli::CommandLine.new(["-e"])
|
108
|
+
st.end.should be_false
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should not start a task that does not exist" do
|
112
|
+
Slimtimercli::CommandLine.any_instance.
|
113
|
+
stubs(:load_tasks).returns(stub("task", :find => nil))
|
114
|
+
|
115
|
+
ARGV[1] = "not exisiting task"
|
116
|
+
st = Slimtimercli::CommandLine.new(ARGV)
|
117
|
+
st.start.should be_false
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should allow to force the deletion of the current task" do
|
122
|
+
st = Slimtimercli::CommandLine.new(["-e"])
|
123
|
+
st.end.should be_false
|
124
|
+
ARGV[0] = "-e"
|
125
|
+
ARGV[1] = "--force" || "-f"
|
126
|
+
st = Slimtimercli::CommandLine.new(ARGV)
|
127
|
+
st.end.should be_true
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
describe "option parser" do
|
133
|
+
|
134
|
+
it "should parse the start part correctly" do
|
135
|
+
|
136
|
+
args = ["--start", "my_task"]
|
137
|
+
options = parse(args)
|
138
|
+
|
139
|
+
options.run.should == "start"
|
140
|
+
options.task_name.should == "my_task"
|
141
|
+
|
90
142
|
end
|
91
143
|
|
92
144
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,10 +1,20 @@
|
|
1
1
|
begin
|
2
|
+
|
2
3
|
require 'spec'
|
4
|
+
require 'mocha'
|
5
|
+
|
3
6
|
rescue LoadError
|
4
7
|
require 'rubygems'
|
5
8
|
gem 'rspec'
|
6
|
-
require 'spec'
|
9
|
+
require 'spec'
|
10
|
+
|
11
|
+
gem 'mocha'
|
12
|
+
require 'mocha'
|
7
13
|
end
|
8
|
-
|
14
|
+
|
9
15
|
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
10
|
-
require 'slimtimercli'
|
16
|
+
require 'slimtimercli'
|
17
|
+
|
18
|
+
Spec::Runner.configure do |config|
|
19
|
+
config.mock_with :mocha
|
20
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: slimtimercli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Martin Grund
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-04-
|
12
|
+
date: 2008-04-07 00:00:00 +02:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|