freshtrack 0.4.2 → 0.5.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 +5 -0
- data/Rakefile +51 -4
- data/VERSION +1 -0
- data/bin/freshtrack +4 -4
- data/config/hoe.rb +2 -2
- data/lib/freshtrack/version.rb +2 -2
- data/lib/freshtrack.rb +13 -4
- data/spec/.bacon +0 -0
- data/spec/core_ext/array_spec.rb +5 -5
- data/spec/core_ext/numeric_spec.rb +2 -2
- data/spec/core_ext/time_spec.rb +3 -3
- data/spec/freshbooks/base_object_spec.rb +7 -7
- data/spec/freshbooks/invoice_spec.rb +40 -40
- data/spec/freshbooks/payment_spec.rb +4 -4
- data/spec/freshbooks/project_spec.rb +119 -119
- data/spec/freshbooks/task_spec.rb +102 -102
- data/spec/freshbooks/time_entry_spec.rb +97 -97
- data/spec/freshtrack_command_spec.rb +77 -45
- data/spec/freshtrack_spec.rb +237 -130
- data/spec/spec_helper.rb +3 -21
- data/spec/time_collectors/one_inch_punch_spec.rb +55 -53
- data/spec/time_collectors/punch_spec.rb +74 -66
- metadata +83 -54
- data/spec/spec.opts +0 -3
data/History.txt
CHANGED
data/Rakefile
CHANGED
@@ -1,4 +1,51 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
|
4
|
-
|
1
|
+
require 'rubygems'
|
2
|
+
#require 'bundler'
|
3
|
+
#begin
|
4
|
+
# Bundler.setup(:default, :development)
|
5
|
+
#rescue Bundler::BundlerError => e
|
6
|
+
# $stderr.puts e.message
|
7
|
+
# $stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
# exit e.status_code
|
9
|
+
#end
|
10
|
+
require 'rake'
|
11
|
+
|
12
|
+
require 'jeweler'
|
13
|
+
Jeweler::Tasks.new do |gem|
|
14
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
15
|
+
gem.name = "freshtrack"
|
16
|
+
gem.homepage = "http://github.com/flogic/freshtrack"
|
17
|
+
gem.license = "MIT"
|
18
|
+
gem.summary = %Q{Track your time on FreshBooks}
|
19
|
+
gem.email = "ymendel@pobox.com"
|
20
|
+
gem.authors = ["Yossef Mendelssohn"]
|
21
|
+
gem.add_runtime_dependency 'freshbooks', '= 2.1'
|
22
|
+
gem.add_development_dependency 'bacon', '>= 1.1.0'
|
23
|
+
gem.add_development_dependency 'facon', '>= 0.4.1'
|
24
|
+
end
|
25
|
+
Jeweler::RubygemsDotOrgTasks.new
|
26
|
+
|
27
|
+
require 'rake/testtask'
|
28
|
+
Rake::TestTask.new(:test) do |test|
|
29
|
+
test.libs << 'lib' << 'test'
|
30
|
+
test.pattern = 'test/**/test_*.rb'
|
31
|
+
test.verbose = true
|
32
|
+
end
|
33
|
+
|
34
|
+
#require 'rcov/rcovtask'
|
35
|
+
#Rcov::RcovTask.new do |test|
|
36
|
+
# test.libs << 'test'
|
37
|
+
# test.pattern = 'test/**/test_*.rb'
|
38
|
+
# test.verbose = true
|
39
|
+
#end
|
40
|
+
|
41
|
+
task :default => :test
|
42
|
+
|
43
|
+
require 'rake/rdoctask'
|
44
|
+
Rake::RDocTask.new do |rdoc|
|
45
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
46
|
+
|
47
|
+
rdoc.rdoc_dir = 'rdoc'
|
48
|
+
rdoc.title = "freshtrack #{version}"
|
49
|
+
rdoc.rdoc_files.include('README*')
|
50
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
51
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.5.0
|
data/bin/freshtrack
CHANGED
@@ -42,8 +42,10 @@ BANNER
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
+
project = ARGV.shift
|
46
|
+
|
45
47
|
if aging
|
46
|
-
Freshtrack.init
|
48
|
+
Freshtrack.init(project)
|
47
49
|
aging = Freshtrack.invoice_aging
|
48
50
|
printf "%-8s %-40s %-5s %-6s %8s %8s\n", 'Invoice', 'Client', 'Age', 'Status', 'Amount', 'Owed'
|
49
51
|
printf "%s\n", '-' * 86
|
@@ -53,12 +55,10 @@ if aging
|
|
53
55
|
exit
|
54
56
|
end
|
55
57
|
|
56
|
-
project = ARGV.shift
|
57
|
-
|
58
58
|
unless project
|
59
59
|
puts "Usage: #{File.basename($0)} [project] [options]"
|
60
60
|
exit
|
61
61
|
end
|
62
62
|
|
63
|
-
Freshtrack.init
|
63
|
+
Freshtrack.init(project)
|
64
64
|
Freshtrack.track(project, OPTIONS)
|
data/config/hoe.rb
CHANGED
@@ -11,8 +11,8 @@ EXTRA_DEPENDENCIES = [
|
|
11
11
|
['freshbooks', '= 2.1']
|
12
12
|
] # An array of rubygem dependencies [name, version]
|
13
13
|
EXTRA_DEV_DEPENDENCIES = [
|
14
|
-
['
|
15
|
-
['
|
14
|
+
['bacon', '>= 1.1.0'],
|
15
|
+
['facon', '>= 0.4.1']
|
16
16
|
] # An array of rubygem dependencies [name, version]
|
17
17
|
|
18
18
|
@config_file = "~/.rubyforge/user-config.yml"
|
data/lib/freshtrack/version.rb
CHANGED
data/lib/freshtrack.rb
CHANGED
@@ -5,10 +5,11 @@ require 'yaml'
|
|
5
5
|
|
6
6
|
module Freshtrack
|
7
7
|
class << self
|
8
|
-
attr_reader :config, :project, :task
|
8
|
+
attr_reader :config, :project_name, :project, :task
|
9
9
|
|
10
|
-
def init
|
10
|
+
def init(project = nil)
|
11
11
|
load_config
|
12
|
+
@project_name = project
|
12
13
|
FreshBooks.setup("#{company}.freshbooks.com", token)
|
13
14
|
end
|
14
15
|
|
@@ -17,13 +18,21 @@ module Freshtrack
|
|
17
18
|
end
|
18
19
|
|
19
20
|
def company
|
20
|
-
|
21
|
+
project_info_value('company')
|
21
22
|
end
|
22
23
|
|
23
24
|
def token
|
24
|
-
|
25
|
+
project_info_value('token')
|
25
26
|
end
|
26
27
|
|
28
|
+
def project_info_value(key)
|
29
|
+
return config[key.to_s] unless project_name
|
30
|
+
info = project_task_mapping[project_name]
|
31
|
+
return config[key.to_s] unless info[:company] and info[:token]
|
32
|
+
info[key.to_sym]
|
33
|
+
end
|
34
|
+
private :project_info_value
|
35
|
+
|
27
36
|
def project_task_mapping
|
28
37
|
config['project_task_mapping']
|
29
38
|
end
|
data/spec/.bacon
ADDED
File without changes
|
data/spec/core_ext/array_spec.rb
CHANGED
@@ -2,24 +2,24 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
|
|
2
2
|
|
3
3
|
describe Array do
|
4
4
|
it 'should be groupable' do
|
5
|
-
Array.new.should
|
5
|
+
Array.new.should.respond_to(:group_by)
|
6
6
|
end
|
7
7
|
|
8
8
|
describe 'when grouping' do
|
9
|
-
before
|
9
|
+
before do
|
10
10
|
@array = (1..10).to_a
|
11
11
|
end
|
12
12
|
|
13
13
|
it 'should require a block' do
|
14
|
-
lambda { @array.group_by }.should
|
14
|
+
lambda { @array.group_by }.should.raise(ArgumentError)
|
15
15
|
end
|
16
16
|
|
17
17
|
it 'should accept a block' do
|
18
|
-
lambda { @array.group_by {} }.
|
18
|
+
lambda { @array.group_by {} }.should.not.raise(ArgumentError)
|
19
19
|
end
|
20
20
|
|
21
21
|
it 'should return a hash' do
|
22
|
-
@array.group_by {}.should
|
22
|
+
@array.group_by {}.should.be.kind_of(Hash)
|
23
23
|
end
|
24
24
|
|
25
25
|
it 'should group the elements by the return value of the block' do
|
@@ -2,7 +2,7 @@ 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
|
5
|
-
90.should
|
5
|
+
90.should.respond_to(:secs_to_hours)
|
6
6
|
end
|
7
7
|
|
8
8
|
it 'should return this number of seconds as an amount of hours' do
|
@@ -12,7 +12,7 @@ end
|
|
12
12
|
|
13
13
|
describe Float do
|
14
14
|
it 'should be convertible from seconds to hours' do
|
15
|
-
90.0.should
|
15
|
+
90.0.should.respond_to(:secs_to_hours)
|
16
16
|
end
|
17
17
|
|
18
18
|
it 'should return this number of seconds as an amount of hours' do
|
data/spec/core_ext/time_spec.rb
CHANGED
@@ -2,17 +2,17 @@ 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
|
5
|
-
Time.now.should
|
5
|
+
Time.now.should.respond_to(:to_date)
|
6
6
|
end
|
7
7
|
|
8
8
|
describe 'when converting to date' do
|
9
|
-
before
|
9
|
+
before do
|
10
10
|
@time = Time.now
|
11
11
|
@date = @time.to_date
|
12
12
|
end
|
13
13
|
|
14
14
|
it 'should return a date' do
|
15
|
-
@date.should
|
15
|
+
@date.should.be.kind_of(Date)
|
16
16
|
end
|
17
17
|
|
18
18
|
it 'should return an date with matching year' do
|
@@ -5,17 +5,17 @@ ThingDeal = FreshBooks::BaseObject.new(:attr)
|
|
5
5
|
|
6
6
|
describe FreshBooks::BaseObject do
|
7
7
|
it 'should have a mapping function for Date' do
|
8
|
-
FreshBooks::BaseObject::MAPPING_FNS[Date].should
|
8
|
+
FreshBooks::BaseObject::MAPPING_FNS[Date].should.respond_to(:call)
|
9
9
|
end
|
10
10
|
|
11
11
|
describe 'Date mapping function' do
|
12
|
-
before
|
12
|
+
before do
|
13
13
|
@func = FreshBooks::BaseObject::MAPPING_FNS[Date]
|
14
|
-
@date =
|
14
|
+
@date = mock('date arg', :text => '2008-01-29')
|
15
15
|
end
|
16
16
|
|
17
17
|
it 'should return a date' do
|
18
|
-
@func.call(@date).should
|
18
|
+
@func.call(@date).should.be.kind_of(Date)
|
19
19
|
end
|
20
20
|
|
21
21
|
it 'should return a date matching the text of its argument' do
|
@@ -24,18 +24,18 @@ describe FreshBooks::BaseObject do
|
|
24
24
|
end
|
25
25
|
|
26
26
|
describe 'converting an instance to XML' do
|
27
|
-
before
|
27
|
+
before do
|
28
28
|
@thing = Thing.new
|
29
29
|
end
|
30
30
|
|
31
31
|
it 'should use the elem name' do
|
32
|
-
@thing.
|
32
|
+
@thing.should.receive(:elem_name)
|
33
33
|
@thing.to_xml
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
37
|
describe 'getting the elem name' do
|
38
|
-
before
|
38
|
+
before do
|
39
39
|
@thing = Thing.new
|
40
40
|
@thing_deal = ThingDeal.new
|
41
41
|
end
|
@@ -1,34 +1,34 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
|
2
2
|
|
3
3
|
describe FreshBooks::Invoice do
|
4
|
-
before
|
4
|
+
before do
|
5
5
|
@invoice = FreshBooks::Invoice.new
|
6
6
|
end
|
7
7
|
|
8
8
|
describe 'attributes' do
|
9
9
|
it 'should have an invoice_id' do
|
10
|
-
@invoice.should
|
10
|
+
@invoice.should.respond_to(:invoice_id)
|
11
11
|
end
|
12
12
|
|
13
13
|
it 'should have a client_id' do
|
14
|
-
@invoice.should
|
14
|
+
@invoice.should.respond_to(:client_id)
|
15
15
|
end
|
16
16
|
|
17
17
|
it 'should have a date' do
|
18
|
-
@invoice.should
|
18
|
+
@invoice.should.respond_to(:date)
|
19
19
|
end
|
20
20
|
|
21
21
|
it 'should have a status' do
|
22
|
-
@invoice.should
|
22
|
+
@invoice.should.respond_to(:status)
|
23
23
|
end
|
24
24
|
|
25
25
|
it 'should have an amount' do
|
26
|
-
@invoice.should
|
26
|
+
@invoice.should.respond_to(:amount)
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
30
|
describe 'type mappings' do
|
31
|
-
before
|
31
|
+
before do
|
32
32
|
@mapping = FreshBooks::Invoice::TYPE_MAPPINGS
|
33
33
|
end
|
34
34
|
|
@@ -46,53 +46,53 @@ describe FreshBooks::Invoice do
|
|
46
46
|
end
|
47
47
|
|
48
48
|
it 'should indicate open status' do
|
49
|
-
@invoice.should
|
49
|
+
@invoice.should.respond_to(:open?)
|
50
50
|
end
|
51
51
|
|
52
52
|
describe 'indicating open status' do
|
53
53
|
it "should be false if the status is 'draft'" do
|
54
54
|
@invoice.status = 'draft'
|
55
|
-
@invoice.
|
55
|
+
@invoice.should.not.be.open
|
56
56
|
end
|
57
57
|
|
58
58
|
it "should be true if the status is 'sent'" do
|
59
59
|
@invoice.status = 'sent'
|
60
|
-
@invoice.should
|
60
|
+
@invoice.should.be.open
|
61
61
|
end
|
62
62
|
|
63
63
|
it "should be true if the status is 'viewed'" do
|
64
64
|
@invoice.status = 'viewed'
|
65
|
-
@invoice.should
|
65
|
+
@invoice.should.be.open
|
66
66
|
end
|
67
67
|
|
68
68
|
it "should be false if the status is 'paid'" do
|
69
69
|
@invoice.status = 'paid'
|
70
|
-
@invoice.
|
70
|
+
@invoice.should.not.be.open
|
71
71
|
end
|
72
72
|
|
73
73
|
it "should be true if the status is 'partial'" do
|
74
74
|
@invoice.status = 'partial'
|
75
|
-
@invoice.should
|
75
|
+
@invoice.should.be.open
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
79
79
|
it 'should have a client' do
|
80
|
-
@invoice.should
|
80
|
+
@invoice.should.respond_to(:client)
|
81
81
|
end
|
82
82
|
|
83
83
|
describe 'client' do
|
84
84
|
it 'should find client based on client_id' do
|
85
|
-
client_id =
|
86
|
-
@invoice.
|
87
|
-
FreshBooks::Client.
|
85
|
+
client_id = mock('client ID')
|
86
|
+
@invoice.stub!(:client_id).and_return(client_id)
|
87
|
+
FreshBooks::Client.should.receive(:get).with(client_id)
|
88
88
|
@invoice.client
|
89
89
|
end
|
90
90
|
|
91
91
|
it 'should return found client' do
|
92
|
-
client =
|
93
|
-
client_id =
|
94
|
-
@invoice.
|
95
|
-
FreshBooks::Client.
|
92
|
+
client = mock('client')
|
93
|
+
client_id = mock('client ID')
|
94
|
+
@invoice.stub!(:client_id).and_return(client_id)
|
95
|
+
FreshBooks::Client.should.receive(:get).with(client_id).and_return(client)
|
96
96
|
@invoice.client.should == client
|
97
97
|
end
|
98
98
|
end
|
@@ -114,12 +114,12 @@ describe FreshBooks::Invoice do
|
|
114
114
|
end
|
115
115
|
|
116
116
|
it 'should show up in the members list' do
|
117
|
-
FreshBooks::Invoice.members.should
|
117
|
+
FreshBooks::Invoice.members.should.include('number')
|
118
118
|
end
|
119
119
|
end
|
120
120
|
|
121
121
|
it 'should still have important core behavior' do
|
122
|
-
FreshBooks::Invoice.should
|
122
|
+
FreshBooks::Invoice.should.respond_to(:list)
|
123
123
|
end
|
124
124
|
|
125
125
|
it 'should still be a type of BaseObject' do
|
@@ -128,10 +128,10 @@ describe FreshBooks::Invoice do
|
|
128
128
|
|
129
129
|
it 'should still have other fields in the members list' do
|
130
130
|
members = FreshBooks::Invoice.members
|
131
|
-
members.should
|
132
|
-
members.should
|
133
|
-
members.should
|
134
|
-
members.should
|
131
|
+
members.should.include('invoice_id')
|
132
|
+
members.should.include('client_id')
|
133
|
+
members.should.include('status')
|
134
|
+
members.should.include('date')
|
135
135
|
end
|
136
136
|
|
137
137
|
it 'should still allow other fields to be set and get using []' do
|
@@ -143,23 +143,23 @@ describe FreshBooks::Invoice do
|
|
143
143
|
end
|
144
144
|
|
145
145
|
it 'should have payments' do
|
146
|
-
@invoice.should
|
146
|
+
@invoice.should.respond_to(:payments)
|
147
147
|
end
|
148
148
|
|
149
149
|
describe 'payments' do
|
150
|
-
before
|
151
|
-
@payments = Array.new(3) {
|
152
|
-
FreshBooks::Payment.
|
150
|
+
before do
|
151
|
+
@payments = Array.new(3) { mock('payment') }
|
152
|
+
FreshBooks::Payment.stub!(:list).and_return(@payments)
|
153
153
|
end
|
154
154
|
|
155
155
|
it 'should get a list from Payment' do
|
156
|
-
FreshBooks::Payment.
|
156
|
+
FreshBooks::Payment.should.receive(:list)
|
157
157
|
@invoice.payments
|
158
158
|
end
|
159
159
|
|
160
160
|
it 'should pass the invoice_id when getting the list' do
|
161
161
|
@invoice.invoice_id = '0000073'
|
162
|
-
FreshBooks::Payment.
|
162
|
+
FreshBooks::Payment.should.receive(:list).with('invoice_id' => @invoice.invoice_id)
|
163
163
|
@invoice.payments
|
164
164
|
end
|
165
165
|
|
@@ -168,36 +168,36 @@ describe FreshBooks::Invoice do
|
|
168
168
|
end
|
169
169
|
|
170
170
|
it 'should return an empty array if Payment returns nil' do
|
171
|
-
FreshBooks::Payment.
|
171
|
+
FreshBooks::Payment.stub!(:list).and_return(nil)
|
172
172
|
@invoice.payments.should == []
|
173
173
|
end
|
174
174
|
end
|
175
175
|
|
176
176
|
it 'should have a paid amount' do
|
177
|
-
@invoice.should
|
177
|
+
@invoice.should.respond_to(:paid_amount)
|
178
178
|
end
|
179
179
|
|
180
180
|
describe 'paid amount' do
|
181
181
|
it 'should be the sum of payment amounts' do
|
182
|
-
payments = [
|
183
|
-
@invoice.
|
182
|
+
payments = [mock('payment', :amount => 3), mock('payment', :amount => 15), mock('payment', :amount => 5)]
|
183
|
+
@invoice.stub!(:payments).and_return(payments)
|
184
184
|
@invoice.paid_amount.should == 23
|
185
185
|
end
|
186
186
|
|
187
187
|
it 'should be 0 if there are no payments' do
|
188
|
-
@invoice.
|
188
|
+
@invoice.stub!(:payments).and_return([])
|
189
189
|
@invoice.paid_amount.should == 0
|
190
190
|
end
|
191
191
|
end
|
192
192
|
|
193
193
|
it 'should have an owed amount' do
|
194
|
-
@invoice.should
|
194
|
+
@invoice.should.respond_to(:owed_amount)
|
195
195
|
end
|
196
196
|
|
197
197
|
describe 'owed amount' do
|
198
198
|
it 'should be invoice amount less paid amount' do
|
199
199
|
@invoice.amount = 60
|
200
|
-
@invoice.
|
200
|
+
@invoice.stub!(:paid_amount).and_return(50)
|
201
201
|
@invoice.owed_amount.should == 10
|
202
202
|
end
|
203
203
|
end
|
@@ -1,22 +1,22 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
|
2
2
|
|
3
3
|
describe FreshBooks::Payment do
|
4
|
-
before
|
4
|
+
before do
|
5
5
|
@payment = FreshBooks::Payment.new
|
6
6
|
end
|
7
7
|
|
8
8
|
describe 'attributes' do
|
9
9
|
it 'should have an invoice_id' do
|
10
|
-
@payment.should
|
10
|
+
@payment.should.respond_to(:invoice_id)
|
11
11
|
end
|
12
12
|
|
13
13
|
it 'should have an amount' do
|
14
|
-
@payment.should
|
14
|
+
@payment.should.respond_to(:amount)
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
18
|
describe 'type mappings' do
|
19
|
-
before
|
19
|
+
before do
|
20
20
|
@mapping = FreshBooks::Payment::TYPE_MAPPINGS
|
21
21
|
end
|
22
22
|
|