freshtrack 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,10 @@
1
+ == 0.7.0 2012-03-30
2
+
3
+ * 1 minor enhancement:
4
+ * Tracking time is now idempotent. If a time entry already exists for a date/project/task combination, that time entry will be updated rather than a new entry created.
5
+ * 1 bugfix:
6
+ * Tracking time for a project when you're punched in no longer raises an exception. Time entries that aren't completed (not punched out) are now ignored.
7
+
1
8
  == 0.6.0 2012-03-30
2
9
 
3
10
  * 1 minor enhancement:
@@ -9,16 +9,17 @@ module FreshBooks
9
9
  when '1' : true
10
10
  end
11
11
  end
12
-
12
+
13
13
  def to_xml
14
14
  # The root element is the elem name
15
15
  root = Element.new elem_name
16
-
16
+
17
17
  # Add each BaseObject member to the root elem
18
- self.members.each do |field_name|
19
-
18
+
19
+ self.included_members.each do |field_name|
20
+
20
21
  value = self.send(field_name)
21
-
22
+
22
23
  if value.is_a?(Array)
23
24
  node = root.add_element(field_name)
24
25
  value.each { |array_elem| node.add_element(array_elem.to_xml) }
@@ -28,10 +29,19 @@ module FreshBooks
28
29
  end
29
30
  root
30
31
  end
31
-
32
+
32
33
  # The root element is the class name, downcased (and underscored if there is any CamelCase)
33
34
  def elem_name
34
35
  elem_name = self.class.to_s.split('::').last.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
35
36
  end
37
+
38
+ def included_members
39
+ members - excluded_members
40
+ end
41
+
42
+ def excluded_members
43
+ return [] unless self.class.const_defined?(:EXCLUDE_XML_ATTRIBUTES)
44
+ self.class.const_get(:EXCLUDE_XML_ATTRIBUTES)
45
+ end
36
46
  end
37
47
  end
@@ -6,7 +6,9 @@ module FreshBooks
6
6
  'time_entry_id' => Fixnum, 'project_id' => Fixnum, 'task_id' => Fixnum,
7
7
  'hours' => Float, 'date' => Date, 'billed' => :boolean
8
8
  }
9
-
9
+
10
+ EXCLUDE_XML_ATTRIBUTES = %w[billed]
11
+
10
12
  class << self
11
13
  def get(time_entry_id)
12
14
  resp = FreshBooks.call_api('time_entry.get', 'time_entry_id' => time_entry_id)
data/lib/freshtrack.rb CHANGED
@@ -52,22 +52,31 @@ module Freshtrack
52
52
 
53
53
  def track(project_name, options = {})
54
54
  data = get_data(project_name, options)
55
+ tracked = get_tracked_data(data)
55
56
  data.each do |entry_data|
56
- create_entry(entry_data)
57
+ tracked_entry = tracked.detect { |t| t.date == entry_data['date'] }
58
+ create_entry(entry_data, tracked_entry)
57
59
  end
58
60
  end
59
-
60
- def create_entry(entry_data)
61
- time_entry = FreshBooks::TimeEntry.new
62
-
61
+
62
+ def get_tracked_data(data)
63
+ return [] if data.empty?
64
+ dates = data.collect { |d| d['date'] }
65
+ FreshBooks::TimeEntry.list('project_id' => project.project_id, 'task_id' => task.task_id, 'date_from' => dates.min, 'date_to' => dates.max)
66
+ end
67
+
68
+ def create_entry(entry_data, time_entry = FreshBooks::TimeEntry.new)
69
+ time_entry ||= FreshBooks::TimeEntry.new
70
+
63
71
  time_entry.project_id = project.project_id
64
72
  time_entry.task_id = task.task_id
65
73
  time_entry.date = entry_data['date']
66
74
  time_entry.hours = entry_data['hours']
67
75
  time_entry.notes = entry_data['notes']
