one_inch_punch 0.4.2 → 0.5.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 +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
|