harvested 0.3.0 → 0.3.1

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/.document ADDED
@@ -0,0 +1,3 @@
1
+ README.md
2
+ lib/**/*.rb
3
+ MIT-LICENSE
data/.gitignore CHANGED
@@ -18,6 +18,8 @@ coverage
18
18
  rdoc
19
19
  pkg
20
20
  spec.opts
21
+ doc
22
+ .yardoc
21
23
 
22
24
  ## PROJECT::SPECIFIC
23
25
  features/support/harvest_credentials.yml
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', :ssl => false) # you can enable SSL if your account supports it
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', :ssl => false)
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 at http://www.getharvest.com/api}
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.0
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, :ssl => false)
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:"
@@ -6,7 +6,7 @@
6
6
  # username = 'yourusername'
7
7
  # password = 'yourpassword'
8
8
  #
9
- # api = Harvest.hardy_client(subdomain, username, password, :ssl => false)
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, :ssl => false)
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, :ssl => false)
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
+
@@ -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)
@@ -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)
@@ -2,7 +2,7 @@ module Harvest
2
2
  module API
3
3
  class Clients < Base
4
4
  api_model Harvest::Client
5
-
5
+
6
6
  include Harvest::Behavior::Crud
7
7
  include Harvest::Behavior::Activatable
8
8
  end
@@ -11,7 +11,7 @@ module Harvest
11
11
  else
12
12
  request(:get, credentials, "/contacts")
13
13
  end
14
-
14
+
15
15
  api_model.parse(response.body)
16
16
  end
17
17
  end
@@ -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'})
@@ -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
@@ -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.id
53
+ model.to_i
28
54
  end
29
55
  end
30
56
  end
@@ -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
@@ -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
@@ -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, String
16
- element :budget_by, Float, :tag => '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
 
@@ -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.0".freeze
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
@@ -1,7 +1,7 @@
1
1
  $LOAD_PATH.unshift(File.dirname(__FILE__))
2
2
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
3
  require "rubygems"
4
- require 'harvest'
4
+ require 'harvested'
5
5
  require 'spec'
6
6
  require 'spec/autorun'
7
7
 
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
- - 0
9
- version: 0.3.0
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-04-11 00:00:00 -04:00
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 at http://www.getharvest.com/api
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.6
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/