harvested 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +3 -0
- data/.gitignore +2 -0
- data/README.md +4 -3
- data/Rakefile +10 -1
- data/VERSION +1 -1
- data/examples/basics.rb +1 -1
- data/examples/clear_account.rb +1 -1
- data/examples/task_assignments.rb +1 -1
- data/examples/user_assignments.rb +1 -1
- data/harvested.gemspec +159 -0
- data/lib/harvest/api/account.rb +5 -0
- data/lib/harvest/api/base.rb +3 -3
- data/lib/harvest/api/clients.rb +1 -1
- data/lib/harvest/api/contacts.rb +1 -1
- data/lib/harvest/api/projects.rb +16 -1
- data/lib/harvest/api/users.rb +7 -1
- data/lib/harvest/base.rb +199 -0
- data/lib/harvest/base_model.rb +43 -4
- data/lib/harvest/behavior/activatable.rb +11 -1
- data/lib/harvest/behavior/crud.rb +27 -1
- data/lib/harvest/client.rb +13 -1
- data/lib/harvest/contact.rb +13 -0
- data/lib/harvest/project.rb +36 -2
- data/lib/harvest/rate_limit_status.rb +12 -0
- data/lib/harvest/task.rb +10 -0
- data/lib/harvest/timezones.rb +1 -2
- data/lib/harvest/user.rb +29 -3
- data/lib/harvested.rb +32 -1
- data/spec/spec_helper.rb +1 -1
- metadata +26 -5
data/.document
ADDED
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -8,14 +8,14 @@ This is a Ruby wrapper for the [Harvest API](http://www.getharvest.com/).
|
|
8
8
|
|
9
9
|
## Examples
|
10
10
|
|
11
|
-
harvest = Harvest.client('yoursubdomain', 'yourusername', 'yourpassword'
|
11
|
+
harvest = Harvest.client('yoursubdomain', 'yourusername', 'yourpassword')
|
12
12
|
harvest.projects() # list out projects
|
13
13
|
|
14
14
|
client = Harvest::Client.new(:name => "Billable Company LTD.")
|
15
15
|
client = harvest.clients.create(client)
|
16
16
|
harvest.clients.find(client.id) # returns a Harvest::Client
|
17
17
|
|
18
|
-
You can find more examples in `/examples`
|
18
|
+
You can find more examples in `/examples` and in the documentation for Harvest::Base
|
19
19
|
|
20
20
|
## Hardy Client
|
21
21
|
|
@@ -23,11 +23,12 @@ The guys at Harvest built a great API, but there are always dangers in writing c
|
|
23
23
|
|
24
24
|
Using `Harvested#client` your code needs to handle all these situations. However you can also use `Harvested#hardy_client` which will retry errors and wait for Rate Limit resets.
|
25
25
|
|
26
|
-
harvest = Harvest.hardy_client('yoursubdomain', 'yourusername', 'yourpassword'
|
26
|
+
harvest = Harvest.hardy_client('yoursubdomain', 'yourusername', 'yourpassword')
|
27
27
|
harvest.projects() # This will wait for the Rate Limit reset if you have gone over your limit
|
28
28
|
|
29
29
|
## Links
|
30
30
|
|
31
|
+
* [Harvested Documentation](http://rdoc.info/projects/zmoazeni/harvested)
|
31
32
|
* [Harvest API Documentation](http://www.getharvest.com/api)
|
32
33
|
* [Source Code for Harvested](http://github.com/zmoazeni/harvested)
|
33
34
|
* [Pivotal Tracker for Harvested](http://www.pivotaltracker.com/projects/63260)
|
data/Rakefile
CHANGED
@@ -6,7 +6,7 @@ begin
|
|
6
6
|
Jeweler::Tasks.new do |gem|
|
7
7
|
gem.name = "harvested"
|
8
8
|
gem.summary = %Q{A Ruby Wrapper for the Harvest API http://www.getharvest.com/}
|
9
|
-
gem.description = %Q{Harvested wraps the Harvest API concisely without the use of Rails dependencies. More information about the Harvest API can be found
|
9
|
+
gem.description = %Q{Harvested wraps the Harvest API concisely without the use of Rails dependencies. More information about the Harvest API can be found on their website (http://www.getharvest.com/api). For support hit up the Mailing List (http://groups.google.com/group/harvested)}
|
10
10
|
gem.email = "zach.moazeni@gmail.com"
|
11
11
|
gem.homepage = "http://github.com/zmoazeni/harvested"
|
12
12
|
gem.authors = ["Zach Moazeni"]
|
@@ -50,3 +50,12 @@ rescue LoadError
|
|
50
50
|
end
|
51
51
|
|
52
52
|
task :default => %w(spec features)
|
53
|
+
|
54
|
+
begin
|
55
|
+
require 'yard'
|
56
|
+
YARD::Rake::YardocTask.new
|
57
|
+
rescue LoadError
|
58
|
+
task :yardoc do
|
59
|
+
abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
|
60
|
+
end
|
61
|
+
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.3.
|
1
|
+
0.3.1
|
data/examples/basics.rb
CHANGED
@@ -4,7 +4,7 @@ subdomain = 'yoursubdomain'
|
|
4
4
|
username = 'yourusername'
|
5
5
|
password = 'yourpassword'
|
6
6
|
|
7
|
-
harvest = Harvest.hardy_client(subdomain, username, password
|
7
|
+
harvest = Harvest.hardy_client(subdomain, username, password)
|
8
8
|
|
9
9
|
# Print out all users, clients, and projects on the account
|
10
10
|
puts "Users:"
|
data/examples/clear_account.rb
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
# username = 'yourusername'
|
7
7
|
# password = 'yourpassword'
|
8
8
|
#
|
9
|
-
# api = Harvest.hardy_client(subdomain, username, password
|
9
|
+
# api = Harvest.hardy_client(subdomain, username, password)
|
10
10
|
#
|
11
11
|
# raise "Are you sure you want to do this? (comment out this exception if so)"
|
12
12
|
#
|
@@ -4,7 +4,7 @@ subdomain = 'yoursubdomain'
|
|
4
4
|
username = 'yourusername'
|
5
5
|
password = 'yourpassword'
|
6
6
|
|
7
|
-
harvest = Harvest.hardy_client(subdomain, username, password
|
7
|
+
harvest = Harvest.hardy_client(subdomain, username, password)
|
8
8
|
|
9
9
|
# Create a Client add a Project to that Client
|
10
10
|
|
@@ -4,7 +4,7 @@ subdomain = 'yoursubdomain'
|
|
4
4
|
username = 'userusername'
|
5
5
|
password = 'yourpassword'
|
6
6
|
|
7
|
-
harvest = Harvest.hardy_client(subdomain, username, password
|
7
|
+
harvest = Harvest.hardy_client(subdomain, username, password)
|
8
8
|
|
9
9
|
# Create a Client, Project, and a User then assign that User to that Project
|
10
10
|
|
data/harvested.gemspec
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{harvested}
|
8
|
+
s.version = "0.3.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Zach Moazeni"]
|
12
|
+
s.date = %q{2010-10-14}
|
13
|
+
s.description = %q{Harvested wraps the Harvest API concisely without the use of Rails dependencies. More information about the Harvest API can be found on their website (http://www.getharvest.com/api). For support hit up the Mailing List (http://groups.google.com/group/harvested)}
|
14
|
+
s.email = %q{zach.moazeni@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"README.md"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
".document",
|
20
|
+
".gitignore",
|
21
|
+
"HISTORY",
|
22
|
+
"MIT-LICENSE",
|
23
|
+
"README.md",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"examples/basics.rb",
|
27
|
+
"examples/clear_account.rb",
|
28
|
+
"examples/task_assignments.rb",
|
29
|
+
"examples/user_assignments.rb",
|
30
|
+
"features/account.feature",
|
31
|
+
"features/client_contacts.feature",
|
32
|
+
"features/clients.feature",
|
33
|
+
"features/errors.feature",
|
34
|
+
"features/expense_categories.feature",
|
35
|
+
"features/expenses.feature",
|
36
|
+
"features/hardy_client.feature",
|
37
|
+
"features/projects.feature",
|
38
|
+
"features/reporting.feature",
|
39
|
+
"features/step_definitions/account_steps.rb",
|
40
|
+
"features/step_definitions/assignment_steps.rb",
|
41
|
+
"features/step_definitions/contact_steps.rb",
|
42
|
+
"features/step_definitions/debug_steps.rb",
|
43
|
+
"features/step_definitions/error_steps.rb",
|
44
|
+
"features/step_definitions/expenses_steps.rb",
|
45
|
+
"features/step_definitions/harvest_steps.rb",
|
46
|
+
"features/step_definitions/model_steps.rb",
|
47
|
+
"features/step_definitions/people_steps.rb",
|
48
|
+
"features/step_definitions/report_steps.rb",
|
49
|
+
"features/step_definitions/time_entry_steps.rb",
|
50
|
+
"features/support/env.rb",
|
51
|
+
"features/support/error_helpers.rb",
|
52
|
+
"features/support/fixtures/empty_clients.xml",
|
53
|
+
"features/support/fixtures/over_limit.xml",
|
54
|
+
"features/support/fixtures/receipt.png",
|
55
|
+
"features/support/fixtures/under_limit.xml",
|
56
|
+
"features/support/harvest_credentials.example.yml",
|
57
|
+
"features/support/harvest_helpers.rb",
|
58
|
+
"features/support/inflections.rb",
|
59
|
+
"features/task_assignment.feature",
|
60
|
+
"features/tasks.feature",
|
61
|
+
"features/time_tracking.feature",
|
62
|
+
"features/user_assignments.feature",
|
63
|
+
"features/users.feature",
|
64
|
+
"harvested.gemspec",
|
65
|
+
"lib/harvest/api/account.rb",
|
66
|
+
"lib/harvest/api/base.rb",
|
67
|
+
"lib/harvest/api/clients.rb",
|
68
|
+
"lib/harvest/api/contacts.rb",
|
69
|
+
"lib/harvest/api/expense_categories.rb",
|
70
|
+
"lib/harvest/api/expenses.rb",
|
71
|
+
"lib/harvest/api/projects.rb",
|
72
|
+
"lib/harvest/api/reports.rb",
|
73
|
+
"lib/harvest/api/task_assignments.rb",
|
74
|
+
"lib/harvest/api/tasks.rb",
|
75
|
+
"lib/harvest/api/time.rb",
|
76
|
+
"lib/harvest/api/user_assignments.rb",
|
77
|
+
"lib/harvest/api/users.rb",
|
78
|
+
"lib/harvest/base.rb",
|
79
|
+
"lib/harvest/base_model.rb",
|
80
|
+
"lib/harvest/behavior/activatable.rb",
|
81
|
+
"lib/harvest/behavior/crud.rb",
|
82
|
+
"lib/harvest/client.rb",
|
83
|
+
"lib/harvest/contact.rb",
|
84
|
+
"lib/harvest/credentials.rb",
|
85
|
+
"lib/harvest/errors.rb",
|
86
|
+
"lib/harvest/expense.rb",
|
87
|
+
"lib/harvest/expense_category.rb",
|
88
|
+
"lib/harvest/hardy_client.rb",
|
89
|
+
"lib/harvest/project.rb",
|
90
|
+
"lib/harvest/rate_limit_status.rb",
|
91
|
+
"lib/harvest/task.rb",
|
92
|
+
"lib/harvest/task_assignment.rb",
|
93
|
+
"lib/harvest/time_entry.rb",
|
94
|
+
"lib/harvest/timezones.rb",
|
95
|
+
"lib/harvest/user.rb",
|
96
|
+
"lib/harvest/user_assignment.rb",
|
97
|
+
"lib/harvested.rb",
|
98
|
+
"spec/harvest/base_spec.rb",
|
99
|
+
"spec/harvest/credentials_spec.rb",
|
100
|
+
"spec/harvest/expense_spec.rb",
|
101
|
+
"spec/harvest/task_assignment_spec.rb",
|
102
|
+
"spec/harvest/time_entry_spec.rb",
|
103
|
+
"spec/harvest/user_assignment_spec.rb",
|
104
|
+
"spec/harvest/user_spec.rb",
|
105
|
+
"spec/spec.default.opts",
|
106
|
+
"spec/spec_helper.rb"
|
107
|
+
]
|
108
|
+
s.homepage = %q{http://github.com/zmoazeni/harvested}
|
109
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
110
|
+
s.require_paths = ["lib"]
|
111
|
+
s.rubygems_version = %q{1.3.7}
|
112
|
+
s.summary = %q{A Ruby Wrapper for the Harvest API http://www.getharvest.com/}
|
113
|
+
s.test_files = [
|
114
|
+
"spec/harvest/base_spec.rb",
|
115
|
+
"spec/harvest/credentials_spec.rb",
|
116
|
+
"spec/harvest/expense_spec.rb",
|
117
|
+
"spec/harvest/task_assignment_spec.rb",
|
118
|
+
"spec/harvest/time_entry_spec.rb",
|
119
|
+
"spec/harvest/user_assignment_spec.rb",
|
120
|
+
"spec/harvest/user_spec.rb",
|
121
|
+
"spec/spec_helper.rb",
|
122
|
+
"examples/basics.rb",
|
123
|
+
"examples/clear_account.rb",
|
124
|
+
"examples/task_assignments.rb",
|
125
|
+
"examples/user_assignments.rb"
|
126
|
+
]
|
127
|
+
|
128
|
+
if s.respond_to? :specification_version then
|
129
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
130
|
+
s.specification_version = 3
|
131
|
+
|
132
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
133
|
+
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
134
|
+
s.add_development_dependency(%q<cucumber>, [">= 0"])
|
135
|
+
s.add_development_dependency(%q<ruby-debug>, [">= 0"])
|
136
|
+
s.add_development_dependency(%q<fakeweb>, [">= 0"])
|
137
|
+
s.add_runtime_dependency(%q<httparty>, [">= 0"])
|
138
|
+
s.add_runtime_dependency(%q<happymapper>, [">= 0"])
|
139
|
+
s.add_runtime_dependency(%q<builder>, [">= 0"])
|
140
|
+
else
|
141
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
142
|
+
s.add_dependency(%q<cucumber>, [">= 0"])
|
143
|
+
s.add_dependency(%q<ruby-debug>, [">= 0"])
|
144
|
+
s.add_dependency(%q<fakeweb>, [">= 0"])
|
145
|
+
s.add_dependency(%q<httparty>, [">= 0"])
|
146
|
+
s.add_dependency(%q<happymapper>, [">= 0"])
|
147
|
+
s.add_dependency(%q<builder>, [">= 0"])
|
148
|
+
end
|
149
|
+
else
|
150
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
151
|
+
s.add_dependency(%q<cucumber>, [">= 0"])
|
152
|
+
s.add_dependency(%q<ruby-debug>, [">= 0"])
|
153
|
+
s.add_dependency(%q<fakeweb>, [">= 0"])
|
154
|
+
s.add_dependency(%q<httparty>, [">= 0"])
|
155
|
+
s.add_dependency(%q<happymapper>, [">= 0"])
|
156
|
+
s.add_dependency(%q<builder>, [">= 0"])
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
data/lib/harvest/api/account.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
module Harvest
|
2
2
|
module API
|
3
|
+
|
4
|
+
# API Methods to contain all account actions
|
3
5
|
class Account < Base
|
6
|
+
|
7
|
+
# Returns the current rate limit information
|
8
|
+
# @return [Harvest::RateLimitStatus]
|
4
9
|
def rate_limit_status
|
5
10
|
response = request(:get, credentials, '/account/rate_limit_status')
|
6
11
|
Harvest::RateLimitStatus.parse(response.body)
|
data/lib/harvest/api/base.rb
CHANGED
@@ -2,11 +2,11 @@ module Harvest
|
|
2
2
|
module API
|
3
3
|
class Base
|
4
4
|
attr_reader :credentials
|
5
|
-
|
5
|
+
|
6
6
|
def initialize(credentials)
|
7
7
|
@credentials = credentials
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
class << self
|
11
11
|
def api_model(klass)
|
12
12
|
class_eval <<-END
|
@@ -16,7 +16,7 @@ module Harvest
|
|
16
16
|
END
|
17
17
|
end
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
protected
|
21
21
|
def request(method, credentials, path, options = {})
|
22
22
|
response = HTTParty.send(method, "#{credentials.host}#{path}", :query => options[:query], :body => options[:body], :headers => {"Accept" => "application/xml", "Content-Type" => "application/xml; charset=utf-8", "Authorization" => "Basic #{credentials.basic_auth}", "User-Agent" => "Harvestable/#{Harvest::VERSION}"}.update(options[:headers] || {}), :format => :plain)
|
data/lib/harvest/api/clients.rb
CHANGED
data/lib/harvest/api/contacts.rb
CHANGED
data/lib/harvest/api/projects.rb
CHANGED
@@ -5,12 +5,23 @@ module Harvest
|
|
5
5
|
|
6
6
|
include Harvest::Behavior::Crud
|
7
7
|
|
8
|
+
# Creates and Assigns a task to the project
|
9
|
+
#
|
10
|
+
# == Examples
|
11
|
+
# project = harvest.projects.find(401)
|
12
|
+
# harvest.projects.create_task(project, 'Bottling Glue') # creates and assigns a task to the project
|
13
|
+
#
|
14
|
+
# @return [Harvest::Project]
|
8
15
|
def create_task(project, task_name)
|
9
16
|
response = request(:post, credentials, "/projects/#{project.to_i}/task_assignments/add_with_create_new_task", :body => task_xml(task_name))
|
10
17
|
id = response.headers["location"].first.match(/\/.*\/(\d+)\/.*\/(\d+)/)[1]
|
11
18
|
find(id)
|
12
19
|
end
|
13
20
|
|
21
|
+
# Deactivates the project. Does nothing if the project is already deactivated
|
22
|
+
#
|
23
|
+
# @param [Harvest::Project] project the project you want to deactivate
|
24
|
+
# @return [Harvest::Project] the deactivated project
|
14
25
|
def deactivate(project)
|
15
26
|
if project.active?
|
16
27
|
request(:put, credentials, "#{api_model.api_path}/#{project.to_i}/toggle", :headers => {'Content-Length' => '0'})
|
@@ -18,7 +29,11 @@ module Harvest
|
|
18
29
|
end
|
19
30
|
project
|
20
31
|
end
|
21
|
-
|
32
|
+
|
33
|
+
# Activates the project. Does nothing if the project is already activated
|
34
|
+
#
|
35
|
+
# @param [Harvest::Project] project the project you want to activate
|
36
|
+
# @return [Harvest::Project] the activated project
|
22
37
|
def activate(project)
|
23
38
|
if !project.active?
|
24
39
|
request(:put, credentials, "#{api_model.api_path}/#{project.to_i}/toggle", :headers => {'Content-Length' => '0'})
|
data/lib/harvest/api/users.rb
CHANGED
@@ -5,7 +5,13 @@ module Harvest
|
|
5
5
|
|
6
6
|
include Harvest::Behavior::Crud
|
7
7
|
include Harvest::Behavior::Activatable
|
8
|
-
|
8
|
+
|
9
|
+
# Triggers Harvest to reset the user's password and sends them an email to change it.
|
10
|
+
# @overload reset_password(id)
|
11
|
+
# @param [Integer] id the id of the user you want to reset the password for
|
12
|
+
# @overload reset_password(user)
|
13
|
+
# @param [Harvest::User] user the user you want to reset the password for
|
14
|
+
# @return [Harvest::User] the user you passed in
|
9
15
|
def reset_password(user)
|
10
16
|
request(:post, credentials, "#{api_model.api_path}/#{user.to_i}/reset_password")
|
11
17
|
user
|
data/lib/harvest/base.rb
CHANGED
@@ -2,48 +2,247 @@ module Harvest
|
|
2
2
|
class Base
|
3
3
|
attr_reader :request, :credentials
|
4
4
|
|
5
|
+
# @see Harvest.client
|
6
|
+
# @see Harvest.hardy_client
|
5
7
|
def initialize(subdomain, username, password, options = {})
|
6
8
|
options[:ssl] = true if options[:ssl].nil?
|
7
9
|
@credentials = Credentials.new(subdomain, username, password, options[:ssl])
|
8
10
|
raise InvalidCredentials unless credentials.valid?
|
9
11
|
end
|
10
12
|
|
13
|
+
# All API actions surrounding accounts
|
14
|
+
#
|
15
|
+
# == Examples
|
16
|
+
# harvest.account.rate_limit_status # Returns a Harvest::RateLimitStatus
|
17
|
+
#
|
18
|
+
# @return [Harvest::API::Account]
|
11
19
|
def account
|
12
20
|
@account ||= Harvest::API::Account.new(credentials)
|
13
21
|
end
|
14
22
|
|
23
|
+
# All API Actions surrounding Clients
|
24
|
+
#
|
25
|
+
# == Examples
|
26
|
+
# harvest.clients.all() # Returns all clients in the system
|
27
|
+
#
|
28
|
+
# harvest.clients.find(100) # Returns the client with id = 100
|
29
|
+
#
|
30
|
+
# client = Harvest::Client.new(:name => 'SuprCorp')
|
31
|
+
# saved_client = harvest.clients.create(client) # returns a saved version of Harvest::Client
|
32
|
+
#
|
33
|
+
# client = harvest.clients.find(205)
|
34
|
+
# client.name = 'SuprCorp LTD.'
|
35
|
+
# updated_client = harvest.clients.update(client) # returns an updated version of Harvest::Client
|
36
|
+
#
|
37
|
+
# client = harvest.clients.find(205)
|
38
|
+
# harvest.clients.delete(client) # returns 205
|
39
|
+
#
|
40
|
+
# client = harvest.clients.find(301)
|
41
|
+
# deactivated_client = harvest.clients.deactivate(client) # returns an updated deactivated client
|
42
|
+
# activated_client = harvest.clients.activate(client) # returns an updated activated client
|
43
|
+
#
|
44
|
+
# @see Harvest::Behavior::Crud
|
45
|
+
# @see Harvest::Behavior::Activatable
|
46
|
+
# @return [Harvest::API::Clients]
|
15
47
|
def clients
|
16
48
|
@clients ||= Harvest::API::Clients.new(credentials)
|
17
49
|
end
|
18
50
|
|
51
|
+
# All API Actions surrounding Client Contacts
|
52
|
+
#
|
53
|
+
# == Examples
|
54
|
+
# harvest.contacts.all() # Returns all contacts in the system
|
55
|
+
# harvest.contacts.all(10) # Returns all contacts for the client id=10 in the system
|
56
|
+
#
|
57
|
+
# harvest.contacts.find(100) # Returns the contact with id = 100
|
58
|
+
#
|
59
|
+
# contact = Harvest::Contact.new(:first_name => 'Jane', :last_name => 'Doe', :client_id => 10)
|
60
|
+
# saved_contact = harvest.contacts.create(contact) # returns a saved version of Harvest::Contact
|
61
|
+
#
|
62
|
+
# contact = harvest.contacts.find(205)
|
63
|
+
# contact.first_name = 'Jilly'
|
64
|
+
# updated_contact = harvest.contacts.update(contact) # returns an updated version of Harvest::Contact
|
65
|
+
#
|
66
|
+
# contact = harvest.contacts.find(205)
|
67
|
+
# harvest.contacts.delete(contact) # returns 205
|
68
|
+
#
|
69
|
+
# @see Harvest::Behavior::Crud
|
70
|
+
# @return [Harvest::API::Contacts]
|
19
71
|
def contacts
|
20
72
|
@contacts ||= Harvest::API::Contacts.new(credentials)
|
21
73
|
end
|
22
74
|
|
75
|
+
# All API Actions surrounding Projects
|
76
|
+
#
|
77
|
+
# == Examples
|
78
|
+
# harvest.projects.all() # Returns all projects in the system
|
79
|
+
#
|
80
|
+
# harvest.projects.find(100) # Returns the project with id = 100
|
81
|
+
#
|
82
|
+
# project = Harvest::Project.new(:name => 'SuprGlu' :client_id => 10)
|
83
|
+
# saved_project = harvest.projects.create(project) # returns a saved version of Harvest::Project
|
84
|
+
#
|
85
|
+
# project = harvest.projects.find(205)
|
86
|
+
# project.name = 'SuprSticky'
|
87
|
+
# updated_project = harvest.projects.update(project) # returns an updated version of Harvest::Project
|
88
|
+
#
|
89
|
+
# project = harvest.project.find(205)
|
90
|
+
# harvest.projects.delete(project) # returns 205
|
91
|
+
#
|
92
|
+
# project = harvest.projects.find(301)
|
93
|
+
# deactivated_project = harvest.projects.deactivate(project) # returns an updated deactivated project
|
94
|
+
# activated_project = harvest.projects.activate(project) # returns an updated activated project
|
95
|
+
#
|
96
|
+
# project = harvest.projects.find(401)
|
97
|
+
# harvest.projects.create_task(project, 'Bottling Glue') # creates and assigns a task to the project
|
98
|
+
#
|
99
|
+
# @see Harvest::Behavior::Crud
|
100
|
+
# @see Harvest::Behavior::Activatable
|
101
|
+
# @return [Harvest::API::Projects]
|
23
102
|
def projects
|
24
103
|
@projects ||= Harvest::API::Projects.new(credentials)
|
25
104
|
end
|
26
105
|
|
106
|
+
# All API Actions surrounding Tasks
|
107
|
+
#
|
108
|
+
# == Examples
|
109
|
+
# harvest.tasks.all() # Returns all tasks in the system
|
110
|
+
#
|
111
|
+
# harvest.tasks.find(100) # Returns the task with id = 100
|
112
|
+
#
|
113
|
+
# task = Harvest::Task.new(:name => 'Server Administration' :default => true)
|
114
|
+
# saved_task = harvest.tasks.create(task) # returns a saved version of Harvest::Task
|
115
|
+
#
|
116
|
+
# task = harvest.tasks.find(205)
|
117
|
+
# task.name = 'Server Administration'
|
118
|
+
# updated_task = harvest.tasks.update(task) # returns an updated version of Harvest::Task
|
119
|
+
#
|
120
|
+
# task = harvest.task.find(205)
|
121
|
+
# harvest.tasks.delete(task) # returns 205
|
122
|
+
#
|
123
|
+
# @see Harvest::Behavior::Crud
|
124
|
+
# @return [Harvest::API::Tasks]
|
27
125
|
def tasks
|
28
126
|
@tasks ||= Harvest::API::Tasks.new(credentials)
|
29
127
|
end
|
30
128
|
|
129
|
+
# All API Actions surrounding Users
|
130
|
+
#
|
131
|
+
# == Examples
|
132
|
+
# harvest.users.all() # Returns all users in the system
|
133
|
+
#
|
134
|
+
# harvest.users.find(100) # Returns the user with id = 100
|
135
|
+
#
|
136
|
+
# user = Harvest::User.new(:first_name => 'Edgar', :last_name => 'Ruth', :email => 'edgar@ruth.com', :password => 'mypassword', :password_confirmation => 'mypassword', :timezone => :cst, :admin => false, :telephone => '444-4444')
|
137
|
+
# saved_user = harvest.users.create(user) # returns a saved version of Harvest::User
|
138
|
+
#
|
139
|
+
# user = harvest.users.find(205)
|
140
|
+
# user.email = 'edgar@ruth.com'
|
141
|
+
# updated_user = harvest.users.update(user) # returns an updated version of Harvest::User
|
142
|
+
#
|
143
|
+
# user = harvest.users.find(205)
|
144
|
+
# harvest.users.delete(user) # returns 205
|
145
|
+
#
|
146
|
+
# user = harvest.users.find(301)
|
147
|
+
# deactivated_user = harvest.users.deactivate(user) # returns an updated deactivated user
|
148
|
+
# activated_user = harvest.users.activate(user) # returns an updated activated user
|
149
|
+
#
|
150
|
+
# user = harvest.users.find(401)
|
151
|
+
# harvest.users.reset_password(user) # will trigger the reset password feature of harvest and shoot the user an email
|
152
|
+
#
|
153
|
+
# @see Harvest::Behavior::Crud
|
154
|
+
# @see Harvest::Behavior::Activatable
|
155
|
+
# @return [Harvest::API::Users]
|
31
156
|
def users
|
32
157
|
@users ||= Harvest::API::Users.new(credentials)
|
33
158
|
end
|
34
159
|
|
160
|
+
# All API Actions surrounding assigning tasks to projects
|
161
|
+
#
|
162
|
+
# == Examples
|
163
|
+
# project = harvest.projects.find(101)
|
164
|
+
# harvest.task_assignments.all(project) # returns all tasks assigned to the project (as Harvest::TaskAssignment)
|
165
|
+
#
|
166
|
+
# project = harvest.projects.find(201)
|
167
|
+
# harvest.task_assignments.find(project, 5) # returns the task assignment with ID 5 that is assigned to the project
|
168
|
+
#
|
169
|
+
# project = harvest.projects.find(301)
|
170
|
+
# task = harvest.tasks.find(100)
|
171
|
+
# assignment = Harvest::TaskAssignment.new(:task_id => task.id, :project_id => project.id)
|
172
|
+
# saved_assignment = harvest.task_assignments.create(assignment) # returns a saved version of the task assignment
|
173
|
+
#
|
174
|
+
# project = harvest.projects.find(401)
|
175
|
+
# assignment = harvest.task_assignments.find(project, 15)
|
176
|
+
# assignment.hourly_rate = 150
|
177
|
+
# updated_assignment = harvest.task_assignments.update(assignment) # returns an updated assignment
|
178
|
+
#
|
179
|
+
# project = harvest.projects.find(501)
|
180
|
+
# assignment = harvest.task_assignments.find(project, 25)
|
181
|
+
# harvest.task_assignments.delete(assignment) # returns 25
|
182
|
+
#
|
183
|
+
# @return [Harvest::API::TaskAssignments]
|
35
184
|
def task_assignments
|
36
185
|
@task_assignments ||= Harvest::API::TaskAssignments.new(credentials)
|
37
186
|
end
|
38
187
|
|
188
|
+
# All API Actions surrounding assigning users to projects
|
189
|
+
#
|
190
|
+
# == Examples
|
191
|
+
# project = harvest.projects.find(101)
|
192
|
+
# harvest.user_assignments.all(project) # returns all users assigned to the project (as Harvest::UserAssignment)
|
193
|
+
#
|
194
|
+
# project = harvest.projects.find(201)
|
195
|
+
# harvest.user_assignments.find(project, 5) # returns the user assignment with ID 5 that is assigned to the project
|
196
|
+
#
|
197
|
+
# project = harvest.projects.find(301)
|
198
|
+
# user = harvest.users.find(100)
|
199
|
+
# assignment = Harvest::UserAssignment.new(:user_id => user.id, :project_id => project.id)
|
200
|
+
# saved_assignment = harvest.user_assignments.create(assignment) # returns a saved version of the user assignment
|
201
|
+
#
|
202
|
+
# project = harvest.projects.find(401)
|
203
|
+
# assignment = harvest.user_assignments.find(project, 15)
|
204
|
+
# assignment.project_manager = true
|
205
|
+
# updated_assignment = harvest.user_assignments.update(assignment) # returns an updated assignment
|
206
|
+
#
|
207
|
+
# project = harvest.projects.find(501)
|
208
|
+
# assignment = harvest.user_assignments.find(project, 25)
|
209
|
+
# harvest.user_assignments.delete(assignment) # returns 25
|
210
|
+
#
|
211
|
+
# @return [Harvest::API::UserAssignments]
|
39
212
|
def user_assignments
|
40
213
|
@user_assignments ||= Harvest::API::UserAssignments.new(credentials)
|
41
214
|
end
|
42
215
|
|
216
|
+
# All API Actions surrounding managing expense categories
|
217
|
+
#
|
218
|
+
# == Examples
|
219
|
+
# harvest.expense_categories.all() # Returns all expense categories in the system
|
220
|
+
#
|
221
|
+
# harvest.expense_categories.find(100) # Returns the expense category with id = 100
|
222
|
+
#
|
223
|
+
# category = Harvest::ExpenseCategory.new(:name => 'Mileage', :unit_price => 0.485)
|
224
|
+
# saved_category = harvest.expense_categories.create(category) # returns a saved version of Harvest::ExpenseCategory
|
225
|
+
#
|
226
|
+
# category = harvest.clients.find(205)
|
227
|
+
# category.name = 'Travel'
|
228
|
+
# updated_category = harvest.expense_categories.update(category) # returns an updated version of Harvest::ExpenseCategory
|
229
|
+
#
|
230
|
+
# category = harvest.expense_categories.find(205)
|
231
|
+
# harvest.expense_categories.delete(category) # returns 205
|
232
|
+
#
|
233
|
+
# @see Harvest::Behavior::Crud
|
234
|
+
# @return [Harvest::API::ExpenseCategories]
|
43
235
|
def expense_categories
|
44
236
|
@expense_categories ||= Harvest::API::ExpenseCategories.new(credentials)
|
45
237
|
end
|
46
238
|
|
239
|
+
# All API Actions surrounding expenses
|
240
|
+
#
|
241
|
+
# == Examples
|
242
|
+
# harvest.expenses.all() # Returns all expenses for the current week
|
243
|
+
# harvest.expenses.all(Time.parse('11/12/2009')) # returns all expenses for the week of 11/12/2009
|
244
|
+
#
|
245
|
+
# harvest.expenses.find(100) # Returns the expense with id = 100
|
47
246
|
def expenses
|
48
247
|
@expenses ||= Harvest::API::Expenses.new(credentials)
|
49
248
|
end
|
data/lib/harvest/base_model.rb
CHANGED
@@ -1,21 +1,58 @@
|
|
1
1
|
module Harvest
|
2
|
+
# The parent class of all Harvest models. Contains useful inheritable methods
|
2
3
|
class BaseModel
|
4
|
+
|
5
|
+
# Initializes the model. You can pass the attributes as a hash in a ActiveRecord-like style
|
6
|
+
#
|
7
|
+
# == Examples
|
8
|
+
# client = Harvest::Client.new(:name => 'John Doe')
|
9
|
+
# client.name # returns 'John Doe'
|
3
10
|
def initialize(attributes = {})
|
4
11
|
self.attributes = attributes
|
5
12
|
end
|
6
|
-
|
13
|
+
|
14
|
+
# Given a hash, sets the corresponding attributes
|
15
|
+
#
|
16
|
+
# == Examples
|
17
|
+
# client = Harvest::Client.new(:name => 'John Doe')
|
18
|
+
# client.name # returns 'John Doe'
|
19
|
+
# client.attributes = {:name => 'Terry Vaughn'}
|
20
|
+
# client.name # returns 'Terry Vaughn'
|
21
|
+
#
|
22
|
+
# @return [void]
|
7
23
|
def attributes=(attributes)
|
8
24
|
attributes.each {|k,v| send("#{k}=", v)}
|
9
25
|
end
|
10
|
-
|
26
|
+
|
27
|
+
# Checks the equality of another model based on the id
|
28
|
+
#
|
29
|
+
# == Examples
|
30
|
+
# client1 = Harvest::Client.new(:id => 1)
|
31
|
+
# client2 = Harvest::Client.new(:id => 1)
|
32
|
+
# client3 = Harvest::Client.new(:id => 2)
|
33
|
+
#
|
34
|
+
# client1 == client2 # returns true
|
35
|
+
# client1 == client3 # returns false
|
36
|
+
#
|
37
|
+
# @return [Boolean]
|
11
38
|
def ==(other)
|
12
39
|
id == other.id
|
13
40
|
end
|
14
|
-
|
41
|
+
|
42
|
+
# Returns the id of the model
|
43
|
+
#
|
44
|
+
# == Examples
|
45
|
+
# client = Harvest::Client.new(:id => 1)
|
46
|
+
# client.to_i # returns 1
|
47
|
+
#
|
48
|
+
# @return [Fixnum]
|
15
49
|
def to_i
|
16
50
|
id
|
17
51
|
end
|
18
|
-
|
52
|
+
|
53
|
+
# Builds the XML of the model. Primarily used to interact with the Harvest API.
|
54
|
+
#
|
55
|
+
# @return [String]
|
19
56
|
def to_xml
|
20
57
|
builder = Builder::XmlMarkup.new
|
21
58
|
builder.tag!(self.class.tag_name) do |c|
|
@@ -26,6 +63,8 @@ module Harvest
|
|
26
63
|
end
|
27
64
|
|
28
65
|
class << self
|
66
|
+
# This sets the API path so the API collections can use them in an agnostic way
|
67
|
+
# @return [void]
|
29
68
|
def api_path(path = nil)
|
30
69
|
@path ||= path
|
31
70
|
end
|
@@ -1,6 +1,12 @@
|
|
1
1
|
module Harvest
|
2
2
|
module Behavior
|
3
|
+
|
4
|
+
# Activate/Deactivate behaviors that can be brought into API collections
|
3
5
|
module Activatable
|
6
|
+
# Deactivates the item. Does nothing if the item is already deactivated
|
7
|
+
#
|
8
|
+
# @param [Harvest::BaseModel] model the model you want to deactivate
|
9
|
+
# @return [Harvest::BaseModel] the deactivated model
|
4
10
|
def deactivate(model)
|
5
11
|
if model.active?
|
6
12
|
request(:post, credentials, "#{api_model.api_path}/#{model.to_i}/toggle")
|
@@ -8,7 +14,11 @@ module Harvest
|
|
8
14
|
end
|
9
15
|
model
|
10
16
|
end
|
11
|
-
|
17
|
+
|
18
|
+
# Activates the item. Does nothing if the item is already activated
|
19
|
+
#
|
20
|
+
# @param [Harvest::BaseModel] model the model you want to activate
|
21
|
+
# @return [Harvest::BaseModel] the activated model
|
12
22
|
def activate(model)
|
13
23
|
if !model.active?
|
14
24
|
request(:post, credentials, "#{api_model.api_path}/#{model.to_i}/toggle")
|
@@ -1,30 +1,56 @@
|
|
1
1
|
module Harvest
|
2
2
|
module Behavior
|
3
3
|
module Crud
|
4
|
+
# Retrieves all items
|
5
|
+
# @return [Array<Harvest::BaseModel>] an array of models depending on where you're calling it from (e.g. [Harvest::Client] from Harvest::Base#clients)
|
4
6
|
def all
|
5
7
|
response = request(:get, credentials, api_model.api_path)
|
6
8
|
api_model.parse(response.body)
|
7
9
|
end
|
8
10
|
|
11
|
+
# Retrieves an item by id
|
12
|
+
# @overload find(id)
|
13
|
+
# @param [Integer] the id of the item you want to retreive
|
14
|
+
# @overload find(id)
|
15
|
+
# @param [String] id the String version of the id
|
16
|
+
# @overload find(model)
|
17
|
+
# @param [Harvest::BaseModel] id you can pass a model and it will return a refreshed version
|
18
|
+
#
|
19
|
+
# @return [Harvest::BaseModel] the model depends on where you're calling it from (e.g. Harvest::Client from Harvest::Base#clients)
|
9
20
|
def find(id)
|
10
21
|
response = request(:get, credentials, "#{api_model.api_path}/#{id}")
|
11
22
|
api_model.parse(response.body, :single => true)
|
12
23
|
end
|
13
24
|
|
25
|
+
# Creates an item
|
26
|
+
# @param [Harvest::BaseModel] model the item you want to create
|
27
|
+
# @return [Harvest::BaseModel] the created model depending on where you're calling it from (e.g. Harvest::Client from Harvest::Base#clients)
|
14
28
|
def create(model)
|
15
29
|
response = request(:post, credentials, "#{api_model.api_path}", :body => model.to_xml)
|
16
30
|
id = response.headers["location"].first.match(/\/.*\/(\d+)/)[1]
|
17
31
|
find(id)
|
18
32
|
end
|
19
33
|
|
34
|
+
# Updates an item
|
35
|
+
# @param [Harvest::BaseModel] model the model you want to update
|
36
|
+
# @return [Harvest::BaseModel] the created model depending on where you're calling it from (e.g. Harvest::Client from Harvest::Base#clients)
|
20
37
|
def update(model)
|
21
38
|
request(:put, credentials, "#{api_model.api_path}/#{model.to_i}", :body => model.to_xml)
|
22
39
|
find(model.id)
|
23
40
|
end
|
24
41
|
|
42
|
+
# Deletes an item
|
43
|
+
# @overload delete(model)
|
44
|
+
# @param [Harvest::BaseModel] model the item you want to delete
|
45
|
+
# @overload delete(id)
|
46
|
+
# @param [Integer] id the id of the item you want to delete
|
47
|
+
# @overload delete(id)
|
48
|
+
# @param [String] id the String version of the id of the item you want to delete
|
49
|
+
#
|
50
|
+
# @return [Integer] the id of the item deleted
|
25
51
|
def delete(model)
|
26
52
|
request(:delete, credentials, "#{api_model.api_path}/#{model.to_i}")
|
27
|
-
model.
|
53
|
+
model.to_i
|
28
54
|
end
|
29
55
|
end
|
30
56
|
end
|
data/lib/harvest/client.rb
CHANGED
@@ -1,4 +1,15 @@
|
|
1
1
|
module Harvest
|
2
|
+
# The model that contains information about a client
|
3
|
+
#
|
4
|
+
# == Fields
|
5
|
+
# [+id+] (READONLY) the id of the client
|
6
|
+
# [+name+] (REQUIRED) the name of the client
|
7
|
+
# [+details+] the details of the client
|
8
|
+
# [+currency+] what type of currency is associated with the client
|
9
|
+
# [+currency_symbol+] what currency symbol is associated with the client
|
10
|
+
# [+active?+] true|false on whether the client is active
|
11
|
+
# [+highrise_id+] (READONLY) the highrise id associated with this client
|
12
|
+
# [+update_at+] (READONLY) the last modification timestamp
|
2
13
|
class Client < BaseModel
|
3
14
|
include HappyMapper
|
4
15
|
|
@@ -12,7 +23,8 @@ module Harvest
|
|
12
23
|
element :currency_symbol, String, :tag => "currency-symbol"
|
13
24
|
element :cache_version, Integer, :tag => "cache-version"
|
14
25
|
element :updated_at, Time, :tag => "updated-at"
|
15
|
-
|
26
|
+
element :highrise_id, Integer, :tag => 'highrise-id'
|
27
|
+
|
16
28
|
alias_method :active?, :active
|
17
29
|
end
|
18
30
|
end
|
data/lib/harvest/contact.rb
CHANGED
@@ -1,4 +1,16 @@
|
|
1
1
|
module Harvest
|
2
|
+
# The model that contains information about a client contact
|
3
|
+
#
|
4
|
+
# == Fields
|
5
|
+
# [+id+] (READONLY) the id of the contact
|
6
|
+
# [+client_id+] (REQUIRED) the id of the client this contact is associated with
|
7
|
+
# [+first_name+] (REQUIRED) the first name of the contact
|
8
|
+
# [+last_name+] (REQUIRED) the last name of the contact
|
9
|
+
# [+email+] the email of the contact
|
10
|
+
# [+title+] the title of the contact
|
11
|
+
# [+phone_office+] the office phone number of the contact
|
12
|
+
# [+phone_moble+] the moble phone number of the contact
|
13
|
+
# [+fax+] the fax number of the contact
|
2
14
|
class Contact < BaseModel
|
3
15
|
include HappyMapper
|
4
16
|
|
@@ -12,5 +24,6 @@ module Harvest
|
|
12
24
|
element :phone_office, String, :tag => "phone-office"
|
13
25
|
element :phone_mobile, String, :tag => "phone-mobile"
|
14
26
|
element :fax, String
|
27
|
+
element :title, String
|
15
28
|
end
|
16
29
|
end
|
data/lib/harvest/project.rb
CHANGED
@@ -1,4 +1,27 @@
|
|
1
1
|
module Harvest
|
2
|
+
|
3
|
+
# The model that contains information about a project
|
4
|
+
#
|
5
|
+
# == Fields
|
6
|
+
# [+id+] (READONLY) the id of the project
|
7
|
+
# [+name+] (REQUIRED) the name of the project
|
8
|
+
# [+client_id+] (REQUIRED) the client id of the project
|
9
|
+
# [+code+] the project code
|
10
|
+
# [+notes+] the project notes
|
11
|
+
# [+active?+] true|false whether the project is active
|
12
|
+
# [+billable?+] true|false where the project is billable
|
13
|
+
# [+budget_by+] how the budget is calculated for the project +project|project_cost|task|person|nil+
|
14
|
+
# [+budget+] what the budget is for the project (based on budget_by)
|
15
|
+
# [+bill_by+] how to bill the project +Tasks|People|Project|nil+
|
16
|
+
# [+hourly_rate+] what the hourly rate for the project is based on +bill_by+
|
17
|
+
# [+notify_when_over_budget?+] whether the project will send notifications when it goes over budget
|
18
|
+
# [+over_budget_notification_percentage+] what percentage of the budget the project has to be before it sends a notification. Based on +notify_when_over_budget?+
|
19
|
+
# [+show_budget_to_all?+] whether the project's budget is shown to employees and contractors
|
20
|
+
# [+basecamp_id+] (READONLY) the id of the basecamp project associated to the project
|
21
|
+
# [+highrise_deal_id+] (READONLY) the id of the highrise deal associated to the project
|
22
|
+
# [+active_task_assignments_count+] (READONLY) the number of active task assignments
|
23
|
+
# [+created_at+] (READONLY) when the project was created
|
24
|
+
# [+updated_at+] (READONLY) when the project was updated
|
2
25
|
class Project < BaseModel
|
3
26
|
include HappyMapper
|
4
27
|
|
@@ -12,11 +35,22 @@ module Harvest
|
|
12
35
|
element :fees, String
|
13
36
|
element :active, Boolean
|
14
37
|
element :billable, Boolean
|
15
|
-
element :budget,
|
16
|
-
element :budget_by,
|
38
|
+
element :budget, Float
|
39
|
+
element :budget_by, String, :tag => 'budget-by'
|
17
40
|
element :hourly_rate, Float, :tag => 'hourly-rate'
|
18
41
|
element :bill_by, String, :tag => 'bill-by'
|
42
|
+
element :created_at, Time, :tag => 'created-at'
|
43
|
+
element :updated_at, Time, :tag => 'updated-at'
|
44
|
+
element :notify_when_over_budget, Boolean, :tag => 'notify-when-over-budget'
|
45
|
+
element :over_budget_notification_percentage, Float, :tag => 'over-budget-notification-percentage'
|
46
|
+
element :show_budget_to_all, Boolean, :tag => 'show-budget-to-all'
|
47
|
+
element :basecamp_id, Integer, :tag => 'basecamp-id'
|
48
|
+
element :highrise_deal_id, Integer, :tag => 'highrise-deal-id'
|
49
|
+
element :active_task_assignments_count, Integer, :tag => 'active-task-assignments-count'
|
19
50
|
|
20
51
|
alias_method :active?, :active
|
52
|
+
alias_method :billable?, :billable
|
53
|
+
alias_method :notify_when_over_budget?, :notify_when_over_budget
|
54
|
+
alias_method :show_budget_to_all?, :show_budget_to_all
|
21
55
|
end
|
22
56
|
end
|
@@ -1,4 +1,13 @@
|
|
1
1
|
module Harvest
|
2
|
+
|
3
|
+
# The model that contains the information about the user's rate limit
|
4
|
+
#
|
5
|
+
# == Fields
|
6
|
+
# [+last_access_at+] The last registered request
|
7
|
+
# [+count+] The current number of requests registered
|
8
|
+
# [+timeframe_limit+] The amount of seconds before a rate limit refresh occurs
|
9
|
+
# [+max_calls+] The number of requests you can make within the +timeframe_limit+
|
10
|
+
# [+lockout_seconds+] If you exceed the rate limit, how long you will be locked out from Harvest
|
2
11
|
class RateLimitStatus < BaseModel
|
3
12
|
include HappyMapper
|
4
13
|
|
@@ -9,6 +18,9 @@ module Harvest
|
|
9
18
|
element :max_calls, Integer, :tag => 'max-calls'
|
10
19
|
element :lockout_seconds, Integer, :tag => 'lockout-seconds'
|
11
20
|
|
21
|
+
# Returns true if the user is over their rate limit
|
22
|
+
# @return [Boolean]
|
23
|
+
# @see http://www.getharvest.com/api
|
12
24
|
def over_limit?
|
13
25
|
count > max_calls
|
14
26
|
end
|
data/lib/harvest/task.rb
CHANGED
@@ -1,4 +1,14 @@
|
|
1
1
|
module Harvest
|
2
|
+
|
3
|
+
# The model that contains information about a task
|
4
|
+
#
|
5
|
+
# == Fields
|
6
|
+
# [+id+] (READONLY) the id of the task
|
7
|
+
# [+name+] (REQUIRED) the name of the task
|
8
|
+
# [+billable+] whether the task is billable by default
|
9
|
+
# [+deactivated+] whether the task is deactivated
|
10
|
+
# [+hourly_rate+] what the default hourly rate for the task is
|
11
|
+
# [+default?+] whether to add this task to new projects by default
|
2
12
|
class Task < BaseModel
|
3
13
|
include HappyMapper
|
4
14
|
|
data/lib/harvest/timezones.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
|
-
# shamelessly ripped from Rails: http://github.com/rails/rails/blob/master/activesupport/lib/active_support/values/time_zone.rb
|
2
|
-
|
3
1
|
module Harvest
|
2
|
+
# shamelessly ripped from Rails: http://github.com/rails/rails/blob/master/activesupport/lib/active_support/values/time_zone.rb
|
4
3
|
module Timezones
|
5
4
|
MAPPING = {
|
6
5
|
"pacific/midway" => "International Date Line West",
|
data/lib/harvest/user.rb
CHANGED
@@ -1,7 +1,24 @@
|
|
1
1
|
module Harvest
|
2
|
+
|
3
|
+
# The model that contains information about a task
|
4
|
+
#
|
5
|
+
# == Fields
|
6
|
+
# [+id+] (READONLY) the id of the user
|
7
|
+
# [+email+] the email of the user
|
8
|
+
# [+first_name+] the first name for the user
|
9
|
+
# [+last_name+] the last name for the user
|
10
|
+
# [+telephone+] the telephone for the user
|
11
|
+
# [+department] the department for the user
|
12
|
+
# [+password|password_confirmation+] the password for the user (only used on create.)
|
13
|
+
# [+has_access_to_all_future_projects+] whether the user should be added to future projects by default
|
14
|
+
# [+hourly_rate+] what the default hourly rate for the user is
|
15
|
+
# [+admin?+] whether the user is an admin
|
16
|
+
# [+contractor?+] whether the user is a contractor
|
17
|
+
# [+contractor?+] whether the user is a contractor
|
18
|
+
# [+timezone+] the timezone for the user.
|
2
19
|
class User < BaseModel
|
3
20
|
include HappyMapper
|
4
|
-
|
21
|
+
|
5
22
|
api_path '/people'
|
6
23
|
element :id, Integer
|
7
24
|
element :email, String
|
@@ -13,14 +30,23 @@ module Harvest
|
|
13
30
|
element :admin, Boolean, :tag => 'is-admin'
|
14
31
|
element :contractor, Boolean, :tag => 'is-contractor'
|
15
32
|
element :telephone, String
|
33
|
+
element :department, String
|
16
34
|
element :timezone, String
|
17
35
|
element :password, String
|
18
36
|
element :password_confirmation, String, :tag => 'password-confirmation'
|
19
|
-
|
37
|
+
|
20
38
|
alias_method :active?, :active
|
21
39
|
alias_method :admin?, :admin
|
22
40
|
alias_method :contractor?, :contractor
|
23
|
-
|
41
|
+
|
42
|
+
# Sets the timezone for the user. This can be done in a variety of ways.
|
43
|
+
#
|
44
|
+
# == Examples
|
45
|
+
# user.timezone = :cst # the easiest way. CST, EST, MST, and PST are supported
|
46
|
+
#
|
47
|
+
# user.timezone = 'america/chicago' # a little more verbose
|
48
|
+
#
|
49
|
+
# user.timezone = 'Central Time (US & Canada)' # the most explicit way
|
24
50
|
def timezone=(timezone)
|
25
51
|
tz = timezone.to_s.downcase
|
26
52
|
case tz
|
data/lib/harvested.rb
CHANGED
@@ -16,13 +16,44 @@ require 'harvest/base'
|
|
16
16
|
%w(base account clients contacts projects tasks users task_assignments user_assignments expense_categories expenses time reports).each {|a| require "harvest/api/#{a}"}
|
17
17
|
|
18
18
|
module Harvest
|
19
|
-
VERSION = "0.3.
|
19
|
+
VERSION = "0.3.1".freeze
|
20
20
|
|
21
21
|
class << self
|
22
|
+
|
23
|
+
# Creates a standard client that will raise all errors it encounters
|
24
|
+
#
|
25
|
+
# == Options
|
26
|
+
# * +:ssl+ - Whether or not to use SSL when connecting to Harvest. This is dependent on whether your account supports it. Set to +true+ by default
|
27
|
+
# == Examples
|
28
|
+
# Harvest.client('mysubdomain', 'myusername', 'mypassword', :ssl => false)
|
29
|
+
#
|
30
|
+
# @return [Harvest::Base]
|
22
31
|
def client(subdomain, username, password, options = {})
|
23
32
|
Harvest::Base.new(subdomain, username, password, options)
|
24
33
|
end
|
25
34
|
|
35
|
+
# Creates a hardy client that will retry common HTTP errors it encounters and sleep() if it determines it is over your rate limit
|
36
|
+
#
|
37
|
+
# == Options
|
38
|
+
# * +:ssl+ - Whether or not to use SSL when connecting to Harvest. This is dependent on whether your account supports it. Set to +true+ by default
|
39
|
+
# * +:retry+ - How many times the hardy client should retry errors. Set to +5+ by default.
|
40
|
+
#
|
41
|
+
# == Examples
|
42
|
+
# Harvest.hardy_client('mysubdomain', 'myusername', 'mypassword', :ssl => true, :retry => 3)
|
43
|
+
#
|
44
|
+
# == Errors
|
45
|
+
# The hardy client will retry the following errors
|
46
|
+
# * Harvest::Unavailable
|
47
|
+
# * Harvest::InformHarvest
|
48
|
+
# * Net::HTTPError
|
49
|
+
# * Net::HTTPFatalError
|
50
|
+
# * Errno::ECONNRESET
|
51
|
+
#
|
52
|
+
# == Rate Limits
|
53
|
+
# The hardy client will make as many requests as it can until it detects it has gone over the rate limit. Then it will +sleep()+ for the how ever long it takes for the limit to reset. You can find more information about the Rate Limiting at http://www.getharvest.com/api
|
54
|
+
#
|
55
|
+
# @return [Harvest::HardyClient] a Harvest::Base wrapped in a Harvest::HardyClient
|
56
|
+
# @see Harvest::Base
|
26
57
|
def hardy_client(subdomain, username, password, options = {})
|
27
58
|
retries = options.delete(:retry)
|
28
59
|
Harvest::HardyClient.new(client(subdomain, username, password, options), (retries || 5))
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: harvested
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 17
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 0
|
7
8
|
- 3
|
8
|
-
-
|
9
|
-
version: 0.3.
|
9
|
+
- 1
|
10
|
+
version: 0.3.1
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Zach Moazeni
|
@@ -14,16 +15,18 @@ autorequire:
|
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date: 2010-
|
18
|
+
date: 2010-10-14 00:00:00 -04:00
|
18
19
|
default_executable:
|
19
20
|
dependencies:
|
20
21
|
- !ruby/object:Gem::Dependency
|
21
22
|
name: rspec
|
22
23
|
prerelease: false
|
23
24
|
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
24
26
|
requirements:
|
25
27
|
- - ">="
|
26
28
|
- !ruby/object:Gem::Version
|
29
|
+
hash: 13
|
27
30
|
segments:
|
28
31
|
- 1
|
29
32
|
- 2
|
@@ -35,9 +38,11 @@ dependencies:
|
|
35
38
|
name: cucumber
|
36
39
|
prerelease: false
|
37
40
|
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
38
42
|
requirements:
|
39
43
|
- - ">="
|
40
44
|
- !ruby/object:Gem::Version
|
45
|
+
hash: 3
|
41
46
|
segments:
|
42
47
|
- 0
|
43
48
|
version: "0"
|
@@ -47,9 +52,11 @@ dependencies:
|
|
47
52
|
name: ruby-debug
|
48
53
|
prerelease: false
|
49
54
|
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
50
56
|
requirements:
|
51
57
|
- - ">="
|
52
58
|
- !ruby/object:Gem::Version
|
59
|
+
hash: 3
|
53
60
|
segments:
|
54
61
|
- 0
|
55
62
|
version: "0"
|
@@ -59,9 +66,11 @@ dependencies:
|
|
59
66
|
name: fakeweb
|
60
67
|
prerelease: false
|
61
68
|
requirement: &id004 !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
62
70
|
requirements:
|
63
71
|
- - ">="
|
64
72
|
- !ruby/object:Gem::Version
|
73
|
+
hash: 3
|
65
74
|
segments:
|
66
75
|
- 0
|
67
76
|
version: "0"
|
@@ -71,9 +80,11 @@ dependencies:
|
|
71
80
|
name: httparty
|
72
81
|
prerelease: false
|
73
82
|
requirement: &id005 !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
74
84
|
requirements:
|
75
85
|
- - ">="
|
76
86
|
- !ruby/object:Gem::Version
|
87
|
+
hash: 3
|
77
88
|
segments:
|
78
89
|
- 0
|
79
90
|
version: "0"
|
@@ -83,9 +94,11 @@ dependencies:
|
|
83
94
|
name: happymapper
|
84
95
|
prerelease: false
|
85
96
|
requirement: &id006 !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
86
98
|
requirements:
|
87
99
|
- - ">="
|
88
100
|
- !ruby/object:Gem::Version
|
101
|
+
hash: 3
|
89
102
|
segments:
|
90
103
|
- 0
|
91
104
|
version: "0"
|
@@ -95,15 +108,17 @@ dependencies:
|
|
95
108
|
name: builder
|
96
109
|
prerelease: false
|
97
110
|
requirement: &id007 !ruby/object:Gem::Requirement
|
111
|
+
none: false
|
98
112
|
requirements:
|
99
113
|
- - ">="
|
100
114
|
- !ruby/object:Gem::Version
|
115
|
+
hash: 3
|
101
116
|
segments:
|
102
117
|
- 0
|
103
118
|
version: "0"
|
104
119
|
type: :runtime
|
105
120
|
version_requirements: *id007
|
106
|
-
description: Harvested wraps the Harvest API concisely without the use of Rails dependencies. More information about the Harvest API can be found
|
121
|
+
description: Harvested wraps the Harvest API concisely without the use of Rails dependencies. More information about the Harvest API can be found on their website (http://www.getharvest.com/api). For support hit up the Mailing List (http://groups.google.com/group/harvested)
|
107
122
|
email: zach.moazeni@gmail.com
|
108
123
|
executables: []
|
109
124
|
|
@@ -112,6 +127,7 @@ extensions: []
|
|
112
127
|
extra_rdoc_files:
|
113
128
|
- README.md
|
114
129
|
files:
|
130
|
+
- .document
|
115
131
|
- .gitignore
|
116
132
|
- HISTORY
|
117
133
|
- MIT-LICENSE
|
@@ -156,6 +172,7 @@ files:
|
|
156
172
|
- features/time_tracking.feature
|
157
173
|
- features/user_assignments.feature
|
158
174
|
- features/users.feature
|
175
|
+
- harvested.gemspec
|
159
176
|
- lib/harvest/api/account.rb
|
160
177
|
- lib/harvest/api/base.rb
|
161
178
|
- lib/harvest/api/clients.rb
|
@@ -208,23 +225,27 @@ rdoc_options:
|
|
208
225
|
require_paths:
|
209
226
|
- lib
|
210
227
|
required_ruby_version: !ruby/object:Gem::Requirement
|
228
|
+
none: false
|
211
229
|
requirements:
|
212
230
|
- - ">="
|
213
231
|
- !ruby/object:Gem::Version
|
232
|
+
hash: 3
|
214
233
|
segments:
|
215
234
|
- 0
|
216
235
|
version: "0"
|
217
236
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
237
|
+
none: false
|
218
238
|
requirements:
|
219
239
|
- - ">="
|
220
240
|
- !ruby/object:Gem::Version
|
241
|
+
hash: 3
|
221
242
|
segments:
|
222
243
|
- 0
|
223
244
|
version: "0"
|
224
245
|
requirements: []
|
225
246
|
|
226
247
|
rubyforge_project:
|
227
|
-
rubygems_version: 1.3.
|
248
|
+
rubygems_version: 1.3.7
|
228
249
|
signing_key:
|
229
250
|
specification_version: 3
|
230
251
|
summary: A Ruby Wrapper for the Harvest API http://www.getharvest.com/
|