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.
Files changed (42) hide show
  1. data/CHANGELOG.rdoc +17 -0
  2. data/Gemfile.lock +12 -10
  3. data/README.rdoc +13 -1
  4. data/TODO +22 -0
  5. data/freeagent.gemspec +4 -3
  6. data/lib/free_agent.rb +1 -0
  7. data/lib/free_agent/base.rb +132 -0
  8. data/lib/free_agent/contact.rb +68 -0
  9. data/lib/free_agent/invoice.rb +58 -0
  10. data/lib/free_agent/project.rb +20 -0
  11. data/lib/free_agent/task.rb +10 -0
  12. data/lib/free_agent/version.rb +1 -1
  13. data/spec/fixtures/bank_accounts/all.xml +24 -19
  14. data/spec/fixtures/bank_accounts/single.xml +6 -6
  15. data/spec/fixtures/contacts/all.xml +13 -13
  16. data/spec/fixtures/contacts/all_filter_all.xml +69 -0
  17. data/spec/fixtures/contacts/invoices.xml +75 -0
  18. data/spec/fixtures/contacts/single.xml +6 -6
  19. data/spec/fixtures/estimates/all.xml +38 -0
  20. data/spec/fixtures/expenses/all.xml +93 -0
  21. data/spec/fixtures/expenses/single.xml +31 -0
  22. data/spec/fixtures/expenses/single_with_project_id.xml +31 -0
  23. data/spec/fixtures/invoices/all.xml +112 -75
  24. data/spec/fixtures/invoices/single.xml +38 -37
  25. data/spec/fixtures/projects/invoices.xml +39 -0
  26. data/spec/fixtures/projects/single.xml +2 -2
  27. data/spec/fixtures/projects/tasks.xml +30 -0
  28. data/spec/fixtures/projects/timeslips.xml +25 -0
  29. data/spec/fixtures/tasks/single.xml +10 -0
  30. data/spec/fixtures/timeslips/all.xml +25 -0
  31. data/spec/fixtures/timeslips/single.xml +12 -0
  32. data/spec/spec_helper.rb +1 -1
  33. data/spec/support/helper.rb +4 -4
  34. data/spec/support/mimic.rb +40 -25
  35. data/spec/unit/bank_account_spec.rb +1 -1
  36. data/spec/unit/base_spec.rb +113 -0
  37. data/spec/unit/contact_spec.rb +87 -1
  38. data/spec/unit/invoice_item_spec.rb +1 -1
  39. data/spec/unit/invoice_spec.rb +48 -2
  40. data/spec/unit/project_spec.rb +34 -0
  41. data/spec/unit/task_spec.rb +77 -0
  42. 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>
@@ -19,7 +19,7 @@ end
19
19
  Dir[File.join(SPEC_ROOT, "support/**/*.rb")].each { |f| require f }
20
20
 
21
21
  RSpec.configure do |config|
22
- # config.mock_with :rr
22
+ config.mock_with :rr
23
23
 
24
24
  config.before(:each) do
25
25
  FreeAgent.configure do |config|
@@ -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
@@ -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 fixture('bank_accounts/all.xml')
18
- get('/bank_accounts/1.xml').returning "", 404
19
- get('/bank_accounts/2.xml').returning fixture('bank_accounts/single.xml')
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 fixture('contacts/all.xml')
30
- get('/contacts/1.xml').returning "", 404
31
- get('/contacts/2.xml').returning fixture('contacts/single.xml')
32
-
33
- get('/invoices.xml').returning fixture('invoices/all.xml')
34
- get('/invoices/1.xml').returning "", 404
35
- get('/invoices/2.xml').returning fixture('invoices/single.xml')
36
-
37
- get('/invoices/1/*') do
38
- [404, {}, []]
39
- end
40
- get('/invoices/2/invoice_items.xml').returning fixture('invoice_items/all.xml')
41
- get('/invoices/2/invoice_items/1.xml').returning "", 404
42
- get('/invoices/2/invoice_items/2.xml').returning fixture('invoice_items/single.xml')
43
-
44
- get('/projects.xml').returning fixture('projects/all.xml')
45
- get('/projects/1.xml').returning "", 404
46
- get('/projects/2.xml').returning fixture('projects/single.xml')
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(2)
39
+ @bank_account = FreeAgent::BankAccount.find(84192)
40
40
  end
41
41
 
42
42
  it "returns a bank account" do
@@ -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
@@ -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(2)
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)
@@ -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(2).records
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(2)
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