freeagent 0.1.0 → 0.2.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/CHANGELOG.rdoc +17 -0
- data/Gemfile.lock +12 -10
- data/README.rdoc +13 -1
- data/TODO +22 -0
- data/freeagent.gemspec +4 -3
- data/lib/free_agent.rb +1 -0
- data/lib/free_agent/base.rb +132 -0
- data/lib/free_agent/contact.rb +68 -0
- data/lib/free_agent/invoice.rb +58 -0
- data/lib/free_agent/project.rb +20 -0
- data/lib/free_agent/task.rb +10 -0
- data/lib/free_agent/version.rb +1 -1
- data/spec/fixtures/bank_accounts/all.xml +24 -19
- data/spec/fixtures/bank_accounts/single.xml +6 -6
- data/spec/fixtures/contacts/all.xml +13 -13
- data/spec/fixtures/contacts/all_filter_all.xml +69 -0
- data/spec/fixtures/contacts/invoices.xml +75 -0
- data/spec/fixtures/contacts/single.xml +6 -6
- data/spec/fixtures/estimates/all.xml +38 -0
- data/spec/fixtures/expenses/all.xml +93 -0
- data/spec/fixtures/expenses/single.xml +31 -0
- data/spec/fixtures/expenses/single_with_project_id.xml +31 -0
- data/spec/fixtures/invoices/all.xml +112 -75
- data/spec/fixtures/invoices/single.xml +38 -37
- data/spec/fixtures/projects/invoices.xml +39 -0
- data/spec/fixtures/projects/single.xml +2 -2
- data/spec/fixtures/projects/tasks.xml +30 -0
- data/spec/fixtures/projects/timeslips.xml +25 -0
- data/spec/fixtures/tasks/single.xml +10 -0
- data/spec/fixtures/timeslips/all.xml +25 -0
- data/spec/fixtures/timeslips/single.xml +12 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/support/helper.rb +4 -4
- data/spec/support/mimic.rb +40 -25
- data/spec/unit/bank_account_spec.rb +1 -1
- data/spec/unit/base_spec.rb +113 -0
- data/spec/unit/contact_spec.rb +87 -1
- data/spec/unit/invoice_item_spec.rb +1 -1
- data/spec/unit/invoice_spec.rb +48 -2
- data/spec/unit/project_spec.rb +34 -0
- data/spec/unit/task_spec.rb +77 -0
- metadata +45 -7
@@ -0,0 +1,25 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<timeslips type="array">
|
3
|
+
<timeslip>
|
4
|
+
<id type="integer">5903024</id>
|
5
|
+
<dated-on type="datetime">2011-04-12T00:00:00+00:00</dated-on>
|
6
|
+
<hours type="decimal">1.0</hours>
|
7
|
+
<comment>I did something.</comment>
|
8
|
+
<user-id type="integer">82948</user-id>
|
9
|
+
<project-id type="integer">116526</project-id>
|
10
|
+
<task-id type="integer">186247</task-id>
|
11
|
+
<updated-at type="integer">Tue Apr 12 20:48:43 UTC 2011</updated-at>
|
12
|
+
<status></status>
|
13
|
+
</timeslip>
|
14
|
+
<timeslip>
|
15
|
+
<id type="integer">5903025</id>
|
16
|
+
<dated-on type="datetime">2011-04-12T00:00:00+00:00</dated-on>
|
17
|
+
<hours type="decimal">3.0</hours>
|
18
|
+
<comment>It was a very difficult task!</comment>
|
19
|
+
<user-id type="integer">82948</user-id>
|
20
|
+
<project-id type="integer">116526</project-id>
|
21
|
+
<task-id type="integer">186247</task-id>
|
22
|
+
<updated-at type="integer">Tue Apr 12 20:49:21 UTC 2011</updated-at>
|
23
|
+
<status></status>
|
24
|
+
</timeslip>
|
25
|
+
</timeslips>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<timeslip>
|
3
|
+
<id type="integer">5903024</id>
|
4
|
+
<dated-on type="datetime">2011-04-12T00:00:00+00:00</dated-on>
|
5
|
+
<hours type="decimal">1.0</hours>
|
6
|
+
<comment>I did something.</comment>
|
7
|
+
<user-id type="integer">82948</user-id>
|
8
|
+
<project-id type="integer">116526</project-id>
|
9
|
+
<task-id type="integer">186247</task-id>
|
10
|
+
<updated-at type="integer">Tue Apr 12 20:48:43 UTC 2011</updated-at>
|
11
|
+
<status></status>
|
12
|
+
</timeslip>
|
data/spec/spec_helper.rb
CHANGED
data/spec/support/helper.rb
CHANGED
@@ -7,12 +7,12 @@ module Helper
|
|
7
7
|
described_class
|
8
8
|
end
|
9
9
|
|
10
|
-
def fixture(*name)
|
11
|
-
File.join(SPEC_ROOT, "fixtures", *name)
|
12
|
-
end
|
13
|
-
|
14
10
|
end
|
15
11
|
|
16
12
|
RSpec.configure do |config|
|
17
13
|
config.include Helper
|
18
14
|
end
|
15
|
+
|
16
|
+
def fixture(*path)
|
17
|
+
File.read(File.join(SPEC_ROOT, 'fixtures', *path))
|
18
|
+
end
|
data/spec/support/mimic.rb
CHANGED
@@ -1,9 +1,5 @@
|
|
1
1
|
require 'mimic'
|
2
2
|
|
3
|
-
def fixture(*path)
|
4
|
-
File.read(File.join(SPEC_ROOT, 'fixtures', *path))
|
5
|
-
end
|
6
|
-
|
7
3
|
Mimic.mimic do
|
8
4
|
|
9
5
|
use Rack::Auth::Basic do |username, password|
|
@@ -14,9 +10,9 @@ Mimic.mimic do
|
|
14
10
|
get('/attachments/1.xml').returning "", 404
|
15
11
|
get('/attachments/2.xml').returning fixture('attachments/single.xml')
|
16
12
|
|
17
|
-
get('/bank_accounts.xml').returning
|
18
|
-
get('/bank_accounts/1.xml').returning
|
19
|
-
get('/bank_accounts/
|
13
|
+
get('/bank_accounts.xml').returning fixture('bank_accounts/all.xml')
|
14
|
+
get('/bank_accounts/1.xml').returning "", 404
|
15
|
+
get('/bank_accounts/84192.xml').returning fixture('bank_accounts/single.xml')
|
20
16
|
|
21
17
|
get('/bills.xml') do
|
22
18
|
return [400, {}, []] unless params[:period]
|
@@ -26,22 +22,41 @@ Mimic.mimic do
|
|
26
22
|
get('/bills/1.xml').returning "", 404
|
27
23
|
get('/bills/2.xml').returning fixture('bills/single.xml')
|
28
24
|
|
29
|
-
get('/contacts.xml').returning
|
30
|
-
get('/contacts/1.xml').returning
|
31
|
-
get('/contacts/
|
32
|
-
|
33
|
-
get('/invoices.xml').returning
|
34
|
-
|
35
|
-
get('/invoices
|
36
|
-
|
37
|
-
get('/invoices/1
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
get('/invoices/
|
42
|
-
|
43
|
-
|
44
|
-
get('/
|
45
|
-
|
46
|
-
get('/
|
25
|
+
get('/contacts.xml').returning fixture('contacts/all.xml')
|
26
|
+
get('/contacts/1.xml').returning "", 404
|
27
|
+
get('/contacts/447693.xml').returning fixture('contacts/single.xml')
|
28
|
+
get('/contacts/1/invoices.xml').returning "", 404
|
29
|
+
get('/contacts/469012/invoices.xml').returning fixture('contacts/invoices.xml')
|
30
|
+
|
31
|
+
get('/invoices.xml').
|
32
|
+
returning fixture('invoices/all.xml')
|
33
|
+
get('/invoices/1.xml').
|
34
|
+
returning "", 404
|
35
|
+
get('/invoices/1/*').
|
36
|
+
returning "", 404
|
37
|
+
get('/invoices/2715138.xml').
|
38
|
+
returning fixture('invoices/single.xml')
|
39
|
+
|
40
|
+
get('/invoices/2/invoice_items.xml').
|
41
|
+
returning fixture('invoice_items/all.xml')
|
42
|
+
get('/invoices/2/invoice_items/1.xml').
|
43
|
+
returning "", 404
|
44
|
+
get('/invoices/2/invoice_items/2.xml').
|
45
|
+
returning fixture('invoice_items/single.xml')
|
46
|
+
|
47
|
+
put('/invoices/2715138/mark_as_draft.xml').
|
48
|
+
returning "", 200
|
49
|
+
put('/invoices/2715138/mark_as_sent.xml').
|
50
|
+
returning "", 200
|
51
|
+
put('/invoices/2715138/mark_as_cancelled.xml').
|
52
|
+
returning "", 200
|
53
|
+
|
54
|
+
get('/projects.xml').returning fixture('projects/all.xml')
|
55
|
+
get('/projects/1.xml').returning "", 404
|
56
|
+
get('/projects/1/*').returning "", 404
|
57
|
+
get('/projects/2.xml').returning fixture('projects/single.xml')
|
58
|
+
get('/projects/2/invoices.xml').returning fixture('projects/invoices.xml')
|
59
|
+
get('/projects/2/tasks.xml').returning fixture('projects/tasks.xml')
|
60
|
+
get('/projects/2/tasks/1.xml').returning "", 404
|
61
|
+
get('/projects/2/tasks/2.xml').returning fixture('tasks/single.xml')
|
47
62
|
end
|
@@ -36,7 +36,7 @@ describe FreeAgent::BankAccount do
|
|
36
36
|
describe ".find(id)" do
|
37
37
|
context "when the record exists" do
|
38
38
|
before(:each) do
|
39
|
-
@bank_account = FreeAgent::BankAccount.find(
|
39
|
+
@bank_account = FreeAgent::BankAccount.find(84192)
|
40
40
|
end
|
41
41
|
|
42
42
|
it "returns a bank account" do
|
data/spec/unit/base_spec.rb
CHANGED
@@ -6,4 +6,117 @@ describe FreeAgent::Base do
|
|
6
6
|
klass.superclass.should == ActiveResource::Base
|
7
7
|
end
|
8
8
|
|
9
|
+
|
10
|
+
describe "DynamicFinderMatch" do
|
11
|
+
|
12
|
+
class Record < FreeAgent::Base
|
13
|
+
end
|
14
|
+
|
15
|
+
before(:each) do
|
16
|
+
@records = [
|
17
|
+
Record.new(:username => "alpha", :email => "alpha@example.org", :locale => "en"),
|
18
|
+
Record.new(:username => "beta", :email => "beta@example.org", :locale => "en"),
|
19
|
+
Record.new(:username => "charlie", :email => "charlie@example.org", :locale => "jp"),
|
20
|
+
Record.new(:username => "delta", :email => "delta@example.org", :locale => "en"),
|
21
|
+
Record.new(:username => "beta", :email => "beta@example.org", :locale => "jp"),
|
22
|
+
]
|
23
|
+
stub(Record).all { @records }
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#find_by_" do
|
27
|
+
it "fetches .all records" do
|
28
|
+
mock(Record).all { @records }
|
29
|
+
Record.find_by_username('test')
|
30
|
+
end
|
31
|
+
|
32
|
+
it "searches and returns the first item matching criteria" do
|
33
|
+
Record.find_by_locale('en').should == @records[0]
|
34
|
+
end
|
35
|
+
|
36
|
+
it "supports multiple criteria" do
|
37
|
+
Record.find_by_email_and_username('beta@example.org', 'beta').should == @records[1]
|
38
|
+
Record.find_by_username_and_email('beta', 'beta@example.org').should == @records[1]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "#find_or_initialize_by_" do
|
43
|
+
it "fetches .all records" do
|
44
|
+
mock(Record).all { @records }
|
45
|
+
Record.find_or_initialize_by_username('test')
|
46
|
+
end
|
47
|
+
|
48
|
+
it "searches and returns the first item matching criteria" do
|
49
|
+
Record.find_or_initialize_by_username('en').should == @records[0]
|
50
|
+
end
|
51
|
+
|
52
|
+
it "supports multiple criteria" do
|
53
|
+
Record.find_or_initialize_by_email_and_username('beta@example.org', 'beta').should == @records[1]
|
54
|
+
Record.find_or_initialize_by_username_and_email('beta', 'beta@example.org').should == @records[1]
|
55
|
+
end
|
56
|
+
|
57
|
+
it "initializes the record if not available" do
|
58
|
+
record = Record.find_or_initialize_by_email('hello@example.org', 'hello')
|
59
|
+
record.email.should == 'hello@example.org'
|
60
|
+
|
61
|
+
record = Record.find_or_initialize_by_username_and_email('hello', 'hello@example.org')
|
62
|
+
record.username.should == 'hello'
|
63
|
+
record.email.should == 'hello@example.org'
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "#find_all_by_" do
|
68
|
+
it "fetches .all records" do
|
69
|
+
mock(Record).all { @records }
|
70
|
+
Record.find_all_by_username('test')
|
71
|
+
end
|
72
|
+
|
73
|
+
it "searches and returns all items matching criteria" do
|
74
|
+
Record.find_all_by_locale('en').should == [@records[0], @records[1], @records[3]]
|
75
|
+
end
|
76
|
+
|
77
|
+
it "supports multiple criteria" do
|
78
|
+
Record.find_all_by_email_and_username('beta@example.org', 'beta').should == [@records[1], @records[4]]
|
79
|
+
Record.find_all_by_username_and_email('beta', 'beta@example.org').should == [@records[1], @records[4]]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "#find_last_by_" do
|
84
|
+
it "fetches .all records" do
|
85
|
+
mock(Record).all { @records }
|
86
|
+
Record.find_last_by_username('test')
|
87
|
+
end
|
88
|
+
|
89
|
+
it "searches and returns the last item matching criteria" do
|
90
|
+
Record.find_last_by_locale('en').should == @records[3]
|
91
|
+
end
|
92
|
+
|
93
|
+
it "supports multiple criteria" do
|
94
|
+
Record.find_last_by_email_and_username('beta@example.org', 'beta').should == @records[4]
|
95
|
+
Record.find_last_by_username_and_email('beta', 'beta@example.org').should == @records[4]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "#respond_to?" do
|
100
|
+
it "responds to find_by_*" do
|
101
|
+
Record.new.should respond_to(:find_by_name)
|
102
|
+
Record.new.should respond_to(:find_by_name_and_email)
|
103
|
+
end
|
104
|
+
|
105
|
+
it "responds to find_or_initialize_by_*" do
|
106
|
+
Record.new.should respond_to(:find_or_initialize_by_name)
|
107
|
+
Record.new.should respond_to(:find_or_initialize_by_name_and_email)
|
108
|
+
end
|
109
|
+
|
110
|
+
it "responds to find_all_by_*" do
|
111
|
+
Record.new.should respond_to(:find_all_by_name)
|
112
|
+
Record.new.should respond_to(:find_all_by_name_and_email)
|
113
|
+
end
|
114
|
+
|
115
|
+
it "responds to find_last_by_*" do
|
116
|
+
Record.new.should respond_to(:find_last_by_name)
|
117
|
+
Record.new.should respond_to(:find_last_by_name_and_email)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
9
122
|
end
|
data/spec/unit/contact_spec.rb
CHANGED
@@ -18,6 +18,35 @@ describe FreeAgent::Contact do
|
|
18
18
|
end
|
19
19
|
|
20
20
|
|
21
|
+
describe "validations" do
|
22
|
+
it "requires either name or organisation_name" do
|
23
|
+
c = klass.new
|
24
|
+
c.should_not be_valid
|
25
|
+
c.errors[:first_name].should_not be_nil
|
26
|
+
c.errors[:last_name].should_not be_nil
|
27
|
+
c.errors[:organisation_name].should_not be_nil
|
28
|
+
|
29
|
+
c = klass.new(:last_name => "Carletti")
|
30
|
+
c.should_not be_valid
|
31
|
+
c.errors[:first_name].should_not be_nil
|
32
|
+
|
33
|
+
c = klass.new(:first_name => "Simone")
|
34
|
+
c.should_not be_valid
|
35
|
+
c.errors[:last_name].should_not be_nil
|
36
|
+
end
|
37
|
+
|
38
|
+
it "is valid with name" do
|
39
|
+
c = klass.new(:first_name => "Simone", :last_name => "Carletti")
|
40
|
+
c.should be_valid
|
41
|
+
end
|
42
|
+
|
43
|
+
it "is valid with organisation_name" do
|
44
|
+
c = klass.new(:organisation_name => "Company")
|
45
|
+
c.should be_valid
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
|
21
50
|
describe ".all" do
|
22
51
|
before(:each) do
|
23
52
|
@contacts = FreeAgent::Contact.all
|
@@ -36,7 +65,7 @@ describe FreeAgent::Contact do
|
|
36
65
|
describe ".find(id)" do
|
37
66
|
context "when the record exists" do
|
38
67
|
before(:each) do
|
39
|
-
@contacts = FreeAgent::Contact.find(
|
68
|
+
@contacts = FreeAgent::Contact.find(447693)
|
40
69
|
end
|
41
70
|
|
42
71
|
it "returns a contact" do
|
@@ -53,4 +82,61 @@ describe FreeAgent::Contact do
|
|
53
82
|
end
|
54
83
|
end
|
55
84
|
|
85
|
+
|
86
|
+
describe "#name" do
|
87
|
+
it "returns nil when no first_name and last_name" do
|
88
|
+
klass.new.name.should be_nil
|
89
|
+
end
|
90
|
+
|
91
|
+
it "returns the first_name when first_name" do
|
92
|
+
klass.new(:last_name => "Carletti").
|
93
|
+
name.should == "Carletti"
|
94
|
+
end
|
95
|
+
|
96
|
+
it "returns the first_name when first_name" do
|
97
|
+
klass.new(:first_name => "Simone").
|
98
|
+
name.should == "Simone"
|
99
|
+
end
|
100
|
+
|
101
|
+
it "returns first_name and last name" do
|
102
|
+
klass.new(:first_name => "Simone", :last_name => "Carletti").
|
103
|
+
name.should == "Simone Carletti"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe "#invoices" do
|
108
|
+
it "merges the finder options" do
|
109
|
+
mock(FreeAgent::Invoice).all(:from => '/contacts/0/invoices.xml')
|
110
|
+
FreeAgent::Contact.new(:id => 0).invoices
|
111
|
+
|
112
|
+
mock(FreeAgent::Invoice).all(:from => '/contacts/0/invoices.xml', :params => { :foo => 'bar' })
|
113
|
+
FreeAgent::Contact.new(:id => 0).invoices(:params => { :foo => 'bar' })
|
114
|
+
end
|
115
|
+
|
116
|
+
context "when the contact exists" do
|
117
|
+
before(:each) do
|
118
|
+
@invoices = FreeAgent::Contact.new(:id => 469012).invoices
|
119
|
+
end
|
120
|
+
|
121
|
+
it "returns an array" do
|
122
|
+
@invoices.should be_a(Array)
|
123
|
+
end
|
124
|
+
|
125
|
+
it "returns the invoices" do
|
126
|
+
@invoices.should have(2).records
|
127
|
+
@invoices.first.should be_a(FreeAgent::Invoice)
|
128
|
+
@invoices.first.id.should == 2715138
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
context "when the contact does not exist" do
|
133
|
+
it "raises a ResourceNotFound error" do
|
134
|
+
pending 'ActiveResource rescues the error and returns nil'
|
135
|
+
lambda do
|
136
|
+
FreeAgent::Contact.new(:id => 1).invoices
|
137
|
+
end.should raise_error(ActiveResource::ResourceNotFound)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
56
142
|
end
|
@@ -58,7 +58,7 @@ describe FreeAgent::InvoiceItem do
|
|
58
58
|
context "when the invoice does not exist" do
|
59
59
|
describe ".all" do
|
60
60
|
it "raises a ResourceNotFound error" do
|
61
|
-
pending 'ActiveResource returns nil'
|
61
|
+
pending 'ActiveResource rescues the error and returns nil'
|
62
62
|
lambda do
|
63
63
|
FreeAgent::InvoiceItem.all(:params => { :invoice_id => 1 })
|
64
64
|
end.should raise_error(ActiveResource::ResourceNotFound)
|
data/spec/unit/invoice_spec.rb
CHANGED
@@ -17,6 +17,16 @@ describe FreeAgent::Invoice do
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
+
describe "initialization" do
|
21
|
+
context "from XML" do
|
22
|
+
it "parses the #invoice_items attribute into an array of InvoiceItem" do
|
23
|
+
@invoice = FreeAgent::Invoice.find(2715138)
|
24
|
+
@invoice.invoice_items.should be_a(Array)
|
25
|
+
@invoice.invoice_items.first.should be_a(FreeAgent::InvoiceItem)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
20
30
|
|
21
31
|
describe ".all" do
|
22
32
|
before(:each) do
|
@@ -28,7 +38,7 @@ describe FreeAgent::Invoice do
|
|
28
38
|
end
|
29
39
|
|
30
40
|
it "returns the invoices" do
|
31
|
-
@invoices.should have(
|
41
|
+
@invoices.should have(3).records
|
32
42
|
@invoices.first.should be_a(FreeAgent::Invoice)
|
33
43
|
end
|
34
44
|
end
|
@@ -36,7 +46,7 @@ describe FreeAgent::Invoice do
|
|
36
46
|
describe ".find(id)" do
|
37
47
|
context "when the record exists" do
|
38
48
|
before(:each) do
|
39
|
-
@invoice = FreeAgent::Invoice.find(
|
49
|
+
@invoice = FreeAgent::Invoice.find(2715138)
|
40
50
|
end
|
41
51
|
|
42
52
|
it "returns an invoice" do
|
@@ -53,4 +63,40 @@ describe FreeAgent::Invoice do
|
|
53
63
|
end
|
54
64
|
end
|
55
65
|
|
66
|
+
describe "#mark_as_draft" do
|
67
|
+
before(:each) do
|
68
|
+
@invoice = FreeAgent::Invoice.find(2715138)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "does not complain on success" do
|
72
|
+
@invoice.mark_as_draft.should be_true
|
73
|
+
end
|
74
|
+
|
75
|
+
it "reloads the attributes"
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "#mark_as_sent" do
|
79
|
+
before(:each) do
|
80
|
+
@invoice = FreeAgent::Invoice.find(2715138)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "does not complain on success" do
|
84
|
+
@invoice.mark_as_sent.should be_true
|
85
|
+
end
|
86
|
+
|
87
|
+
it "reloads the attributes"
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "#mark_as_cancelled" do
|
91
|
+
before(:each) do
|
92
|
+
@invoice = FreeAgent::Invoice.find(2715138)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "does not complain on success" do
|
96
|
+
@invoice.mark_as_cancelled.should be_true
|
97
|
+
end
|
98
|
+
|
99
|
+
it "reloads the attributes"
|
100
|
+
end
|
101
|
+
|
56
102
|
end
|