harvest-ruby 0.1.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.
@@ -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
+