freshtrack 0.3.0 → 0.4.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,11 @@
1
+ == 0.4.0 2008-08-29
2
+
3
+ * 1 minor enhancement:
4
+ * Extracted the time-collection concern from Freshtrack itself and created time collectors:
5
+ * one_inch_punch
6
+ * punch
7
+ * More can be easily made
8
+
1
9
  == 0.3.0 2008-08-27
2
10
 
3
11
  * 1 minor enhancement:
data/Manifest.txt CHANGED
@@ -12,6 +12,9 @@ lib/freshtrack/core_ext.rb
12
12
  lib/freshtrack/core_ext/array.rb
13
13
  lib/freshtrack/core_ext/numeric.rb
14
14
  lib/freshtrack/core_ext/time.rb
15
+ lib/freshtrack/time_collectors/one_inch_punch.rb
16
+ lib/freshtrack/time_collectors/punch.rb
17
+ lib/freshtrack/time_collectors/punchy_template.rb
15
18
  lib/freshbooks/extensions.rb
16
19
  lib/freshbooks/extensions/base_object.rb
17
20
  lib/freshbooks/extensions/project.rb
@@ -21,15 +24,17 @@ log/debug.log
21
24
  script/destroy
22
25
  script/generate
23
26
  setup.rb
24
- spec/base_object_spec.rb
27
+ spec/core_ext/array_spec.rb
28
+ spec/core_ext/numeric_spec.rb
29
+ spec/core_ext/time_spec.rb
30
+ spec/time_collectors/one_inch_punch_spec.rb
31
+ spec/time_collectors/punch_spec.rb
32
+ spec/freshbooks/base_object_spec.rb
33
+ spec/freshbooks/project_spec.rb
34
+ spec/freshbooks/task_spec.rb
35
+ spec/freshbooks/time_entry_spec.rb
25
36
  spec/freshtrack_command_spec.rb
26
37
  spec/freshtrack_spec.rb
27
- spec/project_spec.rb
28
- spec/task_spec.rb
29
- spec/time_entry_spec.rb
30
- spec/array_spec.rb
31
- spec/numeric_spec.rb
32
- spec/time_spec.rb
33
38
  spec/spec.opts
34
39
  spec/spec_helper.rb
35
40
  tasks/deployment.rake
data/README.txt CHANGED
@@ -8,7 +8,7 @@ Freshtrack is used to automatically create time entries in FreshBooks from your
8
8
 
9
9
  * Simple and easy to use
10
10
 
11
- * Only works with data from one_inch_punch
11
+ * Modular time-collector system
12
12
 
13
13
  == SYNOPSIS:
14
14
 
@@ -27,12 +27,13 @@ Freshtrack is used to automatically create time entries in FreshBooks from your
27
27
 
28
28
  == REQUIREMENTS:
29
29
 
30
- * one_inch_punch (gem)
30
+ * a time collector (see below)
31
31
  * freshbooks (gem)
32
32
  * A FreshBooks account
33
33
  * a configuration file located at ~/.freshtrack.yml and looking like
34
34
 
35
35
  ---
36
+ collector: one_inch_punch
36
37
  company: Company Name
37
38
  token: API Token
38
39
  project_task_mapping:
@@ -42,6 +43,9 @@ Freshtrack is used to automatically create time entries in FreshBooks from your
42
43
 
43
44
  (The 'Company Name' is the XXX in 'XXX.freshbooks.com'. The 'project_name' is the XXX in 'punch list XXX'.)
44
45
 
46
+ The 'collector' is what freshtrack will use to gather the time data that will end up as FreshBooks time entries.
47
+ Freshtrack ships with two collectors: 'punch' and 'one_inch_punch'. These are both gems that can be installed (by `gem install [collector name]`) and used without much effort. If these time-tracking tools aren't to your liking, you are free to write your own collector. Further documentation on that is forthcoming, but for now just take a look at the two collectors that already exist.
48
+
45
49
  == INSTALL:
46
50
 
47
51
  * gem install freshtrack
data/config/hoe.rb CHANGED
@@ -60,8 +60,7 @@ hoe = Hoe.new(GEM_NAME, VERS) do |p|
60
60
  # == Optional
61
61
  p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
62
62
  p.extra_deps = [ # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
63
- ['freshbooks', '= 2.1'],
64
- ['one_inch_punch', '>= 0.0.3']
63
+ ['freshbooks', '= 2.1']
65
64
  ]
66
65
 
67
66
  #p.spec_extras = {} # A hash of extra values to set in the gemspec.
@@ -0,0 +1,16 @@
1
+ require 'freshtrack/time_collectors/punchy_template'
2
+ require 'punch'
3
+
4
+ module Freshtrack
5
+ module TimeCollector
6
+ class OneInchPunch
7
+ include PunchyTemplate
8
+
9
+ def get_time_data(project)
10
+ ::Punch.load
11
+ time_data = ::Punch.list(project, options)
12
+ condense_time_data(time_data)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,26 @@
1
+ require 'freshtrack/time_collectors/punchy_template'
2
+
3
+ module Freshtrack
4
+ module TimeCollector
5
+ class Punch
6
+ include PunchyTemplate
7
+
8
+ def get_time_data(project)
9
+ time_data = IO.read("| punch list #{project} #{option_str}")
10
+ converted = convert_time_data(time_data)
11
+ condense_time_data(converted)
12
+ end
13
+
14
+ def convert_time_data(time_data)
15
+ YAML.load(time_data)
16
+ end
17
+
18
+
19
+ private
20
+
21
+ def option_str
22
+ options.collect { |key, val| "--#{key} #{val.strftime('%Y-%m-%dT%H:%M:%S%z')}" }.join(' ')
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,38 @@
1
+ module Freshtrack
2
+ module TimeCollector
3
+ module PunchyTemplate
4
+ attr_reader :options
5
+
6
+ def initialize(options = {})
7
+ @options = options
8
+ end
9
+
10
+ def condense_time_data(time_data)
11
+ date_data = times_to_dates(time_data)
12
+ group_date_data(date_data)
13
+ end
14
+
15
+ def times_to_dates(time_data)
16
+ time_data.each do |td|
17
+ punch_in = td.delete('in')
18
+ punch_out = td.delete('out')
19
+
20
+ td['date'] = punch_in.to_date
21
+ td['hours'] = (punch_out - punch_in).secs_to_hours
22
+ end
23
+ end
24
+
25
+ def group_date_data(date_data)
26
+ separator = '-' * 20
27
+ grouped = date_data.group_by { |x| x['date'] }
28
+ grouped.sort.inject([]) do |arr, (date, data)|
29
+ this_date = { 'date' => date }
30
+ this_date['hours'] = data.inject(0) { |sum, x| sum + x['hours'] }
31
+ this_date['hours'] = ('%.2f' % this_date['hours']).to_f
32
+ this_date['notes'] = data.collect { |x| x['log'].join("\n") }.join("\n" + separator + "\n")
33
+ arr + [this_date]
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,7 +1,7 @@
1
1
  module Freshtrack #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
