harvest-ruby 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,6 @@
1
+ === 0.1.1 / 2008-04-15
2
+
3
+ * 1 major enhancement
4
+
5
+ * Birthday!
6
+
@@ -0,0 +1,7 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ lib/harvest.rb
6
+ lib/harvest_entry.rb
7
+ spec/harvest_spec.rb
@@ -0,0 +1,75 @@
1
+ = harvest-ruby
2
+
3
+ http://github.com/bricooke/harvest-ruby
4
+
5
+ == DESCRIPTION:
6
+
7
+ Ruby wrapper around the Harvest API
8
+ http://www.getharvest.com/api
9
+
10
+ == SYNOPSIS:
11
+
12
+ harvest = Harvest.new("company.harvestapp.com", "email@example.com", "password")
13
+
14
+ harvest.users[0].last_name
15
+ => "email@example.com"
16
+
17
+ harvest.users[0].created_at
18
+ => "Fri May 11 15:00:08 EDT 2007"
19
+
20
+ harvest.projects[0].name
21
+ => "Best Project Ever"
22
+
23
+ harvest.tasks[0].name
24
+ => "Project Management"
25
+
26
+ harvest.tasks(59273).name
27
+ => "Project Management"
28
+
29
+ Fetch all entries and expenses for a project:
30
+ harvest.report(5.years.ago, Time.now, :project_id => 6)
31
+
32
+ or to filter on person:
33
+ harvest.report(5.years.ago, Time.now, :person_id => 555)
34
+
35
+ or to filter on both:
36
+ harvest.report(5.years.ago, Time.now, :person_id => 555, :project_id => 6)
37
+
38
+
39
+ == REQUIREMENTS:
40
+
41
+ activesupport xml-simple
42
+
43
+ == INSTALL:
44
+
45
+ sudo gem install harvest-ruby
46
+
47
+ == TODO:
48
+
49
+ * Specs
50
+ * Document the public API for rdocs
51
+
52
+ == LICENSE:
53
+
54
+ (The MIT License)
55
+
56
+ Copyright (c) 2008 Brian Cooke
57
+
58
+ Permission is hereby granted, free of charge, to any person obtaining
59
+ a copy of this software and associated documentation files (the
60
+ 'Software'), to deal in the Software without restriction, including
61
+ without limitation the rights to use, copy, modify, merge, publish,
62
+ distribute, sublicense, and/or sell copies of the Software, and to
63
+ permit persons to whom the Software is furnished to do so, subject to
64
+ the following conditions:
65
+
66
+ The above copyright notice and this permission notice shall be
67
+ included in all copies or substantial portions of the Software.
68
+
69
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
70
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
71
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
72
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
73
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
74
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
75
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,22 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require './lib/harvest.rb'
6
+ require 'spec/rake/spectask'
7
+
8
+
9
+ Hoe.new('harvest-ruby', Harvest::VERSION) do |p|
10
+ p.rubyforge_name = 'harvest-ruby' # if different than lowercase project name
11
+ p.developer('Brian Cooke', 'bcooke@roobasoft.com')
12
+ end
13
+
14
+
15
+ desc "Run specifications"
16
+ Spec::Rake::SpecTask.new('spec') do |t|
17
+ t.spec_opts = ["--format", "specdoc", "--colour"]
18
+ t.spec_files = './spec/**/*_spec.rb'
19
+ end
20
+
21
+
22
+ # vim: syntax=Ruby
@@ -0,0 +1,93 @@
1
+ require 'rubygems'
2
+ require 'net/http'
3
+ require 'xmlsimple'
4
+ require 'activesupport'
5
+ require File.dirname(__FILE__) + '/harvest_entry'
6
+
7
+ class Harvest
8
+ VERSION = '0.1.1'
9
+ @tasks = []
10
+ @expenses = []
11
+
12
+ def initialize(domain, email, password)
13
+ @domain = domain
14
+ @email = email
15
+ @password = password
16
+ end
17
+
18
+ # returns all the users created in this Harvest account
19
+ # see http://www.getharvest.com/api/people for data available
20
+ def users(params={})
21
+ request("/people", params).users
22
+ end
23
+
24
+ # returns all the projects created in this Harvest account
25
+ # see http://www.getharvest.com/api/projects for data available
26
+ def projects
27
+ request("/projects").projects
28
+ end
29
+
30
+ # returns all tasks and expenses for a given project or person
31
+ # in the given time period. The expenses and entries are
32
+ # mixed in the same array returned. Use .expense? to determine
33
+ # what type it is. Reference http://www.getharvest.com/api/reporting
34
+ # to get data available for each type
35
+ def report(from, to, project_and_or_person_id)
36
+ entries = []
37
+
38
+ %w(entries expenses).each do |type|
39
+ if project_and_or_person_id.has_key?(:project_id)
40
+ t = request("/projects/#{project_and_or_person_id[:project_id]}/#{type}?from=#{from.strftime("%Y%m%d")}&to=#{to.strftime("%Y%m%d")}")
41
+ temp = (type == "entries" ? t.day_entries : t.expenses)
42
+ entries += temp.select {|entry| !(entries.any? {|match| match.id == entry.id })}
43
+ end
44
+
45
+ if project_and_or_person_id.has_key?(:person_id)
46
+ t = request("/people/#{project_and_or_person_id[:person_id]}/#{type}?from=#{from.strftime("%Y%m%d")}&to=#{to.strftime("%Y%m%d")}")
47
+ temp = (type == "entries" ? t.day_entries : t.expenses)
48
+ entries += temp.select {|entry| !(entries.any? {|match| match.id == entry.id })}
49
+ end
50
+ end
51
+ entries
52
+ end
53
+
54
+ # returns all tasks and caches the results
55
+ # see http://www.getharvest.com/api/tasks
56
+ def tasks
57
+ @tasks ||= request("/tasks").tasks
58
+ end
59
+
60
+ # return a specific task
61
+ # see http://www.getharvest.com/api/tasks
62
+ def task(id)
63
+ self.tasks.find {|t| t.id.to_i == id.to_i}
64
+ end
65
+
66
+ # returns all expense categories and caches the results
67
+ # see http://www.getharvest.com/api/expenses
68
+ def expense_categories
69
+ @expense_categories ||= request("/expense_categories").expense_categories
70
+ end
71
+
72
+ # returns a specific expense_category
73
+ # see http://www.getharvest.com/api/expenses
74
+ def expense_category(id)
75
+ self.expense_categories.find {|e| e.id.to_i == id.to_i}
76
+ end
77
+
78
+ private
79
+ def request(path, params={})
80
+ request = Net::HTTP::Get.new(path, { "Accept" => "application/xml"})
81
+ request.basic_auth(@email, @password)
82
+ request.content_type = "application/xml"
83
+ ret = nil
84
+ Net::HTTP.new(@domain).start {|http|
85
+ response = http.request(request)
86
+ if response.class == Net::HTTPServiceUnavailable
87
+ raise ArgumentError, "API Limit exceeded. Retry after #{response["Retry-After"].to_i/60} minutes."
88
+ end
89
+ ret = HarvestEntry.new(XmlSimple.xml_in(response.body))
90
+ }
91
+ ret
92
+ end
93
+ end
@@ -0,0 +1,46 @@
1
+ class HarvestEntry
2
+ def initialize(parsed)
3
+ @hash = parsed
4
+ end
5
+
6
+ def id
7
+ self.method_missing(:id)
8
+ end
9
+
10
+ def empty?
11
+ if @hash["type"] == "array" && @hash["content"] == "\n"
12
+ true
13
+ else
14
+ false
15
+ end
16
+ end
17
+
18
+ def expense?
19
+ begin
20
+ self.expense_category_id
21
+ true
22
+ rescue
23
+ false
24
+ end
25
+ end
26
+
27
+ def method_missing(method, *args)
28
+ method = method.to_s.gsub("_", "-").to_sym
29
+
30
+ if @hash.has_key?(method.to_s.singularize)
31
+ entry = @hash[method.to_s.singularize]
32
+ if method.to_s.pluralize == method.to_s && entry.class == Array
33
+ return entry.collect {|e| HarvestEntry.new(e)}
34
+ else
35
+ return entry[0] unless entry[0].class == Hash && entry[0].has_key?("content")
36
+ return entry[0]["content"]
37
+ end
38
+ elsif @hash.has_key?(method.to_s)
39
+ entry = @hash[method.to_s]
40
+ return entry[0] unless entry[0].class == Hash && entry[0].has_key?("content")
41
+ return entry[0]["content"]
42
+ else
43
+ super
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,6 @@
1
+ require './lib/harvest'
2
+
3
+ describe "Harvest" do
4
+ it "should do some things"
5
+ end
6
+
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: harvest-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Brian Cooke
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-04-15 00:00:00 -06:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hoe
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.5.1
23
+ version:
24
+ description: Ruby wrapper around the Harvest API http://www.getharvest.com/api
25
+ email:
26
+ - bcooke@roobasoft.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - History.txt
33
+ - Manifest.txt
34
+ - README.txt
35
+ files:
36
+ - History.txt
37
+ - Manifest.txt
38
+ - README.txt
39
+ - Rakefile
40
+ - lib/harvest.rb
41
+ - lib/harvest_entry.rb
42
+ - spec/harvest_spec.rb
43
+ has_rdoc: true
44
+ homepage: http://github.com/bricooke/harvest-ruby
45
+ post_install_message:
46
+ rdoc_options:
47
+ - --main
48
+ - README.txt
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: "0"
62
+ version:
63
+ requirements: []
64
+
65
+ rubyforge_project: harvest-ruby
66
+ rubygems_version: 1.1.1
67
+ signing_key:
68
+ specification_version: 2
69
+ summary: Ruby wrapper around the Harvest API http://www.getharvest.com/api
70
+ test_files: []
71
+