freshtrack 0.3.0 → 0.4.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,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