- MINOR = 3
4
+ MINOR = 4
5
5
  TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
data/lib/freshtrack.rb CHANGED
@@ -2,7 +2,6 @@ $:.unshift File.dirname(__FILE__)
2
2
  require 'freshbooks/extensions'
3
3
  require 'freshtrack/core_ext'
4
4
  require 'yaml'
5
- require 'punch'
6
5
 
7
6
  module Freshtrack
8
7
  class << self
@@ -37,42 +36,9 @@ module Freshtrack
37
36
  raise unless @task
38
37
  end
39
38
 
40
- def get_time_data(project_name, options = {})
41
- Punch.load
42
- time_data = Punch.list(project_name, options)
43
- condense_time_data(time_data)
44
- end
45
-
46
- def condense_time_data(time_data)
47
- date_data = times_to_dates(time_data)
48
- group_date_data(date_data)
49
- end
50
-
51
- def times_to_dates(time_data)
52
- time_data.each do |td|
53
- punch_in = td.delete('in')
54
- punch_out = td.delete('out')
55
-
56
- td['date'] = punch_in.to_date
57
- td['hours'] = (punch_out - punch_in).secs_to_hours
58
- end
59
- end
60
-
61
- def group_date_data(date_data)
62
- separator = '-' * 20
63
- grouped = date_data.group_by { |x| x['date'] }
64
- grouped.sort.inject([]) do |arr, (date, data)|
65
- this_date = { 'date' => date }
66
- this_date['hours'] = data.inject(0) { |sum, x| sum + x['hours'] }
67
- this_date['hours'] = ('%.2f' % this_date['hours']).to_f
68
- this_date['notes'] = data.collect { |x| x['log'].join("\n") }.join("\n" + separator + "\n")
69
- arr + [this_date]
70
- end
71
- end
72
-
73
39
  def get_data(project_name, options = {})
74
40
  get_project_data(project_name)
75
- get_time_data(project_name, options)
41
+ collector(options).get_time_data(project_name)
76
42
  end
77
43
 
78
44
  def track(project_name, options = {})
@@ -100,5 +66,13 @@ module Freshtrack
100
66
  nil
101
67
  end
102
68
  end
69
+
70
+ def collector(options = {})
71
+ collector_name = config['collector']
72
+ class_name = collector_name.capitalize.gsub(/([a-z])_([a-z])/) { "#{$1}#{$2.upcase}" }
73
+ require "freshtrack/time_collectors/#{collector_name}"
74
+ klass = Freshtrack::TimeCollector.const_get(class_name)
75
+ klass.new(options)
76
+ end
103
77
  end
104
78
  end
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/spec_helper.rb'
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
2
2
 
3
3
  describe Array do
4
4
  it 'should be groupable' do
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/spec_helper.rb'
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
2
2
 
3
3
  describe Integer do
4
4
  it 'should be convertible from seconds to hours' do
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/spec_helper.rb'
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
2
2
 
3
3
  describe Time do
4
4
  it 'should be convertible to date' do
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/spec_helper.rb'
1
+ 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)
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/spec_helper.rb'
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
2
2
 
3
3
  describe FreshBooks::Project do
4
4
  before :each do
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/spec_helper.rb'
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
2
2
 
3
3
  describe FreshBooks::Task do
4
4
  before :each do
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/spec_helper.rb'
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
2
2
 
3
3
  describe FreshBooks::TimeEntry do
4
4
  before :each do
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/spec_helper.rb'
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper.rb')
2
2
 
3
3
  describe 'freshtrack command' do
4
4
  def run_command(*args)
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/spec_helper.rb'
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper.rb')
2
2
 
3
3
  describe Freshtrack do
4
4
  describe 'loading configuration' do
@@ -128,273 +128,134 @@ describe Freshtrack do
128
128
  end
129
129
  end
130
130
 
131
- describe 'getting time data' do
131
+ describe 'getting data' do
132
132
  before :each do
133
133
  @project_name = :proj
134
- @time_data = stub('time data')
135
- Punch.stubs(:load)
136
- Punch.stubs(:list).returns(@time_data)
137
- Freshtrack.stubs(:condense_time_data)
134
+ Freshtrack.stubs(:get_project_data)
135
+ @collector = stub('collector', :get_time_data => nil)
136
+ Freshtrack.stubs(:collector).returns(@collector)
138
137
  end
139
138
 
140
139
  it 'should require an argument' do
141
- lambda { Freshtrack.get_time_data }.should raise_error(ArgumentError)
140
+ lambda { Freshtrack.get_data }.should raise_error(ArgumentError)
142
141
  end
143
142
 
144
143
  it 'should accept an argument' do
145
- lambda { Freshtrack.get_time_data(@project_name) }.should_not raise_error(ArgumentError)
144
+ lambda { Freshtrack.get_data(@project_name) }.should_not raise_error(ArgumentError)
146
145
  end
147
146
 
148
147
  it 'should accept options' do
149
- lambda { Freshtrack.get_time_data(@project_name, :after => Time.now) }.should_not raise_error(ArgumentError)
148
+ lambda { Freshtrack.get_data(@project_name, :before => Time.now) }.should_not raise_error(ArgumentError)
150
149
  end
151
150
 
152
- it 'should have punch load the time data' do
153
- Punch.expects(:load)
154
- Freshtrack.get_time_data(@project_name)
151
+ it 'should get project data for supplied project' do
152
+ Freshtrack.expects(:get_project_data).with(@project_name)
153
+ Freshtrack.get_data(@project_name)
155
154
  end
156
155
 
157
- it 'should get the time data from punch' do
158
- Punch.expects(:list)
159
- Freshtrack.get_time_data(@project_name)
156
+ it 'should retrieve a time collector' do
157
+ Freshtrack.expects(:collector).returns(@collector)
158
+ Freshtrack.get_data(@project_name)
160
159
  end
161
160
 
162
- it 'should pass the supplied project when getting the time data' do
163
- Punch.expects(:list).with(@project_name, anything)
164
- Freshtrack.get_time_data(@project_name)
161
+ it 'should get time data for supplied project' do
162
+ @collector.expects(:get_time_data).with(@project_name)
163
+ Freshtrack.get_data(@project_name)
165
164
  end
