freshtrack 0.6.0 → 0.7.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 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