one_inch_punch 0.4.2 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +14 -6
- data/bin/punch +8 -0
- data/lib/punch.rb +36 -14
- data/lib/punch/instance.rb +4 -0
- data/lib/punch/version.rb +2 -2
- data/spec/punch_command_spec.rb +85 -1
- data/spec/punch_instance_spec.rb +50 -0
- data/spec/punch_spec.rb +101 -4
- metadata +7 -7
data/History.txt
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
== 0.5.0 2010-11-18
|
2
|
+
|
3
|
+
* 1 minor enhancement:
|
4
|
+
* added 'age' command
|
5
|
+
* moves project data to an old-version name, e.g. project -> project_old/1
|
6
|
+
* cascades old-version project data, e.g. project -> project_old/1, project_old/1 -> project_old/2
|
7
|
+
* can take a :before option to only move a subset of data
|
8
|
+
|
1
9
|
== 0.4.2 2010-08-16
|
2
10
|
|
3
11
|
* 1 tiny enhancement:
|
@@ -6,13 +14,13 @@
|
|
6
14
|
* option can be given on command-line with --on
|
7
15
|
* :before and :after can also take dates now, converting them to times
|
8
16
|
|
9
|
-
== 0.4.1
|
17
|
+
== 0.4.1 2009-08-26
|
10
18
|
|
11
19
|
* 1 tiny enhancement:
|
12
20
|
* allowing command-line log message to be specified with --message
|
13
21
|
(will take precedence over normal message argument if both are specified)
|
14
22
|
|
15
|
-
== 0.4.0
|
23
|
+
== 0.4.0 2009-08-17
|
16
24
|
|
17
25
|
* 1 minor enhancement:
|
18
26
|
* added 'summary' command
|
@@ -23,24 +31,24 @@
|
|
23
31
|
* 1 tiny bugfix
|
24
32
|
* full status now works with "short" (only display what's punched-in) instead of always showing 'out'
|
25
33
|
|
26
|
-
== 0.3.3
|
34
|
+
== 0.3.3 2009-06-25
|
27
35
|
|
28
36
|
* 1 tiny enhancement:
|
29
37
|
* added 'short' status, which only displays what's currently punched in (or 'out' if nothing)
|
30
38
|
* punch command displays status as YAML if it's a hash, like it does for total
|
31
39
|
|
32
|
-
== 0.3.2
|
40
|
+
== 0.3.2 2009-06-01
|
33
41
|
|
34
42
|
* 1 tiny bugfix enhancement:
|
35
43
|
* sub-projects now truly restricting to the naming form of 'parent/child',
|
36
44
|
not 'parent_child' or even simply 'parentseriouslythisisnotachild'
|
37
45
|
|
38
|
-
== 0.3.1
|
46
|
+
== 0.3.1 2009-04-06
|
39
47
|
|
40
48
|
* 1 tiny bugfix enhancement:
|
41
49
|
* Total no longer erroring on non-existent parent project
|
42
50
|
|
43
|
-
== 0.3.0
|
51
|
+
== 0.3.0 2009-04-05
|
44
52
|
|
45
53
|
* 1 minor enhancement:
|
46
54
|
* Can now have 'sub-projects', or projects with names of the form 'parent/child'
|
data/bin/punch
CHANGED
@@ -126,6 +126,14 @@ commands = {
|
|
126
126
|
else
|
127
127
|
puts "Project required"
|
128
128
|
end
|
129
|
+
end,
|
130
|
+
'age' => lambda do |project|
|
131
|
+
if project
|
132
|
+
Punch.write if result = Punch.age(project, OPTIONS)
|
133
|
+
puts result.inspect
|
134
|
+
else
|
135
|
+
puts "Project required"
|
136
|
+
end
|
129
137
|
end
|
130
138
|
}
|
131
139
|
|
data/lib/punch.rb
CHANGED
@@ -158,21 +158,22 @@ class Punch
|
|
158
158
|
summary = Hash.new(0)
|
159
159
|
|
160
160
|
list_data.each do |time_data|
|
161
|
-
|
162
|
-
log = time_data['log'].collect do |l|
|
163
|
-
msg, time = l.split('@')
|
164
|
-
msg = msg.strip
|
165
|
-
msg = 'unspecified' if msg == 'punch in'
|
166
|
-
{ :msg => msg, :time => Time.parse(time) }
|
167
|
-
end
|
168
|
-
|
169
|
-
log << { :msg => 'punch out', :time => Time.now } unless log.last[:msg] == 'punch out'
|
170
|
-
|
171
|
-
log.each_cons(2) do |a, b|
|
172
|
-
summary[a[:msg]] += (b[:time] - a[:time]).to_i
|
173
|
-
end
|
174
|
-
else
|
161
|
+
if (time_data['log'] || []).empty?
|
175
162
|
summary['unspecified'] += ((time_data['out'] || Time.now) - time_data['in']).to_i
|
163
|
+
next
|
164
|
+
end
|
165
|
+
|
166
|
+
log = time_data['log'].collect do |l|
|
167
|
+
msg, time = l.split('@')
|
168
|
+
msg = msg.strip
|
169
|
+
msg = 'unspecified' if msg == 'punch in'
|
170
|
+
{ :msg => msg, :time => Time.parse(time) }
|
171
|
+
end
|
172
|
+
|
173
|
+
log << { :msg => 'punch out', :time => Time.now } unless log.last[:msg] == 'punch out'
|
174
|
+
|
175
|
+
log.each_cons(2) do |a, b|
|
176
|
+
summary[a[:msg]] += (b[:time] - a[:time]).to_i
|
176
177
|
end
|
177
178
|
end
|
178
179
|
|
@@ -181,6 +182,27 @@ class Punch
|
|
181
182
|
summary
|
182
183
|
end
|
183
184
|
|
185
|
+
def age(project, options = {})
|
186
|
+
raise ":after option makes no sense for aging" if options[:after]
|
187
|
+
return nil unless projects.include?(project)
|
188
|
+
|
189
|
+
if project.match(%r{_old/\d+$})
|
190
|
+
aged_project = project.succ
|
191
|
+
else
|
192
|
+
aged_project = "#{project}_old/1"
|
193
|
+
end
|
194
|
+
|
195
|
+
age(aged_project)
|
196
|
+
if options[:before]
|
197
|
+
data[aged_project], data[project] = data[project].partition { |d| d['out'] < options[:before] }
|
198
|
+
[project, aged_project].each { |proj| data.delete(proj) if data[proj].empty? }
|
199
|
+
else
|
200
|
+
data[aged_project] = data.delete(project)
|
201
|
+
end
|
202
|
+
|
203
|
+
true
|
204
|
+
end
|
205
|
+
|
184
206
|
|
185
207
|
private
|
186
208
|
|
data/lib/punch/instance.rb
CHANGED
data/lib/punch/version.rb
CHANGED
data/spec/punch_command_spec.rb
CHANGED
@@ -853,6 +853,90 @@ describe 'punch command' do
|
|
853
853
|
end
|
854
854
|
end
|
855
855
|
|
856
|
+
describe "when the command is 'age'" do
|
857
|
+
before do
|
858
|
+
Punch.stub!(:age)
|
859
|
+
end
|
860
|
+
|
861
|
+
it 'should load punch data' do
|
862
|
+
Punch.should.receive(:load)
|
863
|
+
run_command('age', @project)
|
864
|
+
end
|
865
|
+
|
866
|
+
it 'should age the given project' do
|
867
|
+
Punch.stub!(:write)
|
868
|
+
Punch.should.receive(:age) do |proj, _|
|
869
|
+
proj.should == @project
|
870
|
+
end
|
871
|
+
run_command('age', @project)
|
872
|
+
end
|
873
|
+
|
874
|
+
describe 'when options specified' do
|
875
|
+
it "should pass on a 'before' time option given by --before" do
|
876
|
+
time_option = '2008-08-23 15:39'
|
877
|
+
time = Time.local(2008, 8, 23, 15, 39)
|
878
|
+
Punch.should.receive(:age) do |proj, options|
|
879
|
+
proj.should == @project
|
880
|
+
options[:before].should == time
|
881
|
+
end
|
882
|
+
run_command('age', @project, '--before', time_option)
|
883
|
+
end
|
884
|
+
|
885
|
+
it 'should handle a time option given as a date' do
|
886
|
+
time_option = '2008-08-23'
|
887
|
+
time = Time.local(2008, 8, 23)
|
888
|
+
Punch.should.receive(:age) do |proj, options|
|
889
|
+
proj.should == @project
|
890
|
+
options[:before].should == time
|
891
|
+
end
|
892
|
+
run_command('age', @project, '--before', time_option)
|
893
|
+
end
|
894
|
+
end
|
895
|
+
|
896
|
+
it 'should output the result' do
|
897
|
+
result = 'result'
|
898
|
+
Punch.stub!(:age).and_return(result)
|
899
|
+
self.should.receive(:puts).with(result.inspect)
|
900
|
+
run_command('age', @project)
|
901
|
+
end
|
902
|
+
|
903
|
+
describe 'when aged successfully' do
|
904
|
+
it 'should write the data' do
|
905
|
+
Punch.stub!(:age).and_return(true)
|
906
|
+
Punch.should.receive(:write)
|
907
|
+
run_command('age', @project)
|
908
|
+
end
|
909
|
+
end
|
910
|
+
|
911
|
+
describe 'when not aged successfully' do
|
912
|
+
it 'should not write the data' do
|
913
|
+
Punch.stub!(:age).and_return(nil)
|
914
|
+
Punch.should.receive(:write).never
|
915
|
+
run_command('age', @project)
|
916
|
+
end
|
917
|
+
end
|
918
|
+
|
919
|
+
describe 'when no project given' do
|
920
|
+
it 'should display an error message' do
|
921
|
+
self.should.receive(:puts) do |output|
|
922
|
+
output.should.match(/project.+require/i)
|
923
|
+
end
|
924
|
+
run_command('age')
|
925
|
+
end
|
926
|
+
|
927
|
+
it 'should not age' do
|
928
|
+
Punch.stub!(:write)
|
929
|
+
Punch.should.receive(:age).never
|
930
|
+
run_command('age')
|
931
|
+
end
|
932
|
+
|
933
|
+
it 'should not write the data' do
|
934
|
+
Punch.should.receive(:write).never
|
935
|
+
run_command('age')
|
936
|
+
end
|
937
|
+
end
|
938
|
+
end
|
939
|
+
|
856
940
|
describe 'when the command is unknown' do
|
857
941
|
it 'should not error' do
|
858
942
|
lambda { run_command('bunk') }.should.not.raise
|
@@ -871,7 +955,7 @@ describe 'punch command' do
|
|
871
955
|
end
|
872
956
|
|
873
957
|
it 'should not run any punch command' do
|
874
|
-
[:in, :out, :delete, :status, :total, :log, :list, :summary].each do |command|
|
958
|
+
[:in, :out, :delete, :status, :total, :log, :list, :summary, :age].each do |command|
|
875
959
|
Punch.should.receive(command).never
|
876
960
|
end
|
877
961
|
run_command('bunk')
|
data/spec/punch_instance_spec.rb
CHANGED
@@ -432,6 +432,56 @@ describe Punch, 'instance' do
|
|
432
432
|
end
|
433
433
|
end
|
434
434
|
|
435
|
+
it 'should age the project' do
|
436
|
+
@punch.should.respond_to(:age)
|
437
|
+
end
|
438
|
+
|
439
|
+
describe 'aging project' do
|
440
|
+
before do
|
441
|
+
@age = 'age val'
|
442
|
+
Punch.stub!(:age).and_return(@age)
|
443
|
+
end
|
444
|
+
|
445
|
+
it 'should accept options' do
|
446
|
+
lambda { @punch.age(:before => Time.now) }.should.not.raise(ArgumentError)
|
447
|
+
end
|
448
|
+
|
449
|
+
it 'should not require options' do
|
450
|
+
lambda { @punch.age }.should.not.raise(ArgumentError)
|
451
|
+
end
|
452
|
+
|
453
|
+
it 'should delegate to the class' do
|
454
|
+
Punch.should.receive(:age)
|
455
|
+
@punch.age
|
456
|
+
end
|
457
|
+
|
458
|
+
it 'should pass the project when delegating to the class' do
|
459
|
+
Punch.should.receive(:age) do |proj, _|
|
460
|
+
proj.should == @project
|
461
|
+
end
|
462
|
+
@punch.age
|
463
|
+
end
|
464
|
+
|
465
|
+
it 'should pass the options when delegating to the class' do
|
466
|
+
options = { :before => Time.now }
|
467
|
+
Punch.should.receive(:age) do |_, opts|
|
468
|
+
opts.should == options
|
469
|
+
end
|
470
|
+
@punch.age(options)
|
471
|
+
end
|
472
|
+
|
473
|
+
it 'should pass an empty hash if no options given' do
|
474
|
+
Punch.should.receive(:age) do |_, opts|
|
475
|
+
opts.should == {}
|
476
|
+
end
|
477
|
+
@punch.age
|
478
|
+
end
|
479
|
+
|
480
|
+
it 'should return the value returned by the class method' do
|
481
|
+
@punch.age.should == @age
|
482
|
+
end
|
483
|
+
end
|
484
|
+
|
435
485
|
describe 'equality' do
|
436
486
|
it 'should be equal to another instance for the same project' do
|
437
487
|
Punch.new('proj').should == Punch.new('proj')
|
data/spec/punch_spec.rb
CHANGED
@@ -1576,10 +1576,6 @@ describe Punch do
|
|
1576
1576
|
end
|
1577
1577
|
|
1578
1578
|
describe 'providing a summary of project time use' do
|
1579
|
-
def format_time(time)
|
1580
|
-
time.strftime()
|
1581
|
-
end
|
1582
|
-
|
1583
1579
|
before do
|
1584
1580
|
@message = 'test usage'
|
1585
1581
|
@now = Time.now
|
@@ -1766,4 +1762,105 @@ describe Punch do
|
|
1766
1762
|
end
|
1767
1763
|
end
|
1768
1764
|
end
|
1765
|
+
|
1766
|
+
it 'should age a project' do
|
1767
|
+
Punch.should.respond_to(:age)
|
1768
|
+
end
|
1769
|
+
|
1770
|
+
describe 'aging a project' do
|
1771
|
+
before do
|
1772
|
+
@now = Time.now
|
1773
|
+
Time.stub!(:now).and_return(@now)
|
1774
|
+
@project = 'pro-ject'
|
1775
|
+
@data = { @project => [ {'in' => @now - 3000} ] }
|
1776
|
+
|
1777
|
+
Punch.instance_eval do
|
1778
|
+
class << self
|
1779
|
+
public :data, :data=
|
1780
|
+
end
|
1781
|
+
end
|
1782
|
+
Punch.data = @data
|
1783
|
+
end
|
1784
|
+
|
1785
|
+
it 'should accept a project name' do
|
1786
|
+
lambda { Punch.age('proj') }.should.not.raise(ArgumentError)
|
1787
|
+
end
|
1788
|
+
|
1789
|
+
it 'should require a project name' do
|
1790
|
+
lambda { Punch.age }.should.raise(ArgumentError)
|
1791
|
+
end
|
1792
|
+
|
1793
|
+
it 'should accept options' do
|
1794
|
+
lambda { Punch.age('proj', :before => @now - 5000) }.should.not.raise(ArgumentError)
|
1795
|
+
end
|
1796
|
+
|
1797
|
+
describe 'when the project exists' do
|
1798
|
+
it 'should move the project to #{project}_old/1' do
|
1799
|
+
Punch.age(@project)
|
1800
|
+
Punch.data.should == { "#{@project}_old/1" => [ {'in' => @now - 3000} ] }
|
1801
|
+
end
|
1802
|
+
|
1803
|
+
it 'should simply increment the number of a project name in the x_old/1 format' do
|
1804
|
+
@data = { 'some_old/1' => [ {'in' => @now - 3900} ] }
|
1805
|
+
Punch.data = @data
|
1806
|
+
|
1807
|
+
Punch.age('some_old/1')
|
1808
|
+
Punch.data.should == { "some_old/2" => [ {'in' => @now - 3900} ] }
|
1809
|
+
end
|
1810
|
+
|
1811
|
+
it 'should cascade aging a project to older versions' do
|
1812
|
+
@data["#{@project}_old/1"] = [ {'in' => @now - 50000, 'out' => @now - 40000} ]
|
1813
|
+
@data["#{@project}_old/2"] = [ {'in' => @now - 90000, 'out' => @now - 85000} ]
|
1814
|
+
Punch.data = @data
|
1815
|
+
|
1816
|
+
Punch.age(@project)
|
1817
|
+
Punch.data.should == { "#{@project}_old/1" => [ {'in' => @now - 3000} ], "#{@project}_old/2" => [ {'in' => @now - 50000, 'out' => @now - 40000} ], "#{@project}_old/3" => [ {'in' => @now - 90000, 'out' => @now - 85000} ] }
|
1818
|
+
end
|
1819
|
+
|
1820
|
+
it 'should return true' do
|
1821
|
+
Punch.age(@project).should == true
|
1822
|
+
end
|
1823
|
+
|
1824
|
+
describe 'when options given' do
|
1825
|
+
before do
|
1826
|
+
@data = { @project => [ {'in' => @now - 5000, 'out' => @now - 4000}, {'in' => @now - 3500, 'out' => @now - 3000}, {'in' => @now - 1500, 'out' => @now - 900}, {'in' => @now - 500, 'out' => @now - 400} ] }
|
1827
|
+
Punch.data = @data
|
1828
|
+
end
|
1829
|
+
|
1830
|
+
it 'should only move the appropriate data to the old-version project' do
|
1831
|
+
expected = { @project => [ {'in' => @now - 1500, 'out' => @now - 900}, {'in' => @now - 500, 'out' => @now - 400} ],
|
1832
|
+
"#{@project}_old/1" => [ {'in' => @now - 5000, 'out' => @now - 4000}, {'in' => @now - 3500, 'out' => @now - 3000} ]
|
1833
|
+
}
|
1834
|
+
Punch.age(@project, :before => @now - 1500)
|
1835
|
+
Punch.data.should == expected
|
1836
|
+
end
|
1837
|
+
|
1838
|
+
it 'should not create an old project if no data would be moved' do
|
1839
|
+
expected = { @project => [ {'in' => @now - 5000, 'out' => @now - 4000}, {'in' => @now - 3500, 'out' => @now - 3000}, {'in' => @now - 1500, 'out' => @now - 900}, {'in' => @now - 500, 'out' => @now - 400} ] }
|
1840
|
+
Punch.age(@project, :before => @now - 50000)
|
1841
|
+
Punch.data.should == expected
|
1842
|
+
end
|
1843
|
+
|
1844
|
+
it 'should remove the project if all data would be moved' do
|
1845
|
+
expected = { "#{@project}_old/1" => [ {'in' => @now - 5000, 'out' => @now - 4000}, {'in' => @now - 3500, 'out' => @now - 3000}, {'in' => @now - 1500, 'out' => @now - 900}, {'in' => @now - 500, 'out' => @now - 400} ] }
|
1846
|
+
Punch.age(@project, :before => @now - 10)
|
1847
|
+
Punch.data.should == expected
|
1848
|
+
end
|
1849
|
+
|
1850
|
+
it 'should not accept an after option' do
|
1851
|
+
lambda { Punch.age(@project, :after => @now - 500) }.should.raise
|
1852
|
+
end
|
1853
|
+
end
|
1854
|
+
end
|
1855
|
+
|
1856
|
+
describe 'when the project does not exist' do
|
1857
|
+
before do
|
1858
|
+
@project = 'no dice'
|
1859
|
+
end
|
1860
|
+
|
1861
|
+
it 'should return nil' do
|
1862
|
+
Punch.age(@project).should.be.nil
|
1863
|
+
end
|
1864
|
+
end
|
1865
|
+
end
|
1769
1866
|
end
|
metadata
CHANGED
@@ -5,9 +5,9 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 5
|
9
|
+
- 0
|
10
|
+
version: 0.5.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Yossef Mendelssohn
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-
|
18
|
+
date: 2010-11-18 00:00:00 -06:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -74,12 +74,12 @@ dependencies:
|
|
74
74
|
requirements:
|
75
75
|
- - ">="
|
76
76
|
- !ruby/object:Gem::Version
|
77
|
-
hash:
|
77
|
+
hash: 19
|
78
78
|
segments:
|
79
79
|
- 2
|
80
80
|
- 6
|
81
|
-
-
|
82
|
-
version: 2.6.
|
81
|
+
- 2
|
82
|
+
version: 2.6.2
|
83
83
|
type: :development
|
84
84
|
version_requirements: *id004
|
85
85
|
description: a simple time-tracking tool
|