166
165
 
167
- it 'should pass the supplied options on when getting the time data' do
166
+ it 'should pass the options on when retrieving a time collector' do
168
167
  options = { :after => Time.now - 12345 }
169
- Punch.expects(:list).with(@project_name, options)
170
- Freshtrack.get_time_data(@project_name, options)
171
- end
172
-
173
- it 'should pass an empty hash by default' do
174
- Punch.expects(:list).with(@project_name, {})
175
- Freshtrack.get_time_data(@project_name)
176
- end
177
-
178
- it 'should condense the time data' do
179
- Freshtrack.expects(:condense_time_data).with(@time_data)
180
- Freshtrack.get_time_data(@project_name)
181
- end
182
-
183
- it 'should return the condensed data' do
184
- condensed = stub('condensed time data')
185
- Freshtrack.stubs(:condense_time_data).returns(condensed)
186
- Freshtrack.get_time_data(@project_name).should == condensed
187
- end
188
- end
189
-
190
- describe 'condensing time data' do
191
- before :each do
192
- @time_data = stub('time data')
193
- Freshtrack.stubs(:times_to_dates)
194
- Freshtrack.stubs(:group_date_data)
195
- end
196
-
197
- it 'should require an argument' do
198
- lambda { Freshtrack.condense_time_data }.should raise_error(ArgumentError)
199
- end
200
-
201
- it 'should accept an argument' do
202
- lambda { Freshtrack.condense_time_data(@time_data) }.should_not raise_error(ArgumentError)
203
- end
204
-
205
- it 'should convert times to dates and hour differences' do
206
- Freshtrack.expects(:times_to_dates).with(@time_data)
207
- Freshtrack.condense_time_data(@time_data)
168
+ Freshtrack.expects(:collector).with(options).returns(@collector)
169
+ Freshtrack.get_data(@project_name, options)
208
170
  end
209
171
 
210
- it 'should group date and hour differences' do
211
- date_hour_data = stub('date/hour data')
212
- Freshtrack.stubs(:times_to_dates).returns(date_hour_data)
213
- Freshtrack.expects(:group_date_data).with(date_hour_data)
214
- Freshtrack.condense_time_data(@time_data)
172
+ it 'should default options to an empty hash' do
173
+ Freshtrack.expects(:collector).with({}).returns(@collector)
174
+ Freshtrack.get_data(@project_name)
215
175
  end
216
176
 
217
- it 'should return the grouped date/hour data' do
218
- grouped_dates = stub('grouped date/hour data')
219
- Freshtrack.stubs(:group_date_data).returns(grouped_dates)
220
- Freshtrack.condense_time_data(@time_data).should == grouped_dates
177
+ it 'should return time data' do
178
+ time_data = stub('time data')
179
+ @collector.stubs(:get_time_data).returns(time_data)
180
+ Freshtrack.get_data(@project_name).should == time_data
221
181
  end
222
182
  end
223
183
 
224
- describe 'converting times to dates and hour differences' do
225
- before :each do
226
- @time_data = []
227
- end
228
-
229
- it 'should require an argument' do
230
- lambda { Freshtrack.times_to_dates }.should raise_error(ArgumentError)
231
- end
232
-
233
- it 'should accept an argument' do
234
- lambda { Freshtrack.times_to_dates(@time_data) }.should_not raise_error(ArgumentError)
235
- end
236
-
237
- it 'should return an array' do
238
- Freshtrack.times_to_dates(@time_data).should be_kind_of(Array)
239
- end
240
-
241
- it 'should replace the in/out time data with a single date' do
242
- @time_data.push({ 'in' => Time.local(2008, 1, 25, 6, 25, 0), 'out' => Time.local(2008, 1, 25, 7, 25, 0) })
243
- result = Freshtrack.times_to_dates(@time_data)
244
- result = result.first
245
-
246
- result.should have_key('date')
247
- result.should_not have_key('in')
248
- result.should_not have_key('out')
249
- end
250
-
251
- it 'should make the date appopriate to the time' do
252
- @time_data.push({ 'in' => Time.local(2008, 1, 25, 6, 25, 0), 'out' => Time.local(2008, 1, 25, 7, 25, 0) })
253
- result = Freshtrack.times_to_dates(@time_data)
254
- result = result.first
255
- result['date'].should == Date.civil(2008, 1, 25)
256
- end
257
-
258
- it 'should use the in time date' do
259
- @time_data.push({ 'in' => Time.local(2008, 1, 25, 6, 25, 0), 'out' => Time.local(2008, 1, 26, 7, 25, 0) })
260
- result = Freshtrack.times_to_dates(@time_data)
261
- result = result.first
262
- result['date'].should == Date.civil(2008, 1, 25)
263
- end
264
-
265
- it 'should add hour data' do
266
- @time_data.push({ 'in' => Time.local(2008, 1, 25, 6, 25, 0), 'out' => Time.local(2008, 1, 25, 7, 25, 0) })
267
- result = Freshtrack.times_to_dates(@time_data)
268
- result = result.first
269
- result.should have_key('hours')
184
+ describe 'getting a collector' do
185
+ module Freshtrack
186
+ module TimeCollector
187
+ class Punch
188
+ end
189
+
190
+ class OneInchPunch
191
+ end
192
+ end
270
193
  end
271
194
 
272
- it 'should make the hour data appropriate to the in/out difference' do
273
- @time_data.push({ 'in' => Time.local(2008, 1, 25, 6, 25, 0), 'out' => Time.local(2008, 1, 25, 7, 55, 0) })
274
- result = Freshtrack.times_to_dates(@time_data)
275
- result = result.first
276
- result['hours'].should == 1.5
277
- end
278
- end
279
-
280
- describe 'grouping date data' do
281
195
  before :each do
282
- @date_data = []
196
+ Freshtrack.stubs(:config).returns({'collector' => 'punch' })
197
+ @collector = stub('collector')
198
+ Freshtrack::TimeCollector::Punch.stubs(:new).returns(@collector)
283
199
  end
284
200
 
285
- it 'should require an argument' do
286
- lambda { Freshtrack.group_date_data }.should raise_error(ArgumentError)
201
+ it 'should accept options' do
202
+ lambda { Freshtrack.collector(:before => Time.now) }.should_not raise_error(ArgumentError)
287
203
  end
288
204
 
289
- it 'should accept an argument' do
290
- lambda { Freshtrack.group_date_data(@date_data) }.should_not raise_error(ArgumentError)
205
+ it 'should not require options' do
206
+ lambda { Freshtrack.collector }.should_not raise_error(ArgumentError)
291
207
  end