68
-
69
- result = time_entry.create
70
-
76
+
77
+ method = time_entry.time_entry_id ? :update : :create
78
+ result = time_entry.send(method)
79
+
71
80
  if result
72
81
  true
73
82
  else
@@ -2,26 +2,29 @@ module Freshtrack
2
2
  module TimeCollector
3
3
  module PunchyTemplate
4
4
  attr_reader :options
5
-
5
+
6
6
  def initialize(options = {})
7
7
  @options = options
8
8
  end
9
-
9
+
10
10
  def condense_time_data(time_data)
11
11
  date_data = times_to_dates(time_data)
12
12
  group_date_data(date_data)
13
13
  end
14
-
14
+
15
15
  def times_to_dates(time_data)
16
- time_data.each do |td|
16
+ time_data.collect do |td|
17
17
  punch_in = td.delete('in')
18
18
  punch_out = td.delete('out')
19
19
 
20
- td['date'] = punch_in.to_date
21
- td['hours'] = (punch_out - punch_in).secs_to_hours
22
- end
20
+ if punch_out
21
+ td['date'] = punch_in.to_date
22
+ td['hours'] = (punch_out - punch_in).secs_to_hours
23
+ td
24
+ end
25
+ end.compact
23
26
  end
24
-
27
+
25
28
  def group_date_data(date_data)
26
29
  separator = '-' * 20
27
30
  grouped = date_data.group_by { |x| x['date'] }
@@ -1,7 +1,7 @@
1
1
  module Freshtrack #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
- MINOR = 6
4
+ MINOR = 7
5
5
  TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
@@ -2,6 +2,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
2
2
 
3
3
  Thing = FreshBooks::BaseObject.new(:attr)
4
4
  ThingDeal = FreshBooks::BaseObject.new(:attr)
5
+ OtherThing = FreshBooks::BaseObject.new(:attr, :other, :more, :stuff)
5
6
 
6
7
  describe FreshBooks::BaseObject do
7
8
  it 'should have a mapping function for Date' do
@@ -42,28 +43,56 @@ describe FreshBooks::BaseObject do
42
43
  @func.call(@val).should == true
43
44
  end
44
45
  end
45
-
46
+
46
47
  describe 'converting an instance to XML' do
47
48
  before do
48
49
  @thing = Thing.new
49
50
  end
50
-
51
+
51
52
  it 'should use the elem name' do
52
53
  @thing.should.receive(:elem_name)
53
54
  @thing.to_xml
54
55
  end
56
+
57
+ describe 'selective attribute inclusion' do
58
+ before do
59
+ @other_thing = OtherThing.new
60
+ @other_thing.attr = 'blah'
61
+ @other_thing.other = 'something'
62
+ @other_thing.more = 'things'
63
+ @other_thing.stuff = 'this'
64
+ end
65
+
66
+ it 'should include all attributes' do
67
+ xml = @other_thing.to_xml.to_s
68
+ xml.should.match(Regexp.new('<attr>blah</attr>'))
69
+ xml.should.match(Regexp.new('<other>something</other>'))
70
+ xml.should.match(Regexp.new('<more>things</more>'))
71
+ xml.should.match(Regexp.new('<stuff>this</stuff>'))
72
+ end
73
+
74
+ it 'should remove attributes marked for exclusion' do
75
+ OtherThing::EXCLUDE_XML_ATTRIBUTES = %w[other stuff]
76
+
77
+ xml = @other_thing.to_xml.to_s
78
+ xml.should.match(Regexp.new('<attr>blah</attr>'))
79
+ xml.should.not.match(Regexp.new('<other>something</other>'))
80
+ xml.should.match(Regexp.new('<more>things</more>'))
81
+ xml.should.not.match(Regexp.new('<stuff>this</stuff>'))
82
+ end
83
+ end
55
84
  end
56
-
85
+
57
86
  describe 'getting the elem name' do
58
87
  before do
59
88
  @thing = Thing.new
60
89
  @thing_deal = ThingDeal.new
61
90
  end
