aiaio-harvest 0.8.1 → 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,146 +1,173 @@
1
- = harvest
2
-
3
- A ruby library wrapping (most of) the Harvest Api.
4
-
5
- == DESCRIPTION:
6
-
7
- This library uses ActiveResource and activeresource_throttle to provide simple, reliable access to the Harvest Api. Good for building reporting scripts and simple apps.
8
-
9
- Note that this library does not support the complete Harvest Api at this time. See Features/Issues below for details.
10
-
11
- == USAGE:
12
-
13
- === Creating a new harvest object:
14
-
15
- All calls to the Harvest API will originate from a Harvest object. Initialize it like so:
16
-
17
- @harvest = Harvest(:email => "joe@example.com",
18
- :password => "secret",
19
- :sub_domain => "joeandcompany",
20
- :headers => {"User-Agent" => "MyCompany"})
21
-
22
- The _headers_ argument is optional. Use <b>:ssl => true</b> if your account requires HTTPS.
23
-
24
- === Clients
25
-
26
- Index
27
- @harvest.clients.find(:all)
28
- Show
29
- @harvest.clients.find(43235)
30
- Create
31
- @client = @harvest.clients.new
32
- @client.attributes = {:name => "Company, LLC", :details => "New York, NY"}
33
- @client.save
34
- Update
35
- @client.first_name = "Smith"
36
- @client.save
37
- Destroy
38
- @client.destroy
39
-
40
- Toggle (Active/Inactive)
41
- @client.toggle
42
-
43
- === Tasks
44
-
45
- Index
46
- @harvest.tasks.find(:all)
47
- Show
48
- @harvest.tasks.find(123)
49
- Create
50
- @task = @harvest.tasks.new
51
- @task.attributes = {:name => "Meeting", :billable_by_default => false,
52
- :is_default => false, :default_hourly_rate => 100}
53
- @task.save
54
- Update
55
- @task.name = "Client Meeting"
56
- @task.save
57
- Destroy
58
- @task.destroy
59
-
60
- === People
61
-
62
- Index
63
- @harvest.people.find(:all)
64
- Show
65
- @harvest.people.find(123)
66
-
67
- === Projects
68
-
69
- Index
70
- @harvest.projects.find(:all)
71
- Show
72
- @harvest.projects.find(123)
73
- Create
74
- @project = @harvest.projects.new
75
- @project.attributes = {:name => "Refactor", :active => true,
76
- :bill_by => "None", :client_id => @client.id, }
77
- @project.save
78
- Update
79
- @project.name = "Mega-Refactor"
80
- @project.save
81
- Destroy
82
- @project.destroy
83
-
84
- Toggle (Active/Inactive)
85
- @project.toggle
86
-
87
- ==== User and Task Assignments
88
-
89
- @project.users.find(:all)
90
- @project.users.find(:first)
91
-
92
- @project.tasks.find(:all)
93
- @project.tasks.find(:first)
94
-
95
- === Reports
96
-
97
- @project.entries(:from => Time.now, :to => Time.now)
98
- @project.entries(:from => Time.now, :to => Time.now, :user_id => @person.id)
99
-
100
- @person.entries(:from => Time.now, :to => Time.now)
101
- @person.expenses(:from => Time.now, :to => Time.now)
102
-
103
- == FEATURES/PROBLEMS:
104
-
105
- Supports most of the Harvest Api (http://www.getharvest.com/api).
106
-
107
- Though RESTful, the Harvest Api does not entirely follow ActiveResource conventions. In order to support the complete API, certain customizations will be required. <b>We welcome any contributions/modifications to help complete this Api.</b>
108
-
109
- The following Api features are incomplete due to incompatibilities with ActiveResource conventions (CRUD actions in parentheses):
110
-
111
- 1. People (CUD)
112
- 2. User & Task Assignments (CUD)
113
- 3. Expenses (CUD)
114
-
115
- In addition, the following have not been implemented at all due to time constraints:
116
-
117
- 1. Time Tracking Api
118
- 2. Invoices, Invoice Payments, Invoice Categories, and Invoice Messages
119
-
120
- Expect a more complete library in the near future. Again, contributions are welcome.
121
-
122
- == TEST SUITE
123
-
124
- This library includes a fairly thorough set of unit and integration tests. The unit tests can be run with the "rake" command.
125
-
126
- The integration tests require a Harvest account. It is best to use a trial account when running these tests.
127
-
128
- The credentials may be entered in test/test_helper.rb. To run the integration suite:
129
-
130
- rake test:integration
131
-
132
- Note that the integration test structure is somewhat unorthodox, as it requires tests to run in a particular order. The main test file is test/integration/harvest_integration_test.rb. Examine this to see how the integration suite works.
133
-
134
- == REQUIREMENTS:
135
-
136
- Requires active_resource >= 2.1 and activeresource_throttle >= 1.0.
137
-
138
- active_resource_throttle should install automaticallt, but just in case you need to install manually:
139
-
140
- gem sources -a http://gems.github.com
141
- sudo gem install aiaio-active_resource_throttle
142
-
143
- == INSTALL:
144
-
145
- gem sources -a http://gems.github.com
146
- gem install aiaio-harvest
1
+ = harvest
2
+
3
+ A ruby library wrapping (most of) the Harvest Api.
4
+
5
+ == DESCRIPTION:
6
+
7
+ This library uses ActiveResource and activeresource_throttle to provide simple, reliable access to the Harvest Api. Good for building reporting scripts and simple apps.
8
+
9
+ Note that this library does not support the complete Harvest Api at this time. See Features/Issues below for details.
10
+
11
+ == USAGE:
12
+
13
+ === Creating a new harvest object:
14
+
15
+ All calls to the Harvest API will originate from a Harvest object. Initialize it like so:
16
+
17
+ @harvest = Harvest(:email => "joe@example.com",
18
+ :password => "secret",
19
+ :sub_domain => "joeandcompany",
20
+ :headers => {"User-Agent" => "MyCompany"})
21
+
22
+ The _headers_ argument is optional. Use <b>:ssl => true</b> if your account requires HTTPS.
23
+
24
+ === Clients
25
+
26
+ Index
27
+ @harvest.clients.find(:all)
28
+ Show
29
+ @harvest.clients.find(43235)
30
+ Create
31
+ @client = @harvest.clients.new
32
+ @client.attributes = {:name => "Company, LLC", :details => "New York, NY"}
33
+ @client.save
34
+ Update
35
+ @client.first_name = "Smith"
36
+ @client.save
37
+ Destroy
38
+ @client.destroy
39
+
40
+ Toggle (Active/Inactive)
41
+ @client.toggle
42
+
43
+ === Tasks
44
+
45
+ Index
46
+ @harvest.tasks.find(:all)
47
+ Show
48
+ @harvest.tasks.find(123)
49
+ Create
50
+ @task = @harvest.tasks.new
51
+ @task.attributes = {:name => "Meeting", :billable_by_default => false,
52
+ :is_default => false, :default_hourly_rate => 100}
53
+ @task.save
54
+ Update
55
+ @task.name = "Client Meeting"
56
+ @task.save
57
+ Destroy
58
+ @task.destroy
59
+
60
+ === People
61
+
62
+ Index
63
+ @harvest.people.find(:all)
64
+ Show
65
+ @harvest.people.find(123)
66
+
67
+ === Projects
68
+
69
+ Index
70
+ @harvest.projects.find(:all)
71
+ Show
72
+ @harvest.projects.find(123)
73
+ Create
74
+ @project = @harvest.projects.new
75
+ @project.attributes = {:name => "Refactor", :active => true,
76
+ :bill_by => "None", :client_id => @client.id, }
77
+ @project.save
78
+ Update
79
+ @project.name = "Mega-Refactor"
80
+ @project.save
81
+ Destroy
82
+ @project.destroy
83
+
84
+ Toggle (Active/Inactive)
85
+ @project.toggle
86
+
87
+ === Invoices
88
+
89
+ Index
90
+ @invoices = @harvest.invoices.find(:all)
91
+
92
+ Show
93
+ @invoice = @harvest.invoices.find(1)
94
+
95
+ Find By Invoice Number
96
+ @invoice = @harvest.invoices.find_by_number('200')
97
+
98
+ Create
99
+ @invoice = @harvest.invoices.new
100
+ @invoice.attributes = {:amount => '250.32', :client_id => @client.id}
101
+ @invoice.save
102
+
103
+ Update
104
+ @invoice.notes = "Pending payment"
105
+ @invoice.save
106
+
107
+ Destroy
108
+ @invoice.destroy
109
+
110
+ Return parsed csv line items
111
+ @invoice.parsed_csv_line_items
112
+
113
+ ==== User and Task Assignments
114
+
115
+ @project.users.find(:all)
116
+ @project.users.find(:first)
117
+
118
+ @project.tasks.find(:all)
119
+ @project.tasks.find(:first)
120
+
121
+ === Reports
122
+
123
+ @project.entries(:from => Time.now, :to => Time.now)
124
+ @project.entries(:from => Time.now, :to => Time.now, :user_id => @person.id)
125
+
126
+ @person.entries(:from => Time.now, :to => Time.now)
127
+ @person.expenses(:from => Time.now, :to => Time.now)
128
+
129
+
130
+ == FEATURES/PROBLEMS:
131
+
132
+ Supports most of the Harvest Api (http://www.getharvest.com/api).
133
+
134
+ Though RESTful, the Harvest Api does not entirely follow ActiveResource conventions. In order to support the complete API, certain customizations will be required. <b>We welcome any contributions/modifications to help complete this Api.</b>
135
+
136
+ The following Api features are incomplete due to incompatibilities with ActiveResource conventions (CRUD actions in parentheses):
137
+
138
+ 1. People (CUD)
139
+ 2. User & Task Assignments (CUD)
140
+ 3. Expenses (CUD)
141
+
142
+ In addition, the following have not been implemented at all due to time constraints:
143
+
144
+ 1. Time Tracking Api
145
+ 2. Invoices, Invoice Payments, Invoice Categories, and Invoice Messages
146
+
147
+ Expect a more complete library in the near future. Again, contributions are welcome.
148
+
149
+ == TEST SUITE
150
+
151
+ This library includes a fairly thorough set of unit and integration tests. The unit tests can be run with the "rake" command.
152
+
153
+ The integration tests require a Harvest account. It is best to use a trial account when running these tests.
154
+
155
+ The credentials may be entered in test/test_helper.rb. To run the integration suite:
156
+
157
+ rake test:integration
158
+
159
+ Note that the integration test structure is somewhat unorthodox, as it requires tests to run in a particular order. The main test file is test/integration/harvest_integration_test.rb. Examine this to see how the integration suite works.
160
+
161
+ == REQUIREMENTS:
162
+
163
+ Requires active_resource >= 2.1 and activeresource_throttle >= 1.0.
164
+
165
+ active_resource_throttle should install automaticallt, but just in case you need to install manually:
166
+
167
+ gem sources -a http://gems.github.com
168
+ sudo gem install aiaio-active_resource_throttle
169
+
170
+ == INSTALL:
171
+
172
+ gem sources -a http://gems.github.com
173
+ gem install aiaio-harvest
@@ -5,7 +5,7 @@ module Harvest
5
5
  # Class method to load all ruby files from a given path.
6
6
  def self.load_all_ruby_files_from_path(path)
7
7
  Dir.foreach(path) do |file|
8
- require File.join(path, file) if file =~ /\.rb/
8
+ require File.join(path, file) if file =~ /\.rb$/
9
9
  end
10
10
  end
11
11
 
@@ -33,4 +33,4 @@ require File.join(File.dirname(__FILE__), "harvest", "harvest_resource")
33
33
  # :headers => {"User-Agent => "Harvest Rubygem"})
34
34
  def Harvest(options={})
35
35
  Harvest::Base.new(options)
36
- end
36
+ end
@@ -50,6 +50,11 @@ module Harvest
50
50
  Harvest::Resources::Task
51
51
  end
52
52
 
53
+ # Invoices
54
+ def invoices
55
+ Harvest::Resources::Invoice
56
+ end
57
+
53
58
  private
54
59
 
55
60
  # Configure resource base class so that
@@ -1,151 +1,150 @@
1
- require File.join(File.dirname(__FILE__), "..", "..", "test_helper")
2
-
3
- class PersonTest < Test::Unit::TestCase
4
-
5
- def setup_resources
6
- @person_xml = {:id => 1, :name => "Joe Coder"}.to_xml(:root => "person")
7
- @people = [{:id => 1, :name => "Joe Coder"}].to_xml(:root => "people")
8
- @entry = [{:id => 2, :project_id => 50}].to_xml(:root => "day-entries")
9
- @entries = [{:id => 1, :project_id => "25"},
10
- {:id => 2, :project_id => 50} ].to_xml(:root => "day-entries")
11
- @expenses = [{:id => 1, :project_id => "25"},
12
- {:id => 2, :project_id => 50} ].to_xml(:root => "expenses")
13
- end
14
-
15
- def mock_responses
16
- ActiveResource::HttpMock.respond_to do |mock|
17
- mock.get "/people.xml", {}, @people
18
- mock.get "/people/1.xml", {}, @person_xml
19
- mock.post "/people.xml", {}, @person_xml, 201, "Location" => "/people/5.xml"
20
- mock.put "/people/1.xml", {}, nil, 200
21
- mock.delete "/people/1.xml", {}, nil, 200
22
- mock.put "/people/1/toggle.xml", {}, nil, 200
23
- mock.get "/people/1/entries.xml?from=20080101&to=20080131", {}, @entries
24
- mock.get "/people/1/entries.xml?from=20080101&project_id=3&to=20080131", {}, @entry
25
- mock.get "/people/1/expenses.xml?from=20080101&to=20080131", {}, @expenses
26
- end
27
- end
28
-
29
- context "People CRUD actions -- " do
30
- setup do
31
- setup_resources
32
- mock_responses
33
- end
34
-
35
- should "get index" do
36
- Harvest::Resources::Person.find(:all)
37
- expected_request = ActiveResource::Request.new(:get, "/people.xml")
38
- assert ActiveResource::HttpMock.requests.include?(expected_request)
39
- end
40
-
41
- should "get a single project" do
42
- Harvest::Resources::Person.find(1)
43
- expected_request = ActiveResource::Request.new(:get, "/people/1.xml")
44
- assert ActiveResource::HttpMock.requests.include?(expected_request)
45
- end
46
-
47
- should "create a new project" do
48
- project = Harvest::Resources::Person.new(:name => "Widgets&Co")
49
- project.save
50
- expected_request = ActiveResource::Request.new(:post, "/people.xml")
51
- assert ActiveResource::HttpMock.requests.include?(expected_request)
52
- end
53
-
54
- should "update an existing project" do
55
- project = Harvest::Resources::Person.find(1)
56
- project.name = "Joe Coder"
57
- project.save
58
- expected_request = ActiveResource::Request.new(:put, "/people/1.xml")
59
- assert ActiveResource::HttpMock.requests.include?(expected_request)
60
- end
61
-
62
- should "delete an existing project" do
63
- Harvest::Resources::Person.delete(1)
64
- expected_request = ActiveResource::Request.new(:delete, "/people/1.xml")
65
- assert ActiveResource::HttpMock.requests.include?(expected_request)
66
- end
67
-
68
- end
69
-
70
-
71
- context "Toggling active/inactive status" do
72
- setup do
73
- setup_resources
74
- mock_responses
75
- end
76
-
77
- should "hit the toggle method" do
78
- person = Harvest::Resources::Person.find(1)
79
- person.toggle
80
- expected_request = ActiveResource::Request.new(:put, "/people/1/toggle.xml")
81
- assert ActiveResource::HttpMock.requests.include?(expected_request)
82
- end
83
-
84
- should "return 200 Success toggle method" do
85
- person = Harvest::Resources::Person.find(1)
86
- assert person.toggle.code == 200
87
- end
88
-
89
- end
90
-
91
- context "Getting entries" do
92
- setup do
93
- setup_resources
94
- mock_responses
95
- @person = Harvest::Resources::Person.find(1)
96
- end
97
-
98
- should "raise an error if start and end are not included" do
99
- assert_raises ArgumentError, "Must specify :start and :end as dates." do
100
- @person.entries
101
- end
102
- end
103
-
104
- should "raise an error if end date precedes start date" do
105
- assert_raises ArgumentError, "Must specify :start and :end as dates." do
106
- @person.entries(:from => Time.utc(2007, 1, 1), :to => Time.utc(2006, 1, 1))
107
- end
108
- end
109
-
110
- should "return the project site prefix" do
111
- entry_class = Harvest::Resources::Entry.clone
112
- entry_class.person_id = @person.id
113
- assert_equal "http://example.com/people/1", entry_class.site.to_s
114
- end
115
-
116
- end
117
-
118
- context "Reports -- " do
119
- setup do
120
- setup_resources
121
- mock_responses
122
- @from = Time.utc(2008, 1, 1)
123
- @to = Time.utc(2008, 1, 31)
124
- @person = Harvest::Resources::Person.find(1)
125
- @request_path = "/people/1/entries.xml?from=20080101&to=20080131"
126
- @expense_path = "/people/1/expenses.xml?from=20080101&to=20080131"
127
- end
128
-
129
- should "get all entries for a given date range" do
130
- @person.entries(:from => @from, :to => @to)
131
- expected_request = ActiveResource::Request.new(:get, @request_path)
132
- assert ActiveResource::HttpMock.requests.include?(expected_request)
133
- end
134
-
135
- should "get all entries for the given date range and user" do
136
- path = "/people/1/entries.xml?from=20080101&project_id=3&to=20080131"
137
- @person.entries(:from => @from, :to => @to, :project_id => 3)
138
- expected_request = ActiveResource::Request.new(:get, path)
139
- assert ActiveResource::HttpMock.requests.include?(expected_request)
140
- end
141
-
142
- should "return all expense entries logged by the given user" do
143
- @person.expenses(:from => @from, :to => @to)
144
- expected_request = ActiveResource::Request.new(:get, @expense_path)
145
- p ActiveResource::HttpMock.requests
146
- assert ActiveResource::HttpMock.requests.include?(expected_request)
147
- end
148
-
149
- end
150
-
151
- end
1
+ require File.join(File.dirname(__FILE__), "..", "..", "test_helper")
2
+
3
+ class PersonTest < Test::Unit::TestCase
4
+
5
+ def setup_resources
6
+ @person_xml = {:id => 1, :name => "Joe Coder"}.to_xml(:root => "person")
7
+ @people = [{:id => 1, :name => "Joe Coder"}].to_xml(:root => "people")
8
+ @entry = [{:id => 2, :project_id => 50}].to_xml(:root => "day-entries")
9
+ @entries = [{:id => 1, :project_id => "25"},
10
+ {:id => 2, :project_id => 50} ].to_xml(:root => "day-entries")
11
+ @expenses = [{:id => 1, :project_id => "25"},
12
+ {:id => 2, :project_id => 50} ].to_xml(:root => "expenses")
13
+ end
14
+
15
+ def mock_responses
16
+ ActiveResource::HttpMock.respond_to do |mock|
17
+ mock.get "/people.xml", {}, @people
18
+ mock.get "/people/1.xml", {}, @person_xml
19
+ mock.post "/people.xml", {}, @person_xml, 201, "Location" => "/people/5.xml"
20
+ mock.put "/people/1.xml", {}, nil, 200
21
+ mock.delete "/people/1.xml", {}, nil, 200
22
+ mock.put "/people/1/toggle.xml", {}, nil, 200
23
+ mock.get "/people/1/entries.xml?from=20080101&to=20080131", {}, @entries
24
+ mock.get "/people/1/entries.xml?from=20080101&project_id=3&to=20080131", {}, @entry
25
+ mock.get "/people/1/expenses.xml?from=20080101&to=20080131", {}, @expenses
26
+ end
27
+ end
28
+
29
+ context "People CRUD actions -- " do
30
+ setup do
31
+ setup_resources
32
+ mock_responses
33
+ end
34
+
35
+ should "get index" do
36
+ Harvest::Resources::Person.find(:all)
37
+ expected_request = ActiveResource::Request.new(:get, "/people.xml")
38
+ assert ActiveResource::HttpMock.requests.include?(expected_request)
39
+ end
40
+
41
+ should "get a single project" do
42
+ Harvest::Resources::Person.find(1)
43
+ expected_request = ActiveResource::Request.new(:get, "/people/1.xml")
44
+ assert ActiveResource::HttpMock.requests.include?(expected_request)
45
+ end
46
+
47
+ should "create a new project" do
48
+ project = Harvest::Resources::Person.new(:name => "Widgets&Co")
49
+ project.save
50
+ expected_request = ActiveResource::Request.new(:post, "/people.xml")
51
+ assert ActiveResource::HttpMock.requests.include?(expected_request)
52
+ end
53
+
54
+ should "update an existing project" do
55
+ project = Harvest::Resources::Person.find(1)
56
+ project.name = "Joe Coder"
57
+ project.save
58
+ expected_request = ActiveResource::Request.new(:put, "/people/1.xml")
59
+ assert ActiveResource::HttpMock.requests.include?(expected_request)
60
+ end
61
+
62
+ should "delete an existing project" do
63
+ Harvest::Resources::Person.delete(1)
64
+ expected_request = ActiveResource::Request.new(:delete, "/people/1.xml")
65
+ assert ActiveResource::HttpMock.requests.include?(expected_request)
66
+ end
67
+
68
+ end
69
+
70
+
71
+ context "Toggling active/inactive status" do
72
+ setup do
73
+ setup_resources
74
+ mock_responses
75
+ end
76
+
77
+ should "hit the toggle method" do
78
+ person = Harvest::Resources::Person.find(1)
79
+ person.toggle
80
+ expected_request = ActiveResource::Request.new(:put, "/people/1/toggle.xml")
81
+ assert ActiveResource::HttpMock.requests.include?(expected_request)
82
+ end
83
+
84
+ should "return 200 Success toggle method" do
85
+ person = Harvest::Resources::Person.find(1)
86
+ assert person.toggle.code == 200
87
+ end
88
+
89
+ end
90
+
91
+ context "Getting entries" do
92
+ setup do
93
+ setup_resources
94
+ mock_responses
95
+ @person = Harvest::Resources::Person.find(1)
96
+ end
97
+
98
+ should "raise an error if start and end are not included" do
99
+ assert_raises ArgumentError, "Must specify :start and :end as dates." do
100
+ @person.entries
101
+ end
102
+ end
103
+
104
+ should "raise an error if end date precedes start date" do
105
+ assert_raises ArgumentError, "Must specify :start and :end as dates." do
106
+ @person.entries(:from => Time.utc(2007, 1, 1), :to => Time.utc(2006, 1, 1))
107
+ end
108
+ end
109
+
110
+ should "return the project site prefix" do
111
+ entry_class = Harvest::Resources::Entry.clone
112
+ entry_class.person_id = @person.id
113
+ assert_equal "http://example.com/people/1", entry_class.site.to_s
114
+ end
115
+
116
+ end
117
+
118
+ context "Reports -- " do
119
+ setup do
120
+ setup_resources
121
+ mock_responses
122
+ @from = Time.utc(2008, 1, 1)
123
+ @to = Time.utc(2008, 1, 31)
124
+ @person = Harvest::Resources::Person.find(1)
125
+ @request_path = "/people/1/entries.xml?from=20080101&to=20080131"
126
+ @expense_path = "/people/1/expenses.xml?from=20080101&to=20080131"
127
+ end
128
+
129
+ should "get all entries for a given date range" do
130
+ @person.entries(:from => @from, :to => @to)
131
+ expected_request = ActiveResource::Request.new(:get, @request_path)
132
+ assert ActiveResource::HttpMock.requests.include?(expected_request)
133
+ end
134
+
135
+ should "get all entries for the given date range and user" do
136
+ path = "/people/1/entries.xml?from=20080101&project_id=3&to=20080131"
137
+ @person.entries(:from => @from, :to => @to, :project_id => 3)
138
+ expected_request = ActiveResource::Request.new(:get, path)
139
+ assert ActiveResource::HttpMock.requests.include?(expected_request)
140
+ end
141
+
142
+ should "return all expense entries logged by the given user" do
143
+ @person.expenses(:from => @from, :to => @to)
144
+ expected_request = ActiveResource::Request.new(:get, @expense_path)
145
+ assert ActiveResource::HttpMock.requests.include?(expected_request)
146
+ end
147
+
148
+ end
149
+
150
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aiaio-harvest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.8.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kyle Banker