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 +8 -0
- data/Manifest.txt +12 -7
- data/README.txt +6 -2
- data/config/hoe.rb +1 -2
- data/lib/freshtrack/time_collectors/one_inch_punch.rb +16 -0
- data/lib/freshtrack/time_collectors/punch.rb +26 -0
- data/lib/freshtrack/time_collectors/punchy_template.rb +38 -0
- data/lib/freshtrack/version.rb +1 -1
- data/lib/freshtrack.rb +9 -35
- data/spec/{array_spec.rb → core_ext/array_spec.rb} +1 -1
- data/spec/{numeric_spec.rb → core_ext/numeric_spec.rb} +1 -1
- data/spec/{time_spec.rb → core_ext/time_spec.rb} +1 -1
- data/spec/{base_object_spec.rb → freshbooks/base_object_spec.rb} +1 -1
- data/spec/{project_spec.rb → freshbooks/project_spec.rb} +1 -1
- data/spec/{task_spec.rb → freshbooks/task_spec.rb} +1 -1
- data/spec/{time_entry_spec.rb → freshbooks/time_entry_spec.rb} +1 -1
- data/spec/freshtrack_command_spec.rb +1 -1
- data/spec/freshtrack_spec.rb +75 -214
- data/spec/time_collectors/one_inch_punch_spec.rb +258 -0
- data/spec/time_collectors/punch_spec.rb +298 -0
- metadata +14 -19
data/History.txt
CHANGED
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/
|
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
|
-
*
|
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
|
-
*
|
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
|
data/lib/freshtrack/version.rb
CHANGED
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
|
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
|
data/spec/freshtrack_spec.rb
CHANGED
@@ -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
|
131
|
+
describe 'getting data' do
|
132
132
|
before :each do
|
133
133
|
@project_name = :proj
|
134
|
-
|
135
|
-
|
136
|
-
|
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.
|
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.
|
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.
|
148
|
+
lambda { Freshtrack.get_data(@project_name, :before => Time.now) }.should_not raise_error(ArgumentError)
|
150
149
|
end
|
151
150
|
|
152
|
-
it 'should
|
153
|
-
|
154
|
-
Freshtrack.
|
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
|
158
|
-
|
159
|
-
Freshtrack.
|
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
|
163
|
-
|
164
|
-
Freshtrack.
|
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
|
166
|
+
it 'should pass the options on when retrieving a time collector' do
|
168
167
|
options = { :after => Time.now - 12345 }
|
169
|
-
|
170
|
-
Freshtrack.
|
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
|
211
|
-
|
212
|
-
Freshtrack.
|
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
|
218
|
-
|
219
|
-
|
220
|
-
Freshtrack.
|
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 '
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
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
|
-
|
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
|
286
|
-
lambda { Freshtrack.
|
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
|
290
|
-
lambda { Freshtrack.
|
205
|
+
it 'should not require options' do
|
206
|
+
lambda { Freshtrack.collector }.should_not raise_error(ArgumentError)
|
291
207
|
end
|
292
208
|
|
293
|
-
it 'should
|
294
|
-
Freshtrack.
|
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
|
298
|
-
|
299
|
-
|
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
|
306
|
-
|
307
|
-
|
308
|
-
|
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
|
315
|
-
|
316
|
-
|
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
|
329
|
-
|
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
|
338
|
-
|
339
|
-
|
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
|
362
|
-
|
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
|
366
|
-
|
367
|
-
|
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
|
390
|
-
Freshtrack.
|
391
|
-
Freshtrack.
|
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
|
395
|
-
|
396
|
-
Freshtrack.
|
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.
|
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-
|
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/
|
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
|