62
-
91
+
63
92
  it 'should be the class name, downcased' do
64
93
  @thing.elem_name.should == 'thing'
65
94
  end
66
-
95
+
67
96
  it 'should be underscored if the class name has CamelCase' do
68
97
  @thing_deal.elem_name.should == 'thing_deal'
69
98
  end
@@ -355,33 +355,35 @@ describe Freshtrack do
355
355
  lambda { Freshtrack.collector }.should.raise(LoadError)
356
356
  end
357
357
  end
358
-
358
+
359
359
  describe 'tracking time' do
360
360
  before do
361
361
  @project_name = :proj
362
362
  @data = []
363
+ @tracked = []
363
364
  Freshtrack.stub!(:get_data).and_return(@data)
365
+ Freshtrack.stub!(:get_tracked_data).and_return(@tracked)
364
366
  end
365
-
367
+
366
368
  it 'should require an argument' do
367
369
  lambda { Freshtrack.track }.should.raise(ArgumentError)
368
370
  end
369
-
371
+
370
372
  it 'should accept an argument' do
371
373
  lambda { Freshtrack.track(@project_name) }.should.not.raise(ArgumentError)
372
374
  end
373
-
375
+
374
376
  it 'should accept options' do
375
377
  lambda { Freshtrack.track(@project_name, :before => Time.now) }.should.not.raise(ArgumentError)
376
378
  end
377
-
379
+
378
380
  it 'should get data for supplied project' do
379
381
  Freshtrack.should.receive(:get_data).and_return(@data) do |project, _|
380
382
  project.should == @project_name
381
383
  end
382
384
  Freshtrack.track(@project_name)
383
385
  end
384
-
386
+
385
387
  it 'should pass options on when getting data' do
386
388
  options = { :after => Time.now - 12345 }
387
389
  Freshtrack.should.receive(:get_data).and_return(@data) do |project, opts|
@@ -390,7 +392,7 @@ describe Freshtrack do
390
392
  end
391
393
  Freshtrack.track(@project_name, options)
392
394
  end
393
-
395
+
394
396
  it 'should default options to an empty hash' do
395
397
  Freshtrack.should.receive(:get_data).and_return(@data) do |project, opts|
396
398
  project.should == @project_name
@@ -398,112 +400,343 @@ describe Freshtrack do
398
400
  end
399
401
  Freshtrack.track(@project_name)
400
402
  end
401
-
402
- it 'should create entries for project data' do
403
- 2.times do
404
- ent = mock('entry data')
405
- @data.push(ent)
406
- Freshtrack.should.receive(:create_entry).with(ent)
403
+
404
+ it 'should get already-tracked time, passing the data along' do
405
+ Freshtrack.should.receive(:get_tracked_data).with(@data)
406
+ Freshtrack.track(@project_name)
407
+ end
408
+
409
+ it 'should create entries for project data, matching them with the already-tracked time by date' do
410
+ data = Array.new(5) { |i| { 'date' => Date.today - i*2 } }
411
+ tracked = Array.new(5) { |i| te = FreshBooks::TimeEntry.new; te.date = Date.today - i*3; te }
412
+ Freshtrack.stub!(:get_data).and_return(data)
413
+ Freshtrack.stub!(:get_tracked_data).and_return(tracked)
414
+
415
+ matches = {
416
+ data[0] => tracked[0],
417
+ data[1] => nil,
418
+ data[2] => nil,
419
+ data[3] => tracked[2],
420
+ data[4] => nil
421
+ }
422
+
423
+ matches.each do |data, entry|
424
+ Freshtrack.should.receive(:create_entry).with(data, entry)
407
425
  end
408
426
  Freshtrack.track(@project_name)
409
427
  end
410
428
  end
