From d7e972967560aaacf674b8638dd755d734b709d2 Mon Sep 17 00:00:00 2001 From: Yossef Mendelssohn Date: Tue, 10 Apr 2012 18:51:02 -0400 Subject: [PATCH 1/5] Adding Punch.entry for making a whole time entry Punching in and out in one command! For your pleasure! --- lib/punch.rb | 13 ++++++++- spec/punch_spec.rb | 71 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/lib/punch.rb b/lib/punch.rb index 37f6b7e..d4cc3fb 100644 --- a/lib/punch.rb +++ b/lib/punch.rb @@ -108,7 +108,18 @@ def out(*args) end true end - + + def entry(project, options = {}) + raise ArgumentError, 'both :from and :to time are needed' unless options[:from] and options[:to] + + in_options = { :time => options[:from] } + in_options[:message] = options[:message] if options[:message] + result = self.in(project, in_options) + return result unless result + + out(project, :time => options[:to]) + end + def delete(project) return nil unless data.delete(project) true diff --git a/spec/punch_spec.rb b/spec/punch_spec.rb index 31c7d47..72247c0 100644 --- a/spec/punch_spec.rb +++ b/spec/punch_spec.rb @@ -990,6 +990,77 @@ class << self end end + it 'should create a full punch entry' do + Punch.should.respond_to(:entry) + end + + describe 'creating a full punch entry' do + before do + Punch.stub!(:in) + Punch.stub!(:out) + + @now = Time.now + @from_time = @now - 1000 + @to_time = @now - 100 + @project = 'myproj' + end + + it 'should require a project name' do + lambda { Punch.entry }.should.raise(ArgumentError) + end + + it 'should accept a project name and options' do + lambda { Punch.entry('proj', :from => Time.now - 500, :to => Time.now - 20) }.should.not.raise(ArgumentError) + end + + it 'should require :from and :to options' do + lambda { Punch.entry('proj') }.should.raise(ArgumentError) + end + + it 'should punch the project in with the given :from time' do + Punch.should.receive(:in).with(@project, :time => @from_time) + Punch.entry(@project, :from => @from_time, :to => @to_time) + end + + it 'should pass any given message when punching in' do + msg = 'just some work' + Punch.should.receive(:in).with(@project, :time => @from_time, :message => msg) + Punch.entry(@project, :from => @from_time, :to => @to_time, :message => msg) + end + + describe 'when punching in is successful' do + before do + Punch.stub!(:in).and_return(true) + end + + it 'should punch the project out with the given :to time' do + Punch.should.receive(:out).with(@project, :time => @to_time) + Punch.entry(@project, :from => @from_time, :to => @to_time) + end + + it 'should return the result from punching out' do + result = Object.new + Punch.stub!(:out).and_return(result) + Punch.entry(@project, :from => @from_time, :to => @to_time).should == result + end + end + + describe 'when punching in is unsuccesful' do + before do + Punch.stub!(:in).and_return(false) + end + + it 'should not attempt the punch the project out' do + Punch.should.receive(:out).never + Punch.entry(@project, :from => @from_time, :to => @to_time) + end + + it 'should return false' do + Punch.entry(@project, :from => @from_time, :to => @to_time).should == false + end + end + end + it 'should delete a project' do Punch.should.respond_to(:delete) end From 370656c78d66344e1d64f4e5dc362b46cf3c6a44 Mon Sep 17 00:00:00 2001 From: Yossef Mendelssohn Date: Tue, 10 Apr 2012 18:54:35 -0400 Subject: [PATCH 2/5] Adding Punch.clock as an alias for Punch.entry It sounds better. It's actually the first thing that came to mind (and maybe what Ara used long ago), but Punch.entry sounds more sensible as the "real" method name. --- lib/punch.rb | 1 + spec/punch_spec.rb | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/lib/punch.rb b/lib/punch.rb index d4cc3fb..43a9841 100644 --- a/lib/punch.rb +++ b/lib/punch.rb @@ -119,6 +119,7 @@ def entry(project, options = {}) out(project, :time => options[:to]) end + alias_method :clock, :entry def delete(project) return nil unless data.delete(project) diff --git a/spec/punch_spec.rb b/spec/punch_spec.rb index 72247c0..04ae588 100644 --- a/spec/punch_spec.rb +++ b/spec/punch_spec.rb @@ -1059,6 +1059,13 @@ class << self Punch.entry(@project, :from => @from_time, :to => @to_time).should == false end end + + it 'should have .clock as an alias' do + msg = 'just some work' + Punch.should.receive(:in).with(@project, :time => @from_time, :message => msg).and_return(true) + Punch.should.receive(:out).with(@project, :time => @to_time) + Punch.clock(@project, :from => @from_time, :to => @to_time, :message => msg) + end end it 'should delete a project' do From 462a00f6fa0958758a026a8aed57a209aa7205af Mon Sep 17 00:00:00 2001 From: Yossef Mendelssohn Date: Tue, 10 Apr 2012 18:59:49 -0400 Subject: [PATCH 3/5] Adding entry/clock to instance. Amazingly enough, this is just like every other delegated method here. I should do something about the repetition in this class. --- lib/punch/instance.rb | 7 ++++- spec/punch_instance_spec.rb | 54 +++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/lib/punch/instance.rb b/lib/punch/instance.rb index 3230553..17204ef 100644 --- a/lib/punch/instance.rb +++ b/lib/punch/instance.rb @@ -24,7 +24,12 @@ def in(options = {}) def out(options = {}) self.class.out(project, options) end - + + def entry(options = {}) + self.class.entry(project, options) + end + alias_method :clock, :entry + def list(options = {}) self.class.list(project, options) end diff --git a/spec/punch_instance_spec.rb b/spec/punch_instance_spec.rb index 86f9d8f..ac1f0cf 100644 --- a/spec/punch_instance_spec.rb +++ b/spec/punch_instance_spec.rb @@ -220,6 +220,60 @@ end end + it 'should make a punch entry' do + @punch.should.respond_to(:entry) + end + + describe 'making a punch entry' do + before do + @entry = 'entry val' + Punch.stub!(:entry).and_return(@entry) + end + + it 'should accept options' do + lambda { @punch.entry(:time => Time.now) }.should.not.raise(ArgumentError) + end + + it 'should not require options' do + lambda { @punch.entry }.should.not.raise(ArgumentError) + end + + it 'should delegate to the class' do + Punch.should.receive(:entry) + @punch.entry + end + + it 'should pass the project when delegating to the class' do + Punch.should.receive(:entry) do |proj, _| + proj.should == @project + end + @punch.entry + end + + it 'should pass the options when delegating to the class' do + options = { :time => Time.now } + Punch.should.receive(:entry) do |_, opts| + opts.should == options + end + @punch.entry(options) + end + + it 'should pass an empty hash if no options given' do + Punch.should.receive(:entry) do |_, opts| + opts.should == {} + end + @punch.entry + end + + it 'should return the value returned by the class method' do + @punch.entry.should == @entry + end + + it 'should have clock as an alias' do + @punch.clock.should == @entry + end + end + it 'should list the project data' do @punch.should.respond_to(:list) end From 54ff639b2c616c8f0c996e6932403405c59ffeda Mon Sep 17 00:00:00 2001 From: Yossef Mendelssohn Date: Tue, 10 Apr 2012 19:16:57 -0400 Subject: [PATCH 4/5] Adding 'entry' to punch command-line Same as the rest, really. Also adding from/to command-line options for use with this command. --- bin/punch | 15 ++++++ spec/punch_command_spec.rb | 106 ++++++++++++++++++++++++++++++++++++- 2 files changed, 120 insertions(+), 1 deletion(-) diff --git a/bin/punch b/bin/punch index db8a275..6b8b1bc 100755 --- a/bin/punch +++ b/bin/punch @@ -33,6 +33,10 @@ BANNER "Restrict command to only on the given date") { |date| OPTIONS[:on] = Date.parse(date) } opts.on('--at [TIME]', '--time [TIME]', String, "Use the given time") { |time| OPTIONS[:time] = Time.parse(time) } + opts.on('--from [TIME]', String, + "Use the given time") { |time| OPTIONS[:from] = Time.parse(time) } + opts.on('--to [TIME]', String, + "Use the given time") { |time| OPTIONS[:to] = Time.parse(time) } opts.on('-m', '--message [MESSAGE]', String, "Use the given log message") { |message| OPTIONS[:message] = message } opts.on('--full', @@ -104,6 +108,17 @@ commands = { puts message end end, + 'entry' => lambda do |project| + if project + if Punch.entry(project, OPTIONS) + Punch.write + else + puts "Cannot create entry for '#{project}'" + end + else + puts "Project required" + end + end, 'log' => lambda do |project| if project if message = OPTIONS.delete(:message) || ARGV[2] diff --git a/spec/punch_command_spec.rb b/spec/punch_command_spec.rb index 4a12203..adb4438 100644 --- a/spec/punch_command_spec.rb +++ b/spec/punch_command_spec.rb @@ -467,7 +467,111 @@ def run_command(*args) end end end - + + describe "when the command is 'entry'" do + before do + Punch.stub!(:entry) + @from_option = '2012-04-10 14:39' + @to_option = '2012-04-10 17:43' + end + + it 'should load punch data' do + Punch.should.receive(:load) + run_command('entry', @project) + end + + it 'should make a punch entry for the given project' do + from_option = '2012-04-10 14:39' + from_time = Time.local(2012, 4, 10, 14, 39) + to_option = '2012-04-10 17:43' + to_time = Time.local(2012, 4, 10, 17, 43) + + Punch.should.receive(:entry) do |proj, options| + proj.should == @project + options[:from].should == from_time + options[:to ].should == to_time + end + + run_command('entry', @project, '--from', from_option, '--to', to_option) + end + + it 'should pass a message if specified on the command line (with --message)' do + message = 'About to do some amazing work' + + Punch.should.receive(:entry) do |proj, options| + proj.should == @project + options[:message].should == message + end + + run_command('entry', @project, '--from', @from_option, '--to', @to_option, '--message', message) + end + + it 'should pass a message if specified on the command line (with -m)' do + message = 'About to do some amazing work' + + Punch.should.receive(:entry) do |proj, options| + proj.should == @project + options[:message].should == message + end + + run_command('entry', @project, '--from', @from_option, '--to', @to_option, '-m', message) + end + + describe 'when entry created successfully' do + before do + Punch.stub!(:entry).and_return(true) + end + + it 'should write the data' do + Punch.should.receive(:write) + run_command('entry', @project, '--from', @from_option, '--to', @to_option) + end + + it 'should not print anything' do + self.should.receive(:puts).never + run_command('entry', @project, '--from', @from_option, '--to', @to_option) + end + end + + describe 'when entry not created successfully' do + before do + Punch.stub!(:entry).and_return(false) + end + + it 'should not write the data' do + Punch.should.receive(:write).never + run_command('entry', @project, '--from', @from_option, '--to', @to_option) + end + + it 'should print a message' do + self.should.receive(:puts) do |output| + output.should.match(/cannot.+entry/i) + end + run_command('entry', @project, '--from', @from_option, '--to', @to_option) + end + end + + describe 'when no project given' do + it 'should display an error message' do + self.should.receive(:puts) do |output| + output.should.match(/project.+require/i) + end + run_command('entry') + end + + it 'should not create an entry' do + Punch.stub!(:write) + Punch.should.receive(:entry).never + run_command('entry') + end + + it 'should not write the data' do + Punch.should.receive(:write).never + run_command('entry') + end + end + end + describe "when the command is 'delete'" do before do Punch.stub!(:delete) From 1d865ebcf735f34d73766bc894ac26d07b98e5d1 Mon Sep 17 00:00:00 2001 From: Yossef Mendelssohn Date: Tue, 10 Apr 2012 19:20:41 -0400 Subject: [PATCH 5/5] Adding 'clock' to command-line Thought momentarily about putting in extra code merely so I could have a line in the hash assignment reading `'clock' => 'entry'`, but quickly got a hold of myself. --- bin/punch | 1 + spec/punch_command_spec.rb | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/bin/punch b/bin/punch index 6b8b1bc..0556201 100755 --- a/bin/punch +++ b/bin/punch @@ -151,6 +151,7 @@ commands = { end end } +commands['clock'] = commands['entry'] if command_code = commands[command] command_code.call(project) diff --git a/spec/punch_command_spec.rb b/spec/punch_command_spec.rb index adb4438..653201e 100644 --- a/spec/punch_command_spec.rb +++ b/spec/punch_command_spec.rb @@ -570,6 +570,22 @@ def run_command(*args) run_command('entry') end end + + it "should have 'clock' as an alias" do + from_option = '2012-04-10 14:39' + from_time = Time.local(2012, 4, 10, 14, 39) + to_option = '2012-04-10 17:43' + to_time = Time.local(2012, 4, 10, 17, 43) + + Punch.should.receive(:entry) do |proj, options| + proj.should == @project + options[:from].should == from_time + options[:to ].should == to_time + end + + run_command('clock', @project, '--from', from_option, '--to', to_option) + end + end describe "when the command is 'delete'" do