292
208
 
293
- it 'should return an array' do
294
- Freshtrack.group_date_data(@date_data).should be_kind_of(Array)
209
+ it 'should require a library based on the collector given in the config' do
210
+ Freshtrack.expects(:require).with('freshtrack/time_collectors/punch')
211
+ Freshtrack.collector
295
212
  end
296
213
 
297
- it 'should group the data by date' do
298
- today = Date.today
299
- @date_data.push({ 'date' => today, 'hours' => 0, 'log' => [] })
300
- @date_data.push({ 'date' => today, 'hours' => 0, 'log' => [] })
301
- @date_data.push({ 'date' => today + 1, 'hours' => 0, 'log' => [] })
302
- Freshtrack.group_date_data(@date_data).collect { |x| x['date'] }.should == [today, today + 1]
214
+ it 'should create a collector object based on the collector given in the config' do
215
+ Freshtrack::TimeCollector::Punch.expects(:new)
216
+ Freshtrack.collector
303
217
  end
304
218
 
305
- it 'should return the array sorted by date' do
306
- today = Date.today
307
- @date_data.push({ 'date' => today + 1, 'hours' => 0, 'log' => [] })
308
- @date_data.push({ 'date' => today - 1, 'hours' => 0, 'log' => [] })
309
- @date_data.push({ 'date' => today, 'hours' => 0, 'log' => [] })
310
- @date_data.push({ 'date' => today + 1, 'hours' => 0, 'log' => [] })
311
- Freshtrack.group_date_data(@date_data).collect { |x| x['date'] }.should == [today - 1, today, today + 1]
219
+ it 'should pass the options on when creating a collector object' do
220
+ options = { :before => Time.now }
221
+ Freshtrack::TimeCollector::Punch.expects(:new).with(options)
222
+ Freshtrack.collector(options)
312
223
  end
313
224
 
314
- it 'should add the hours for a particular date' do
315
- today = Date.today
316
- @date_data.push({ 'date' => today, 'hours' => 1, 'log' => [] })
317
- @date_data.push({ 'date' => today, 'hours' => 3, 'log' => [] })
318
- @date_data.push({ 'date' => today + 1, 'hours' => 2, 'log' => [] })
319
- result = Freshtrack.group_date_data(@date_data)
320
-
321
- result[0]['date'].should == today
322
- result[0]['hours'].should == 4
323
-
324
- result[1]['date'].should == today + 1
325
- result[1]['hours'].should == 2
225
+ it 'should pass an empty hash if no options given' do
226
+ Freshtrack::TimeCollector::Punch.expects(:new).with({})
227
+ Freshtrack.collector
326
228
  end
327
229
 
328
- it 'should round the hours to two decimal places' do
329
- today = Date.today
330
- @date_data.push({ 'date' => today, 'hours' => 1.666666666, 'log' => [] })
331
- result = Freshtrack.group_date_data(@date_data)
332
-
333
- result[0]['date'].should == today
334
- result[0]['hours'].should == 1.67
230
+ it 'should return the collector' do
231
+ Freshtrack.collector.should == @collector
335
232
  end
336
233
 
337
- it 'should join the log into notes' do
338
- today = Date.today
339
- @date_data.push({ 'date' => today, 'hours' => 0, 'log' => ['punch in 1', 'punch out 1'] })
340
- @date_data.push({ 'date' => today, 'hours' => 0, 'log' => ['punch in 2', 'punch out 2'] })
341
- @date_data.push({ 'date' => today + 1, 'hours' => 0, 'log' => ['punch in 3', 'punch out 3'] })
342
- result = Freshtrack.group_date_data(@date_data)
343
-
344
- result[0]['date'].should == today
345
- result[0]['notes'].should == "punch in 1\npunch out 1\n--------------------\npunch in 2\npunch out 2"
346
- result[0].should_not have_key('log')
347
-
348
- result[1]['date'].should == today + 1
349
- result[1]['notes'].should == "punch in 3\npunch out 3"
350
- result[1].should_not have_key('log')
351
- end
352
- end
353
-
354
- describe 'getting data' do
355
- before :each do
356
- @project_name = :proj
357
- Freshtrack.stubs(:get_project_data)
358
- Freshtrack.stubs(:get_time_data)
234
+ it 'should error if no collector is given in the config' do
235
+ Freshtrack.stubs(:config).returns({})
236
+ lambda { Freshtrack.collector }.should raise_error
359
237
  end
360
238
 
361
- it 'should require an argument' do
362
- lambda { Freshtrack.get_data }.should raise_error(ArgumentError)
239
+ it "should accept a collector of 'punch'" do
240
+ Freshtrack.stubs(:config).returns({'collector' => 'punch'})
241
+ lambda { Freshtrack.collector }.should_not raise_error
363
242
  end
364
243
 
365
- it 'should accept an argument' do
366
- lambda { Freshtrack.get_data(@project_name) }.should_not raise_error(ArgumentError)
367
- end
368
-
369
- it 'should accept options' do
370
- lambda { Freshtrack.get_data(@project_name, :before => Time.now) }.should_not raise_error(ArgumentError)
371
- end
372
-
373
- it 'should get project data for supplied project' do
374
- Freshtrack.expects(:get_project_data).with(@project_name)
375
- Freshtrack.get_data(@project_name)
376
- end
377
-
378
- it 'should get time data for supplied project' do
379
- Freshtrack.expects(:get_time_data).with(@project_name, anything)
380
- Freshtrack.get_data(@project_name)
381
- end
382
-
383
- it 'should pass options on when getting time data' do
384
- options = { :after => Time.now - 12345 }
385
- Freshtrack.expects(:get_time_data).with(@project_name, options)
386
- Freshtrack.get_data(@project_name, options)
244
+ it "should accept a collector of 'one_inch_punch'" do
245
+ Freshtrack.stubs(:config).returns({'collector' => 'one_inch_punch'})
246
+ Freshtrack::TimeCollector::OneInchPunch.stubs(:new)
247
+ lambda { Freshtrack.collector }.should_not raise_error
387
248
  end
388
249
 
389
- it 'should default options to an empty hash' do
390
- Freshtrack.expects(:get_time_data).with(@project_name, {})
391
- Freshtrack.get_data(@project_name)
250
+ it 'should correctly camel-case a collector name' do
251
+ Freshtrack.stubs(:config).returns({'collector' => 'one_inch_punch'})
252
+ Freshtrack::TimeCollector::OneInchPunch.expects(:new)
253
+ Freshtrack.collector
392
254
  end
393
255
 