411
-
429
+
430
+ describe 'getting already-tracked time' do
431
+ before do
432
+ @data = [{ 'date' => Date.today - 3 }]
433
+ @tracked = []
434
+ FreshBooks::TimeEntry.stub!(:list).and_return(@tracked)
435
+
436
+ @project = mock('project', :project_id => mock('project id'))
437
+ @task = mock('task', :task_id => mock('task id'))
438
+ Freshtrack.stub!(:project).and_return(@project)
439
+ Freshtrack.stub!(:task).and_return(@task)
440
+ end
441
+
442
+ it 'should accept data' do
443
+ lambda { Freshtrack.get_tracked_data(@data) }.should.not.raise(ArgumentError)
444
+ end
445
+
446
+ it 'should require data' do
447
+ lambda { Freshtrack.get_tracked_data }.should.raise(ArgumentError)
448
+ end
449
+
450
+ it 'should get a list of time entries based upon the data' do
451
+ data = Array.new(3) { |i| { 'date' => Date.today - i*3 } } + Array.new(3) { |i| { 'date' => Date.today - 5 + i*2 } }
452
+ data_options = { 'project_id' => @project.project_id, 'task_id' => @task.task_id }
453
+ data_options['date_from'] = data.collect { |d| d['date'] }.min
454
+ data_options['date_to'] = data.collect { |d| d['date'] }.max
455
+
456
+ FreshBooks::TimeEntry.should.receive(:list).with(data_options)
457
+ Freshtrack.get_tracked_data(data)
458
+ end
459
+
460
+ it 'should return the list of time entries' do
461
+ entries = Object.new
462
+ FreshBooks::TimeEntry.stub!(:list).and_return(entries)
463
+ Freshtrack.get_tracked_data(@data).should == entries
464
+ end
465
+
466
+ it 'should not bother getting data for empty input' do
467
+ FreshBooks::TimeEntry.should.receive(:list).never
468
+ Freshtrack.get_tracked_data([])
469
+ end
470
+
471
+ it 'should return an empty list for empty input' do
472
+ Freshtrack.get_tracked_data([]).should == []
473
+ end
474
+ end
475
+
412
476
  describe 'creating an entry' do
413
477
  before do
414
478
  @date = Date.today - 3
415
479
  @hours = 5.67
416
480
  @notes = 'notes for the time entry'
417
481
  @entry_data = { 'date' => @date, 'hours' => @hours, 'notes' => @notes }
418
- @time_entry = mock('time entry', :project_id= => nil, :task_id= => nil, :date= => nil, :hours= => nil, :notes= => nil, :create => true)
482
+ @time_entry = FreshBooks::TimeEntry.new
483
+ @time_entry.stub!(:create)
484
+ @time_entry.stub!(:update)
419
485
  FreshBooks::TimeEntry.stub!(:new).and_return(@time_entry)
420
-
486
+
421
487
  @project = mock('project', :project_id => mock('project id'))
422
488
  @task = mock('task', :task_id => mock('task id'))
423
489
  Freshtrack.stub!(:project).and_return(@project)
424
490
  Freshtrack.stub!(:task).and_return(@task)
425
-
491
+
426
492
  STDERR.stub!(:puts)
427
493
  end
428
-
494
+
429
495
  it 'should require an argument' do
430
496
  lambda { Freshtrack.create_entry }.should.raise(ArgumentError)
431
497
  end
432
-
498
+
433
499
  it 'should accept an argument' do
434
500
  lambda { Freshtrack.create_entry(@entry_data) }.should.not.raise(ArgumentError)
435
501
  end
436
-
437
- it 'should instantiate a new time entry' do
438
- FreshBooks::TimeEntry.should.receive(:new).and_return(@time_entry)
439
- Freshtrack.create_entry(@entry_data)
502
+
503
+ it 'should accept a time entry argument' do
504
+ lambda { Freshtrack.create_entry(@entry_data, @time_entry) }.should.not.raise(ArgumentError)
440
505
  end
