freshtrack 0.2.2 → 0.3.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.
- data/History.txt +5 -0
- data/Manifest.txt +1 -0
- data/README.txt +34 -12
- data/bin/freshtrack +28 -2
- data/config/hoe.rb +3 -3
- data/lib/freshtrack/version.rb +2 -2
- data/lib/freshtrack.rb +7 -10
- data/script/destroy +0 -0
- data/script/generate +0 -0
- data/spec/freshtrack_command_spec.rb +74 -0
- data/spec/freshtrack_spec.rb +33 -61
- metadata +21 -8
data/History.txt
CHANGED
data/Manifest.txt
CHANGED
data/README.txt
CHANGED
@@ -1,18 +1,36 @@
|
|
1
|
-
|
1
|
+
= freshtrack
|
2
2
|
|
3
|
-
|
4
|
-
freshtrack are passed along to punch as if freshtrack were an alias for 'punch list'.
|
3
|
+
== DESCRIPTION:
|
5
4
|
|
6
|
-
|
5
|
+
Freshtrack is used to automatically create time entries in FreshBooks from your own tracked time.
|
7
6
|
|
8
|
-
|
7
|
+
== FEATURES/PROBLEMS:
|
9
8
|
|
10
|
-
|
9
|
+
* Simple and easy to use
|
11
10
|
|
12
|
-
|
11
|
+
* Only works with data from one_inch_punch
|
13
12
|
|
13
|
+
== SYNOPSIS:
|
14
14
|
|
15
|
-
|
15
|
+
$ freshtrack proj
|
16
|
+
|
17
|
+
or
|
18
|
+
|
19
|
+
$ freshtrack proj --before 2008-08-16
|
20
|
+
|
21
|
+
or! (if you really want)
|
22
|
+
|
23
|
+
require 'freshtrack'
|
24
|
+
|
25
|
+
Freshtrack.init
|
26
|
+
Freshtrack.track('proj', :before => Time.parse('2008-08-16'))
|
27
|
+
|
28
|
+
== REQUIREMENTS:
|
29
|
+
|
30
|
+
* one_inch_punch (gem)
|
31
|
+
* freshbooks (gem)
|
32
|
+
* A FreshBooks account
|
33
|
+
* a configuration file located at ~/.freshtrack.yml and looking like
|
16
34
|
|
17
35
|
---
|
18
36
|
company: Company Name
|
@@ -22,9 +40,13 @@ Freshtrack requires a configuration file, ~/.freshtrack.yml, that looks somethin
|
|
22
40
|
:project: FreshBooks Project Name
|
23
41
|
:task: FreshBooks Task Name
|
24
42
|
|
25
|
-
The 'Company Name' is the XXX in 'XXX.freshbooks.com'. The 'project_name' is the XXX in 'punch list XXX'
|
43
|
+
(The 'Company Name' is the XXX in 'XXX.freshbooks.com'. The 'project_name' is the XXX in 'punch list XXX'.)
|
44
|
+
|
45
|
+
== INSTALL:
|
46
|
+
|
47
|
+
* gem install freshtrack
|
26
48
|
|
49
|
+
== THANKS:
|
27
50
|
|
28
|
-
|
29
|
-
|
30
|
-
of attributes is 5.0.0.
|
51
|
+
* Kevin Barnes and Rick Bradley, for giving me a reason to track time and invoice people for it
|
52
|
+
* The FreshBooks team, for making invoicing easy
|
data/bin/freshtrack
CHANGED
@@ -10,8 +10,34 @@ rescue LoadError
|
|
10
10
|
end
|
11
11
|
|
12
12
|
require 'freshtrack'
|
13
|
+
require 'optparse'
|
14
|
+
require 'time'
|
15
|
+
|
16
|
+
OPTIONS = {}
|
17
|
+
MANDATORY_OPTIONS = %w[]
|
18
|
+
|
19
|
+
parser = OptionParser.new do |opts|
|
20
|
+
opts.banner = <<BANNER
|
21
|
+
Usage: #{File.basename($0)} [options]
|
22
|
+
|
23
|
+
Options are:
|
24
|
+
BANNER
|
25
|
+
opts.separator ''
|
26
|
+
opts.on('-v', '--version',
|
27
|
+
"Show the #{File.basename($0)} version number and exit") { require 'freshtrack/version'; puts "#{File.basename($0)} #{Freshtrack::VERSION::STRING}"; exit }
|
28
|
+
opts.on('--after [TIME]', String,
|
29
|
+
"Restrict command to only after the given time") { |time| OPTIONS[:after] = Time.parse(time) }
|
30
|
+
opts.on('--before [TIME]', String,
|
31
|
+
"Restrict command to only before the given time") { |time| OPTIONS[:before] = Time.parse(time) }
|
32
|
+
opts.on("-h", "--help",
|
33
|
+
"Show this help message.") { puts opts; exit }
|
34
|
+
opts.parse!(ARGV)
|
35
|
+
|
36
|
+
if MANDATORY_OPTIONS && MANDATORY_OPTIONS.find { |option| OPTIONS[option.to_sym].nil? }
|
37
|
+
puts opts; exit
|
38
|
+
end
|
39
|
+
end
|
13
40
|
|
14
|
-
# do stuff
|
15
41
|
project = ARGV.shift
|
16
42
|
|
17
43
|
unless project
|
@@ -20,4 +46,4 @@ unless project
|
|
20
46
|
end
|
21
47
|
|
22
48
|
Freshtrack.init
|
23
|
-
Freshtrack.track(project,
|
49
|
+
Freshtrack.track(project, OPTIONS)
|
data/config/hoe.rb
CHANGED
@@ -2,7 +2,7 @@ require 'freshtrack/version'
|
|
2
2
|
|
3
3
|
AUTHOR = 'Yossef Mendelssohn' # can also be an array of Authors
|
4
4
|
EMAIL = 'ymendel@pobox.com'
|
5
|
-
DESCRIPTION = "
|
5
|
+
DESCRIPTION = "Track your time on FreshBooks"
|
6
6
|
GEM_NAME = 'freshtrack' # what ppl will type to install your gem
|
7
7
|
RUBYFORGE_PROJECT = 'yomendel' # The unix name for your project
|
8
8
|
HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
|
@@ -60,8 +60,8 @@ hoe = Hoe.new(GEM_NAME, VERS) do |p|
|
|
60
60
|
# == Optional
|
61
61
|
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
62
62
|
p.extra_deps = [ # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
|
63
|
-
['freshbooks', '
|
64
|
-
['
|
63
|
+
['freshbooks', '= 2.1'],
|
64
|
+
['one_inch_punch', '>= 0.0.3']
|
65
65
|
]
|
66
66
|
|
67
67
|
#p.spec_extras = {} # A hash of extra values to set in the gemspec.
|
data/lib/freshtrack/version.rb
CHANGED
data/lib/freshtrack.rb
CHANGED
@@ -2,6 +2,7 @@ $:.unshift File.dirname(__FILE__)
|
|
2
2
|
require 'freshbooks/extensions'
|
3
3
|
require 'freshtrack/core_ext'
|
4
4
|
require 'yaml'
|
5
|
+
require 'punch'
|
5
6
|
|
6
7
|
module Freshtrack
|
7
8
|
class << self
|
@@ -36,14 +37,10 @@ module Freshtrack
|
|
36
37
|
raise unless @task
|
37
38
|
end
|
38
39
|
|
39
|
-
def get_time_data(project_name, options =
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
def convert_time_data(time_data)
|
45
|
-
raw = YAML.load(time_data)
|
46
|
-
condense_time_data(raw)
|
40
|
+
def get_time_data(project_name, options = {})
|
41
|
+
Punch.load
|
42
|
+
time_data = Punch.list(project_name, options)
|
43
|
+
condense_time_data(time_data)
|
47
44
|
end
|
48
45
|
|
49
46
|
def condense_time_data(time_data)
|
@@ -73,12 +70,12 @@ module Freshtrack
|
|
73
70
|
end
|
74
71
|
end
|
75
72
|
|
76
|
-
def get_data(project_name, options =
|
73
|
+
def get_data(project_name, options = {})
|
77
74
|
get_project_data(project_name)
|
78
75
|
get_time_data(project_name, options)
|
79
76
|
end
|
80
77
|
|
81
|
-
def track(project_name, options =
|
78
|
+
def track(project_name, options = {})
|
82
79
|
data = get_data(project_name, options)
|
83
80
|
data.each do |entry_data|
|
84
81
|
create_entry(entry_data)
|
data/script/destroy
CHANGED
File without changes
|
data/script/generate
CHANGED
File without changes
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe 'freshtrack command' do
|
4
|
+
def run_command(*args)
|
5
|
+
Object.const_set(:ARGV, args)
|
6
|
+
begin
|
7
|
+
eval File.read(File.join(File.dirname(__FILE__), *%w[.. bin freshtrack]))
|
8
|
+
rescue SystemExit
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
before :each do
|
13
|
+
[:ARGV, :OPTIONS, :MANDATORY_OPTIONS].each do |const|
|
14
|
+
Object.send(:remove_const, const) if Object.const_defined?(const)
|
15
|
+
end
|
16
|
+
|
17
|
+
Freshtrack.stubs(:init)
|
18
|
+
Freshtrack.stubs(:track)
|
19
|
+
|
20
|
+
@project = 'myproj'
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should exist' do
|
24
|
+
lambda { run_command(@project) }.should_not raise_error(Errno::ENOENT)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should require a project' do
|
28
|
+
self.expects(:puts) { |text| text.match(/usage.+project/i) }
|
29
|
+
run_command
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should init Freshtrack' do
|
33
|
+
Freshtrack.expects(:init)
|
34
|
+
run_command(@project)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should track time' do
|
38
|
+
Freshtrack.expects(:track)
|
39
|
+
run_command(@project)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should track time for the given project' do
|
43
|
+
Freshtrack.expects(:track).with(@project, anything)
|
44
|
+
run_command(@project)
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'when options specified' do
|
48
|
+
it "should pass on an 'after' time option given by --after" do
|
49
|
+
time_option = '2008-08-26 09:47'
|
50
|
+
time = Time.local(2008, 8, 26, 9, 47)
|
51
|
+
Freshtrack.expects(:track).with(@project, has_entry(:after => time))
|
52
|
+
run_command(@project, '--after', time_option)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should pass on a 'before' time option given by --before" do
|
56
|
+
time_option = '2008-08-23 15:39'
|
57
|
+
time = Time.local(2008, 8, 23, 15, 39)
|
58
|
+
Freshtrack.expects(:track).with(@project, has_entry(:before => time))
|
59
|
+
run_command(@project, '--before', time_option)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should handle a time option given as a date' do
|
63
|
+
time_option = '2008-08-23'
|
64
|
+
time = Time.local(2008, 8, 23)
|
65
|
+
Freshtrack.expects(:track).with(@project, has_entry(:before => time))
|
66
|
+
run_command(@project, '--before', time_option)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should pass no options if none specified' do
|
71
|
+
Freshtrack.expects(:track).with(@project, {})
|
72
|
+
run_command(@project)
|
73
|
+
end
|
74
|
+
end
|
data/spec/freshtrack_spec.rb
CHANGED
@@ -132,8 +132,9 @@ describe Freshtrack do
|
|
132
132
|
before :each do
|
133
133
|
@project_name = :proj
|
134
134
|
@time_data = stub('time data')
|
135
|
-
|
136
|
-
|
135
|
+
Punch.stubs(:load)
|
136
|
+
Punch.stubs(:list).returns(@time_data)
|
137
|
+
Freshtrack.stubs(:condense_time_data)
|
137
138
|
end
|
138
139
|
|
139
140
|
it 'should require an argument' do
|
@@ -144,74 +145,45 @@ describe Freshtrack do
|
|
144
145
|
lambda { Freshtrack.get_time_data(@project_name) }.should_not raise_error(ArgumentError)
|
145
146
|
end
|
146
147
|
|
147
|
-
it 'should accept
|
148
|
-
lambda { Freshtrack.get_time_data(@project_name,
|
148
|
+
it 'should accept options' do
|
149
|
+
lambda { Freshtrack.get_time_data(@project_name, :after => Time.now) }.should_not raise_error(ArgumentError)
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'should have punch load the time data' do
|
153
|
+
Punch.expects(:load)
|
154
|
+
Freshtrack.get_time_data(@project_name)
|
149
155
|
end
|
150
156
|
|
151
|
-
it 'should get the time data
|
152
|
-
|
157
|
+
it 'should get the time data from punch' do
|
158
|
+
Punch.expects(:list)
|
153
159
|
Freshtrack.get_time_data(@project_name)
|
154
160
|
end
|
155
161
|
|
156
162
|
it 'should pass the supplied project when getting the time data' do
|
157
|
-
|
163
|
+
Punch.expects(:list).with(@project_name, anything)
|
158
164
|
Freshtrack.get_time_data(@project_name)
|
159
165
|
end
|
160
166
|
|
161
|
-
it 'should pass the supplied
|
162
|
-
options =
|
163
|
-
|
167
|
+
it 'should pass the supplied options on when getting the time data' do
|
168
|
+
options = { :after => Time.now - 12345 }
|
169
|
+
Punch.expects(:list).with(@project_name, options)
|
164
170
|
Freshtrack.get_time_data(@project_name, options)
|
165
171
|
end
|
166
172
|
|
167
|
-
it 'should
|
168
|
-
|
173
|
+
it 'should pass an empty hash by default' do
|
174
|
+
Punch.expects(:list).with(@project_name, {})
|
169
175
|
Freshtrack.get_time_data(@project_name)
|
170
176
|
end
|
171
177
|
|
172
|
-
it 'should
|
173
|
-
Freshtrack.expects(:
|
178
|
+
it 'should condense the time data' do
|
179
|
+
Freshtrack.expects(:condense_time_data).with(@time_data)
|
174
180
|
Freshtrack.get_time_data(@project_name)
|
175
181
|
end
|
176
182
|
|
177
|
-
it 'should return the converted data' do
|
178
|
-
converted = stub('converted time data')
|
179
|
-
Freshtrack.stubs(:convert_time_data).returns(converted)
|
180
|
-
Freshtrack.get_time_data(@project_name).should == converted
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
describe 'converting time data' do
|
185
|
-
before :each do
|
186
|
-
@time_data = stub('time data')
|
187
|
-
YAML.stubs(:load)
|
188
|
-
Freshtrack.stubs(:condense_time_data)
|
189
|
-
end
|
190
|
-
|
191
|
-
it 'should require an argument' do
|
192
|
-
lambda { Freshtrack.convert_time_data }.should raise_error(ArgumentError)
|
193
|
-
end
|
194
|
-
|
195
|
-
it 'should accept an argument' do
|
196
|
-
lambda { Freshtrack.convert_time_data(@time_data) }.should_not raise_error(ArgumentError)
|
197
|
-
end
|
198
|
-
|
199
|
-
it 'should convert the time data from YAML' do
|
200
|
-
YAML.expects(:load).with(@time_data)
|
201
|
-
Freshtrack.convert_time_data(@time_data)
|
202
|
-
end
|
203
|
-
|
204
|
-
it 'should condense the raw data' do
|
205
|
-
raw = stub('raw time data')
|
206
|
-
YAML.stubs(:load).returns(raw)
|
207
|
-
Freshtrack.expects(:condense_time_data).with(raw)
|
208
|
-
Freshtrack.convert_time_data(@project_name)
|
209
|
-
end
|
210
|
-
|
211
183
|
it 'should return the condensed data' do
|
212
184
|
condensed = stub('condensed time data')
|
213
185
|
Freshtrack.stubs(:condense_time_data).returns(condensed)
|
214
|
-
Freshtrack.
|
186
|
+
Freshtrack.get_time_data(@project_name).should == condensed
|
215
187
|
end
|
216
188
|
end
|
217
189
|
|
@@ -394,8 +366,8 @@ describe Freshtrack do
|
|
394
366
|
lambda { Freshtrack.get_data(@project_name) }.should_not raise_error(ArgumentError)
|
395
367
|
end
|
396
368
|
|
397
|
-
it 'should accept
|
398
|
-
lambda { Freshtrack.get_data(@project_name,
|
369
|
+
it 'should accept options' do
|
370
|
+
lambda { Freshtrack.get_data(@project_name, :before => Time.now) }.should_not raise_error(ArgumentError)
|
399
371
|
end
|
400
372
|
|
401
373
|
it 'should get project data for supplied project' do
|
@@ -408,14 +380,14 @@ describe Freshtrack do
|
|
408
380
|
Freshtrack.get_data(@project_name)
|
409
381
|
end
|
410
382
|
|
411
|
-
it 'should pass
|
412
|
-
options =
|
383
|
+
it 'should pass options on when getting time data' do
|
384
|
+
options = { :after => Time.now - 12345 }
|
413
385
|
Freshtrack.expects(:get_time_data).with(@project_name, options)
|
414
386
|
Freshtrack.get_data(@project_name, options)
|
415
387
|
end
|
416
388
|
|
417
|
-
it 'should default
|
418
|
-
Freshtrack.expects(:get_time_data).with(@project_name,
|
389
|
+
it 'should default options to an empty hash' do
|
390
|
+
Freshtrack.expects(:get_time_data).with(@project_name, {})
|
419
391
|
Freshtrack.get_data(@project_name)
|
420
392
|
end
|
421
393
|
|
@@ -441,8 +413,8 @@ describe Freshtrack do
|
|
441
413
|
lambda { Freshtrack.track(@project_name) }.should_not raise_error(ArgumentError)
|
442
414
|
end
|
443
415
|
|
444
|
-
it 'should accept
|
445
|
-
lambda { Freshtrack.track(@project_name,
|
416
|
+
it 'should accept options' do
|
417
|
+
lambda { Freshtrack.track(@project_name, :before => Time.now) }.should_not raise_error(ArgumentError)
|
446
418
|
end
|
447
419
|
|
448
420
|
it 'should get data for supplied project' do
|
@@ -450,14 +422,14 @@ describe Freshtrack do
|
|
450
422
|
Freshtrack.track(@project_name)
|
451
423
|
end
|
452
424
|
|
453
|
-
it 'should pass
|
454
|
-
options =
|
425
|
+
it 'should pass options on when getting data' do
|
426
|
+
options = { :after => Time.now - 12345 }
|
455
427
|
Freshtrack.expects(:get_data).with(@project_name, options).returns(@data)
|
456
428
|
Freshtrack.track(@project_name, options)
|
457
429
|
end
|
458
430
|
|
459
|
-
it 'should default
|
460
|
-
Freshtrack.expects(:get_data).with(@project_name,
|
431
|
+
it 'should default options to an empty hash' do
|
432
|
+
Freshtrack.expects(:get_data).with(@project_name, {}).returns(@data)
|
461
433
|
Freshtrack.track(@project_name)
|
462
434
|
end
|
463
435
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: freshtrack
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yossef Mendelssohn
|
@@ -9,28 +9,40 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-08-27 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: freshbooks
|
17
|
+
type: :runtime
|
17
18
|
version_requirement:
|
18
19
|
version_requirements: !ruby/object:Gem::Requirement
|
19
20
|
requirements:
|
20
|
-
- - "
|
21
|
+
- - "="
|
21
22
|
- !ruby/object:Gem::Version
|
22
23
|
version: "2.1"
|
23
24
|
version:
|
24
25
|
- !ruby/object:Gem::Dependency
|
25
|
-
name:
|
26
|
+
name: one_inch_punch
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.0.3
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: hoe
|
37
|
+
type: :development
|
26
38
|
version_requirement:
|
27
39
|
version_requirements: !ruby/object:Gem::Requirement
|
28
40
|
requirements:
|
29
41
|
- - ">="
|
30
42
|
- !ruby/object:Gem::Version
|
31
|
-
version:
|
43
|
+
version: 1.7.0
|
32
44
|
version:
|
33
|
-
description:
|
45
|
+
description: Track your time on FreshBooks
|
34
46
|
email: ymendel@pobox.com
|
35
47
|
executables:
|
36
48
|
- freshtrack
|
@@ -66,6 +78,7 @@ files:
|
|
66
78
|
- script/generate
|
67
79
|
- setup.rb
|
68
80
|
- spec/base_object_spec.rb
|
81
|
+
- spec/freshtrack_command_spec.rb
|
69
82
|
- spec/freshtrack_spec.rb
|
70
83
|
- spec/project_spec.rb
|
71
84
|
- spec/task_spec.rb
|
@@ -102,9 +115,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
102
115
|
requirements: []
|
103
116
|
|
104
117
|
rubyforge_project: yomendel
|
105
|
-
rubygems_version: 1.0
|
118
|
+
rubygems_version: 1.2.0
|
106
119
|
signing_key:
|
107
120
|
specification_version: 2
|
108
|
-
summary:
|
121
|
+
summary: Track your time on FreshBooks
|
109
122
|
test_files: []
|
110
123
|
|