394
- it 'should return time data' do
395
- time_data = stub('time data')
396
- Freshtrack.stubs(:get_time_data).returns(time_data)
397
- Freshtrack.get_data(@project_name).should == time_data
256
+ it 'should error if an unknown collector is given in the config' do
257
+ Freshtrack.stubs(:config).returns({'collector' => 'blam'})
258
+ lambda { Freshtrack.collector }.should raise_error
398
259
  end
399
260
  end
400
261
 
@@ -0,0 +1,258 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
2
+ require 'freshtrack/time_collectors/one_inch_punch'
3
+
4
+ describe Freshtrack::TimeCollector::OneInchPunch do
5
+ before :each do
6
+ @collector = Freshtrack::TimeCollector::OneInchPunch.new
7
+ end
8
+
9
+ describe 'when initialized' do
10
+ it 'should accept options' do
11
+ lambda { Freshtrack::TimeCollector::OneInchPunch.new(:before => Time.now) }.should_not raise_error(ArgumentError)
12
+ end
13
+
14
+ it 'should not require options' do
15
+ lambda { Freshtrack::TimeCollector::OneInchPunch.new }.should_not raise_error(ArgumentError)
16
+ end
17
+
18
+ it 'should provide access to the given options' do
19
+ options = { :after => Time.now }
20
+ Freshtrack::TimeCollector::OneInchPunch.new(options).options.should == options
21
+ end
22
+
23
+ it 'should default options to an empty hash' do
24
+ Freshtrack::TimeCollector::OneInchPunch.new.options.should == {}
25
+ end
26
+ end
27
+
28
+ it 'should get time data' do
29
+ @collector.should respond_to(:get_time_data)
30
+ end
31
+
32
+ describe 'getting time data' do
33
+ before :each do
34
+ @project = 'myproj'
35
+ @time_data = stub('time data')
36
+ Punch.stubs(:load)
37
+ Punch.stubs(:list).returns(@time_data)
38
+ @collector.stubs(:condense_time_data)
39
+ end
40
+
41
+ it 'should accept a project' do
42
+ lambda { @collector.get_time_data(@project) }.should_not raise_error(ArgumentError)
43
+ end
44
+
45
+ it 'should require a project' do
46
+ lambda { @collector.get_time_data }.should raise_error(ArgumentError)
47
+ end
48
+
49
+ it 'should have punch load the time data' do
50
+ Punch.expects(:load)
51
+ @collector.get_time_data(@project)
52
+ end
53
+
54
+ it 'should get the time data from punch' do
55
+ Punch.expects(:list)
56
+ @collector.get_time_data(@project)
57
+ end
58
+
59
+ it 'should pass the supplied project when getting the time data' do
60
+ Punch.expects(:list).with(@project, anything)
61
+ @collector.get_time_data(@project)
62
+ end
63
+
64
+ it 'should pass the supplied options on when getting the time data' do
65
+ options = { :after => Time.now - 12345 }
66
+ @collector.stubs(:options).returns(options)
67
+ Punch.expects(:list).with(@project, options)
68
+ @collector.get_time_data(@project)
69
+ end
70
+
71
+ it 'should condense the time data' do
72
+ @collector.expects(:condense_time_data).with(@time_data)
73
+ @collector.get_time_data(@project)
74
+ end
75
+
76
+ it 'should return the condensed data' do
77
+ condensed = stub('condensed time data')
78
+ @collector.stubs(:condense_time_data).returns(condensed)
79
+ @collector.get_time_data(@project).should == condensed
80
+ end
81
+ end
82
+
83
+ it 'should condense time data' do
84
+ @collector.should respond_to(:condense_time_data)
85
+ end
86
+
87
+ describe 'condensing time data' do
88
+ before :each do
89
+ @time_data = stub('time data')
90
+ @collector.stubs(:times_to_dates)
91
+ @collector.stubs(:group_date_data)
92
+ end
93
+
94
+ it 'should accept time data' do
95
+ lambda { @collector.condense_time_data(@time_data) }.should_not raise_error(ArgumentError)
96
+ end
97
+
98
+ it 'should require time data' do
99
+ lambda { @collector.condense_time_data }.should raise_error(ArgumentError)
100
+ end
101
+
102
+ it 'should convert times to dates and hour differences' do
103
+ @collector.expects(:times_to_dates).with(@time_data)
104
+ @collector.condense_time_data(@time_data)
105
+ end
106
+
107
+ it 'should group date and hour differences' do
108
+ date_hour_data = stub('date/hour data')
109
+ @collector.stubs(:times_to_dates).returns(date_hour_data)
110
+ @collector.expects(:group_date_data).with(date_hour_data)
111
+ @collector.condense_time_data(@time_data)
112
+ end
113
+
114
+ it 'should return the grouped date/hour data' do
115
+ grouped_dates = stub('grouped date/hour data')
116
+ @collector.stubs(:group_date_data).returns(grouped_dates)
117
+ @collector.condense_time_data(@time_data).should == grouped_dates
118
+ end
119
+ end
120
+
121
+ it 'should convert times to dates and hour differences' do
122
+ @collector.should respond_to(:times_to_dates)
123
+ end
124
+
125
+ describe 'converting times to dates and hour differences' do
126
+ before :each do
127
+ @time_data = []
128
+ end
129
+
130
+ it 'should accept time data' do
131
+ lambda { @collector.times_to_dates(@time_data) }.should_not raise_error(ArgumentError)
132
+ end
133
+
134
+ it 'should require time data' do
135
+ lambda { @collector.times_to_dates }.should raise_error(ArgumentError)
136
+ end
137
+
138
+ it 'should return an array' do
139
+ @collector.times_to_dates(@time_data).should be_kind_of(Array)
140
+ end
141
+
142
+ it 'should replace the in/out time data with a single date' do
143
+ @time_data.push({ 'in' => Time.local(2008, 1, 25, 6, 25, 0), 'out' => Time.local(2008, 1, 25, 7, 25, 0) })
144
+ result = @collector.times_to_dates(@time_data)
145
+ result = result.first
146
+
147
+ result.should have_key('date')
148
+ result.should_not have_key('in')
149
+ result.should_not have_key('out')
150
+ end
151
+
152
+ it 'should make the date appopriate to the time' do
153
+ @time_data.push({ 'in' => Time.local(2008, 1, 25, 6, 25, 0), 'out' => Time.local(2008, 1, 25, 7, 25, 0) })
154
+ result = @collector.times_to_dates(@time_data)
155
+ result = result.first
156
+ result['date'].should == Date.civil(2008, 1, 25)
157
+ end
158
+
159
+ it 'should use the in time date' do
160
+ @time_data.push({ 'in' => Time.local(2008, 1, 25, 6, 25, 0), 'out' => Time.local(2008, 1, 26, 7, 25, 0) })
161
+ result = @collector.times_to_dates(@time_data)
162
+ result = result.first
163
+ result['date'].should == Date.civil(2008, 1, 25)
164
+ end
165
+
166
+ it 'should add hour data' do
167
+ @time_data.push({ 'in' => Time.local(2008, 1, 25, 6, 25, 0), 'out' => Time.local(2008, 1, 25, 7, 25, 0) })
168
+ result = @collector.times_to_dates(@time_data)
169
+ result = result.first
170
+ result.should have_key('hours')
171
+ end
172
+
173
+ it 'should make the hour data appropriate to the in/out difference' do
174
+ @time_data.push({ 'in' => Time.local(2008, 1, 25, 6, 25, 0), 'out' => Time.local(2008, 1, 25, 7, 55, 0) })
175
+ result = @collector.times_to_dates(@time_data)
176
+ result = result.first
177
+ result['hours'].should == 1.5
178
+ end
179
+ end
180
+
181
+ it 'should group date data' do
182
+ @collector.should respond_to(:group_date_data)
183
+ end
184
+
185
+ describe 'grouping date data' do
186
+ before :each do
187
+ @date_data = []
188
+ end
189
+
190
+ it 'should accept date data' do
191
+ lambda { @collector.group_date_data(@date_data) }.should_not raise_error(ArgumentError)
192
+ end
193
+
194
+ it 'should require date data' do
195
+ lambda { @collector.group_date_data }.should raise_error(ArgumentError)
196
+ end
197
+
198
+ it 'should return an array' do
199
+ @collector.group_date_data(@date_data).should be_kind_of(Array)
200
+ end
201
+
202
+ it 'should group the data by date' do
203
+ today = Date.today
204
+ @date_data.push({ 'date' => today, 'hours' => 0, 'log' => [] })
205
+ @date_data.push({ 'date' => today, 'hours' => 0, 'log' => [] })
206
+ @date_data.push({ 'date' => today + 1, 'hours' => 0, 'log' => [] })
207
+ @collector.group_date_data(@date_data).collect { |x| x['date'] }.should == [today, today + 1]
208
+ end
209
+
210
+ it 'should return the array sorted by date' do
211
+ today = Date.today
212
+ @date_data.push({ 'date' => today + 1, 'hours' => 0, 'log' => [] })
213
+ @date_data.push({ 'date' => today - 1, 'hours' => 0, 'log' => [] })
214
+ @date_data.push({ 'date' => today, 'hours' => 0, 'log' => [] })
215
+ @date_data.push({ 'date' => today + 1, 'hours' => 0, 'log' => [] })
216
+ @collector.group_date_data(@date_data).collect { |x| x['date'] }.should == [today - 1, today, today + 1]
217
+ end
218
+
219
+ it 'should add the hours for a particular date' do
220
+ today = Date.today
221
+ @date_data.push({ 'date' => today, 'hours' => 1, 'log' => [] })
222
+ @date_data.push({ 'date' => today, 'hours' => 3, 'log' => [] })
223
+ @date_data.push({ 'date' => today + 1, 'hours' => 2, 'log' => [] })
224
+ result = @collector.group_date_data(@date_data)
225
+
226
+ result[0]['date'].should == today
227
+ result[0]['hours'].should == 4
228
+
229
+ result[1]['date'].should == today + 1
230
+ result[1]['hours'].should == 2
231
+ end
232
+
233
+ it 'should round the hours to two decimal places' do
234
+ today = Date.today
235
+ @date_data.push({ 'date' => today, 'hours' => 1.666666666, 'log' => [] })
236
+ result = @collector.group_date_data(@date_data)
237
+
238
+ result[0]['date'].should == today
239
+ result[0]['hours'].should == 1.67
240
+ end
241
+
242
+ it 'should join the log into notes' do
243
+ today = Date.today
244
+ @date_data.push({ 'date' => today, 'hours' => 0, 'log' => ['punch in 1', 'punch out 1'] })
245
+ @date_data.push({ 'date' => today, 'hours' => 0, 'log' => ['punch in 2', 'punch out 2'] })
246
+ @date_data.push({ 'date' => today + 1, 'hours' => 0, 'log' => ['punch in 3', 'punch out 3'] })
247
+ result = @collector.group_date_data(@date_data)
248
+
249
+ result[0]['date'].should == today
250
+ result[0]['notes'].should == "punch in 1\npunch out 1\n--------------------\npunch in 2\npunch out 2"
251
+ result[0].should_not have_key('log')
252
+
253
+ result[1]['date'].should == today + 1
254
+ result[1]['notes'].should == "punch in 3\npunch out 3"
255
+ result[1].should_not have_key('log')
256
+ end
257
+ end
258
+ end
@@ -0,0 +1,298 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
2
+ require 'freshtrack/time_collectors/punch'
3
+
4
+ describe Freshtrack::TimeCollector::Punch do
5
+ before :each do
6
+ @collector = Freshtrack::TimeCollector::Punch.new
7
+ end
8
+
9
+ describe 'when initialized' do
10
+ it 'should accept options' do
11
+ lambda { Freshtrack::TimeCollector::Punch.new(:before => Time.now) }.should_not raise_error(ArgumentError)
12
+ end
13
+
14
+ it 'should not require options' do
15
+ lambda { Freshtrack::TimeCollector::Punch.new }.should_not raise_error(ArgumentError)
16
+ end
17
+
18
+ it 'should provide access to the given options' do
19
+ options = { :after => Time.now }
20
+ Freshtrack::TimeCollector::Punch.new(options).options.should == options
21
+ end
22
+
23
+ it 'should default options to an empty hash' do
24
+ Freshtrack::TimeCollector::Punch.new.options.should == {}
25
+ end
26
+ end
27
+
28
+ it 'should get time data' do
29
+ @collector.should respond_to(:get_time_data)
30
+ end
31
+
32
+ describe 'getting time data' do
33
+ before :each do
34
+ @project = 'myproj'
35
+ @time_data = stub('time data')
36
+ IO.stubs(:read).returns(@time_data)
37
+ @collector.stubs(:convert_time_data)
38
+ @collector.stubs(:condense_time_data)
39
+ end
40
+
41
+ it 'should accept a project' do
42
+ lambda { @collector.get_time_data(@project) }.should_not raise_error(ArgumentError)
43
+ end
44
+
45
+ it 'should require a project' do
46
+ lambda { @collector.get_time_data }.should raise_error(ArgumentError)
47
+ end
48
+
49
+ it 'should get the time data (from punch)' do
50
+ IO.expects(:read).with(regexp_matches(/^\| punch list\b/))
51
+ @collector.get_time_data(@project)
52
+ end
53
+
54
+ it 'should pass the supplied project when getting the time data' do
55
+ IO.expects(:read).with(regexp_matches(/\b#{@project}\b/))
56
+ @collector.get_time_data(@project)
57
+ end
58
+
59
+ it 'should pass the supplied options on as a string when getting the time data' do
60
+ time = Time.local(2007, 3, 4, 13, 47, 56)
61
+ options = { :after => time }
62
+ option_str = '--after 2007-03-04T13:47:56-0500'
63
+ @collector.stubs(:options).returns(options)
64
+ IO.expects(:read).with(regexp_matches(/\b#{@project} #{option_str}\b/))
65
+ @collector.get_time_data(@project)
66
+ end
67
+
68
+ it 'should default option string to empty string' do
69
+ IO.expects(:read).with(regexp_matches(/\b#{@project} $/))
70
+ @collector.get_time_data(@project)
71
+ end
72
+
73
+ it 'should convert the time data' do
74
+ @collector.expects(:convert_time_data).with(@time_data)
75
+ @collector.get_time_data(@project)
76
+ end
77
+
78
+ it 'should condense the converted time data' do
79
+ converted_time_data = stub('converted time data')
80
+ @collector.stubs(:convert_time_data).returns(converted_time_data)
81
+ @collector.expects(:condense_time_data).with(converted_time_data)
82
+ @collector.get_time_data(@project)
83
+ end
84
+
85
+ it 'should return the condensed data' do
86
+ condensed = stub('condensed time data')
87
+ @collector.stubs(:condense_time_data).returns(condensed)
88
+ @collector.get_time_data(@project).should == condensed
89
+ end
90
+ end
91
+
92
+ it 'should convert time data' do
93
+ @collector.should respond_to(:convert_time_data)
94
+ end
95
+
96
+ describe 'converting time data' do
97
+ before :each do
98
+ @time_data = stub('time data')
99
+ YAML.stubs(:load)
100
+ @collector.stubs(:condense_time_data)
101
+ end
102
+
103
+ it 'should accept time data' do
104
+ lambda { @collector.convert_time_data(@time_data) }.should_not raise_error(ArgumentError)
105
+ end
106
+
107
+ it 'should require time data' do
108
+ lambda { @collector.convert_time_data }.should raise_error(ArgumentError)
109
+ end
110
+
111
+ it 'should convert the time data from YAML' do
112
+ YAML.expects(:load).with(@time_data)
113
+ @collector.convert_time_data(@time_data)
114
+ end
115
+
116
+ it 'should return the converted time data' do
117
+ converted = stub('converted time data')
118
+ YAML.stubs(:load).returns(converted)
119
+ @collector.convert_time_data(@time_data).should == converted
120
+ end
121
+ end
122
+
123
+ it 'should condense time data' do
124
+ @collector.should respond_to(:condense_time_data)
125
+ end
126
+
127
+ describe 'condensing time data' do
128
+ before :each do
129
+ @time_data = stub('time data')
130
+ @collector.stubs(:times_to_dates)
131
+ @collector.stubs(:group_date_data)
132
+ end
133
+
134
+ it 'should accept time data' do
135
+ lambda { @collector.condense_time_data(@time_data) }.should_not raise_error(ArgumentError)
136
+ end
137
+
138
+ it 'should require time data' do
139
+ lambda { @collector.condense_time_data }.should raise_error(ArgumentError)
140
+ end
141
+
142
+ it 'should convert times to dates and hour differences' do
143
+ @collector.expects(:times_to_dates).with(@time_data)
144
+ @collector.condense_time_data(@time_data)
145
+ end
146
+
147
+ it 'should group date and hour differences' do
148
+ date_hour_data = stub('date/hour data')
149
+ @collector.stubs(:times_to_dates).returns(date_hour_data)
150
+ @collector.expects(:group_date_data).with(date_hour_data)
151
+ @collector.condense_time_data(@time_data)
152
+ end
153
+
154
+ it 'should return the grouped date/hour data' do
155
+ grouped_dates = stub('grouped date/hour data')
156
+ @collector.stubs(:group_date_data).returns(grouped_dates)
157
+ @collector.condense_time_data(@time_data).should == grouped_dates
158
+ end
159
+ end
160
+
161
+ it 'should convert times to dates and hour differences' do
162
+ @collector.should respond_to(:times_to_dates)
163
+ end
164
+
165
+ describe 'converting times to dates and hour differences' do
166
+ before :each do
167
+ @time_data = []
168
+ end
169
+
170
+ it 'should accept time data' do
171
+ lambda { @collector.times_to_dates(@time_data) }.should_not raise_error(ArgumentError)
172
+ end
173
+
174
+ it 'should require time data' do
175
+ lambda { @collector.times_to_dates }.should raise_error(ArgumentError)
176
+ end
177
+
178
+ it 'should return an array' do
179
+ @collector.times_to_dates(@time_data).should be_kind_of(Array)
180
+ end
181
+
182
+ it 'should replace the in/out time data with a single date' do
183
+ @time_data.push({ 'in' => Time.local(2008, 1, 25, 6, 25, 0), 'out' => Time.local(2008, 1, 25, 7, 25, 0) })
184
+ result = @collector.times_to_dates(@time_data)
185
+ result = result.first
186
+
187
+ result.should have_key('date')
188
+ result.should_not have_key('in')
189
+ result.should_not have_key('out')
190
+ end
191
+
192
+ it 'should make the date appopriate to the time' do
193
+ @time_data.push({ 'in' => Time.local(2008, 1, 25, 6, 25, 0), 'out' => Time.local(2008, 1, 25, 7, 25, 0) })
194
+ result = @collector.times_to_dates(@time_data)
195
+ result = result.first
196
+ result['date'].should == Date.civil(2008, 1, 25)
197
+ end
198
+
199
+ it 'should use the in time date' do
200
+ @time_data.push({ 'in' => Time.local(2008, 1, 25, 6, 25, 0), 'out' => Time.local(2008, 1, 26, 7, 25, 0) })
201
+ result = @collector.times_to_dates(@time_data)
202
+ result = result.first
203
+ result['date'].should == Date.civil(2008, 1, 25)
204
+ end
205
+
206
+ it 'should add hour data' do
207
+ @time_data.push({ 'in' => Time.local(2008, 1, 25, 6, 25, 0), 'out' => Time.local(2008, 1, 25, 7, 25, 0) })
208
+ result = @collector.times_to_dates(@time_data)
209
+ result = result.first
210
+ result.should have_key('hours')
211
+ end
212
+
213
+ it 'should make the hour data appropriate to the in/out difference' do
214
+ @time_data.push({ 'in' => Time.local(2008, 1, 25, 6, 25, 0), 'out' => Time.local(2008, 1, 25, 7, 55, 0) })
215
+ result = @collector.times_to_dates(@time_data)
216
+ result = result.first
217
+ result['hours'].should == 1.5
218
+ end
219
+ end
220
+
221
+ it 'should group date data' do
222
+ @collector.should respond_to(:group_date_data)
223
+ end
224
+
225
+ describe 'grouping date data' do
226
+ before :each do
227
+ @date_data = []
228
+ end
229
+
230
+ it 'should accept date data' do
231
+ lambda { @collector.group_date_data(@date_data) }.should_not raise_error(ArgumentError)
232
+ end
233
+
234
+ it 'should require date data' do
235
+ lambda { @collector.group_date_data }.should raise_error(ArgumentError)
236
+ end
237
+
238
+ it 'should return an array' do
239
+ @collector.group_date_data(@date_data).should be_kind_of(Array)
240
+ end
241
+
242
+ it 'should group the data by date' do
243
+ today = Date.today
244
+ @date_data.push({ 'date' => today, 'hours' => 0, 'log' => [] })
245
+ @date_data.push({ 'date' => today, 'hours' => 0, 'log' => [] })
246
+ @date_data.push({ 'date' => today + 1, 'hours' => 0, 'log' => [] })
247
+ @collector.group_date_data(@date_data).collect { |x| x['date'] }.should == [today, today + 1]
248
+ end
249
+
250
+ it 'should return the array sorted by date' do
251
+ today = Date.today
252
+ @date_data.push({ 'date' => today + 1, 'hours' => 0, 'log' => [] })
253
+ @date_data.push({ 'date' => today - 1, 'hours' => 0, 'log' => [] })
254
+ @date_data.push({ 'date' => today, 'hours' => 0, 'log' => [] })
255
+ @date_data.push({ 'date' => today + 1, 'hours' => 0, 'log' => [] })
256
+ @collector.group_date_data(@date_data).collect { |x| x['date'] }.should == [today - 1, today, today + 1]
257
+ end
258
+
259
+ it 'should add the hours for a particular date' do
260
+ today = Date.today
261
+ @date_data.push({ 'date' => today, 'hours' => 1, 'log' => [] })
262
+ @date_data.push({ 'date' => today, 'hours' => 3, 'log' => [] })
263
+ @date_data.push({ 'date' => today + 1, 'hours' => 2, 'log' => [] })
264
+ result = @collector.group_date_data(@date_data)
265
+
266
+ result[0]['date'].should == today
267
+ result[0]['hours'].should == 4
268
+
269
+ result[1]['date'].should == today + 1
270
+ result[1]['hours'].should == 2
271
+ end
272
+
273
+ it 'should round the hours to two decimal places' do
274
+ today = Date.today
275
+ @date_data.push({ 'date' => today, 'hours' => 1.666666666, 'log' => [] })
276
+ result = @collector.group_date_data(@date_data)
277
+
278
+ result[0]['date'].should == today
279
+ result[0]['hours'].should == 1.67
280
+ end
281
+
282
+ it 'should join the log into notes' do
283
+ today = Date.today
284
+ @date_data.push({ 'date' => today, 'hours' => 0, 'log' => ['punch in 1', 'punch out 1'] })
285
+ @date_data.push({ 'date' => today, 'hours' => 0, 'log' => ['punch in 2', 'punch out 2'] })
286
+ @date_data.push({ 'date' => today + 1, 'hours' => 0, 'log' => ['punch in 3', 'punch out 3'] })
287
+ result = @collector.group_date_data(@date_data)
288
+
289
+ result[0]['date'].should == today
290
+ result[0]['notes'].should == "punch in 1\npunch out 1\n--------------------\npunch in 2\npunch out 2"
291
+ result[0].should_not have_key('log')
292
+
293
+ result[1]['date'].should == today + 1
294
+ result[1]['notes'].should == "punch in 3\npunch out 3"
295
+ result[1].should_not have_key('log')
296
+ end
297
+ end
298
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: freshtrack
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yossef Mendelssohn
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-08-27 00:00:00 -05:00
12
+ date: 2008-08-29 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -22,16 +22,6 @@ dependencies:
22
22
  - !ruby/object:Gem::Version
23
23
  version: "2.1"
24
24
  version:
25
- - !ruby/object:Gem::Dependency
26
- name: one_inch_punch
27
- type: :runtime
28
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: 0.0.3
34
- version:
35
25
  - !ruby/object:Gem::Dependency
36
26
  name: hoe
37
27
  type: :development
@@ -68,6 +58,9 @@ files:
68
58
  - lib/freshtrack/core_ext/array.rb
69
59
  - lib/freshtrack/core_ext/numeric.rb
70
60
  - lib/freshtrack/core_ext/time.rb
61
+ - lib/freshtrack/time_collectors/one_inch_punch.rb
62
+ - lib/freshtrack/time_collectors/punch.rb
63
+ - lib/freshtrack/time_collectors/punchy_template.rb
71
64
  - lib/freshbooks/extensions.rb
72
65
  - lib/freshbooks/extensions/base_object.rb
73
66
  - lib/freshbooks/extensions/project.rb
@@ -77,15 +70,17 @@ files:
77
70
  - script/destroy
78
71
  - script/generate
79
72
  - setup.rb
80
- - spec/base_object_spec.rb
73
+ - spec/core_ext/array_spec.rb
74
+ - spec/core_ext/numeric_spec.rb
75
+ - spec/core_ext/time_spec.rb
76
+ - spec/time_collectors/one_inch_punch_spec.rb
77
+ - spec/time_collectors/punch_spec.rb
78
+ - spec/freshbooks/base_object_spec.rb
79
+ - spec/freshbooks/project_spec.rb
80
+ - spec/freshbooks/task_spec.rb
81
+ - spec/freshbooks/time_entry_spec.rb
81
82
  - spec/freshtrack_command_spec.rb
82
83
  - spec/freshtrack_spec.rb
83
- - spec/project_spec.rb
84
- - spec/task_spec.rb
85
- - spec/time_entry_spec.rb
86
- - spec/array_spec.rb
87
- - spec/numeric_spec.rb
88
- - spec/time_spec.rb
89
84
  - spec/spec.opts
90
85
  - spec/spec_helper.rb
91
86
  - tasks/deployment.rake