441
-
442
- describe 'with the time entry instance' do
443
- it 'should set the project' do
444
- @time_entry.should.receive(:project_id=).with(@project.project_id)
506
+
507
+ describe 'when given no time entry argument' do
508
+ it 'should instantiate a new time entry' do
509
+ FreshBooks::TimeEntry.should.receive(:new).and_return(@time_entry)
445
510
  Freshtrack.create_entry(@entry_data)
446
511
  end
447
-
448
- it 'should set the task' do
449
- @time_entry.should.receive(:task_id=).with(@task.task_id)
450
- Freshtrack.create_entry(@entry_data)
512
+
513
+ describe 'with the time entry instance' do
514
+ it 'should set the project' do
515
+ @time_entry.should.receive(:project_id=).with(@project.project_id)
516
+ Freshtrack.create_entry(@entry_data)
517
+ end
518
+
519
+ it 'should set the task' do
520
+ @time_entry.should.receive(:task_id=).with(@task.task_id)
521
+ Freshtrack.create_entry(@entry_data)
522
+ end
523
+
524
+ it 'should set the date' do
525
+ @time_entry.should.receive(:date=).with(@date)
526
+ Freshtrack.create_entry(@entry_data)
527
+ end
528
+
529
+ it 'should set the hours' do
530
+ @time_entry.should.receive(:hours=).with(@hours)
531
+ Freshtrack.create_entry(@entry_data)
532
+ end
533
+
534
+ it 'should set the notes' do
535
+ @time_entry.should.receive(:notes=).with(@notes)
536
+ Freshtrack.create_entry(@entry_data)
537
+ end
451
538
  end
452
-
453
- it 'should set the date' do
454
- @time_entry.should.receive(:date=).with(@date)
539
+
540
+ it 'should create the time entry' do
541
+ @time_entry.should.receive(:create)
455
542
  Freshtrack.create_entry(@entry_data)
456
543
  end
457
-
458
- it 'should set the hours' do
459
- @time_entry.should.receive(:hours=).with(@hours)
544
+
545
+ it 'should not update the time entry' do
546
+ @time_entry.should.receive(:update).never
460
547
  Freshtrack.create_entry(@entry_data)
461
548
  end
462
-
463
- it 'should set the notes' do
464
- @time_entry.should.receive(:notes=).with(@notes)
465
- Freshtrack.create_entry(@entry_data)
549
+
550
+ describe 'successfully' do
551
+ before do
552
+ @time_entry.stub!(:create).and_return(5)
553
+ end
554
+
555
+ it 'should be silent' do
556
+ STDERR.should.receive(:puts).never
557
+ Freshtrack.create_entry(@entry_data)
558
+ end
559
+
560
+ it 'should return true' do
561
+ Freshtrack.create_entry(@entry_data).should == true
562
+ end
563
+ end
564
+
565
+ describe 'unsuccessfully' do
566
+ before do
567
+ @time_entry.stub!(:create).and_return(nil)
568
+ end
569
+
570
+ it 'should output an indication' do
571
+ STDERR.should.receive(:puts) do |arg|
572
+ arg.should.match(/#{@date.to_s}/)
573
+ end
574
+ Freshtrack.create_entry(@entry_data)
575
+ end
576
+
577
+ it 'should return nil' do
578
+ Freshtrack.create_entry(@entry_data).should.be.nil
579
+ end
466
580
  end
467
581
  end
468
-
469
- it 'should create the time entry' do
470
- @time_entry.should.receive(:create)
471
- Freshtrack.create_entry(@entry_data)
472
- end
473
-
474
- describe 'successfully' do
475
- before do
476
- @time_entry.stub!(:create).and_return(5)
582
+
583
+ describe 'when given a nil time entry argument' do
584
+ it 'should instantiate a new time entry' do
585
+ FreshBooks::TimeEntry.should.receive(:new).and_return(@time_entry)
586
+ Freshtrack.create_entry(@entry_data, nil)
477
587
  end
478
-
479
- it 'should be silent' do
480
- STDERR.should.receive(:puts).never
588
+
589
+ describe 'with the time entry instance' do
590
+ it 'should set the project' do
591
+ @time_entry.should.receive(:project_id=).with(@project.project_id)
592
+ Freshtrack.create_entry(@entry_data, nil)
593
+ end
594
+
595
+ it 'should set the task' do
596
+ @time_entry.should.receive(:task_id=).with(@task.task_id)
597
+ Freshtrack.create_entry(@entry_data, nil)
598
+ end
599
+
600
+ it 'should set the date' do
601
+ @time_entry.should.receive(:date=).with(@date)
602
+ Freshtrack.create_entry(@entry_data, nil)
603
+ end
604
+
605
+ it 'should set the hours' do
606
+ @time_entry.should.receive(:hours=).with(@hours)
607
+ Freshtrack.create_entry(@entry_data, nil)
608
+ end
609
+
610
+ it 'should set the notes' do
611
+ @time_entry.should.receive(:notes=).with(@notes)
612
+ Freshtrack.create_entry(@entry_data, nil)
613
+ end
614
+ end
615
+
616
+ it 'should create the time entry' do
617
+ @time_entry.should.receive(:create)
618
+ Freshtrack.create_entry(@entry_data, nil)
619
+ end
620
+
621
+ it 'should not update the time entry' do
622
+ @time_entry.should.receive(:update).never
481
623
  Freshtrack.create_entry(@entry_data)
482
624
  end
483
-
484
- it 'should return true' do
485
- Freshtrack.create_entry(@entry_data).should == true
625
+
626
+ describe 'successfully' do
627
+ before do
628
+ @time_entry.stub!(:create).and_return(5)
629
+ end
630
+
631
+ it 'should be silent' do
632
+ STDERR.should.receive(:puts).never
633
+ Freshtrack.create_entry(@entry_data, nil)
634
+ end
635
+
636
+ it 'should return true' do
637
+ Freshtrack.create_entry(@entry_data, nil).should == true
638
+ end
639
+ end
640
+
641
+ describe 'unsuccessfully' do
642
+ before do
643
+ @time_entry.stub!(:create).and_return(nil)
644
+ end
645
+
646
+ it 'should output an indication' do
647
+ STDERR.should.receive(:puts) do |arg|
648
+ arg.should.match(/#{@date.to_s}/)
649
+ end
650
+ Freshtrack.create_entry(@entry_data, nil)
651
+ end
652
+
653
+ it 'should return nil' do
654
+ Freshtrack.create_entry(@entry_data, nil).should.be.nil
655
+ end
486
656
  end
487
657
  end
488
-
489
- describe 'unsuccessfully' do
658
+
659
+ describe 'when given an existing time entry argument' do
490
660
  before do
491
- @time_entry.stub!(:create).and_return(nil)
661
+ @time_entry.time_entry_id = 293
492
662
  end
493
-
494
- it 'should output an indication' do
495
- STDERR.should.receive(:puts) do |arg|
496
- arg.should.match(/#{@date.to_s}/)
663
+
664
+ it 'should not instantiate a new time entry' do
665
+ FreshBooks::TimeEntry.should.receive(:new).never
666
+ Freshtrack.create_entry(@entry_data, @time_entry)
667
+ end
668
+
669
+ describe 'with the time entry instance' do
670
+ it 'should set the project' do
671
+ @time_entry.should.receive(:project_id=).with(@project.project_id)
672
+ Freshtrack.create_entry(@entry_data, @time_entry)
673
+ end
674
+
675
+ it 'should set the task' do
676
+ @time_entry.should.receive(:task_id=).with(@task.task_id)
677
+ Freshtrack.create_entry(@entry_data, @time_entry)
678
+ end
679
+
680
+ it 'should set the date' do
681
+ @time_entry.should.receive(:date=).with(@date)
682
+ Freshtrack.create_entry(@entry_data, @time_entry)
683
+ end
684
+
685
+ it 'should set the hours' do
686
+ @time_entry.should.receive(:hours=).with(@hours)
687
+ Freshtrack.create_entry(@entry_data, @time_entry)
688
+ end
689
+
690
+ it 'should set the notes' do
691
+ @time_entry.should.receive(:notes=).with(@notes)
692
+ Freshtrack.create_entry(@entry_data, @time_entry)
497
693
  end
498
- Freshtrack.create_entry(@entry_data)
499
694
  end
500
-
501
- it 'should return nil' do
502
- Freshtrack.create_entry(@entry_data).should.be.nil
695
+
696
+ it 'should update the time entry' do
697
+ @time_entry.should.receive(:update)
698
+ Freshtrack.create_entry(@entry_data, @time_entry)
699
+ end
700
+
701
+ it 'should not create the time entry' do
702
+ @time_entry.should.receive(:create).never
703
+ Freshtrack.create_entry(@entry_data, @time_entry)
704
+ end
705
+
706
+ describe 'successfully' do
707
+ before do
708
+ @time_entry.stub!(:update).and_return(true)
709
+ end
710
+
711
+ it 'should be silent' do
712
+ STDERR.should.receive(:puts).never
713
+ Freshtrack.create_entry(@entry_data, @time_entry)
714
+ end
715
+
716
+ it 'should return true' do
717
+ Freshtrack.create_entry(@entry_data, @time_entry).should == true
718
+ end
719
+ end
720
+
721
+ describe 'unsuccessfully' do
722
+ before do
723
+ @time_entry.stub!(:update).and_return(false)
724
+ end
725
+
726
+ it 'should output an indication' do
727
+ STDERR.should.receive(:puts) do |arg|
728
+ arg.should.match(/#{@date.to_s}/)
729
+ end
730
+ Freshtrack.create_entry(@entry_data, @time_entry)
731
+ end
732
+
733
+ it 'should return nil' do
734
+ Freshtrack.create_entry(@entry_data, @time_entry).should.be.nil
735
+ end
503
736
  end
504
737
  end
505
738
  end
506
-
739
+
507
740
  it 'should list open invoices' do
508
741
  Freshtrack.should.respond_to(:open_invoices)
509
742
  end
@@ -178,6 +178,15 @@ describe Freshtrack::TimeCollector::OneInchPunch do
178
178
  result = result.first
179
179
  result['hours'].should == 1.5
180
180
  end
181
+
182
+ it 'should ignore any time data that is not finished' do
183
+ @time_data.push({ 'in' => Time.local(2012, 1, 25, 6, 25, 0), 'out' => Time.local(2012, 1, 25, 7, 55, 0) })
184
+ @time_data.push({ 'in' => Time.local(2012, 3, 25, 6, 25, 0)})
185
+
186
+ result = @collector.times_to_dates(@time_data)
187
+ result.length.should == 1
188
+ result.first['hours'].should == 1.5
189
+ end
181
190
  end
182
191
 
183
192
  it 'should group date data' do
@@ -224,6 +224,15 @@ describe Freshtrack::TimeCollector::Punch do
224
224
  result = result.first
225
225
  result['hours'].should == 1.5
226
226
  end
227
+
228
+ it 'should ignore any time data that is not finished' do
229
+ @time_data.push({ 'in' => Time.local(2012, 1, 25, 6, 25, 0), 'out' => Time.local(2012, 1, 25, 7, 55, 0) })
230
+ @time_data.push({ 'in' => Time.local(2012, 3, 25, 6, 25, 0)})
231
+
232
+ result = @collector.times_to_dates(@time_data)
233
+ result.length.should == 1
234
+ result.first['hours'].should == 1.5
235
+ end
227
236
  end
228
237
 
229
238
  it 'should group date data' do
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: freshtrack
3
3
  version: !ruby/object:Gem::Version
4
- hash: 7
4
+ hash: 3
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 6
8
+ - 7
9
9
  - 0
10
- version: 0.6.0
10
+ version: 0.7.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: 2012-03-30 00:00:00 Z
18
+ date: 2012-04-11 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: bacon