kmayer-highrise 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.mkdn +69 -0
  3. data/Rakefile +68 -0
  4. data/lib/highrise/base.rb +4 -0
  5. data/lib/highrise/comment.rb +4 -0
  6. data/lib/highrise/company.rb +14 -0
  7. data/lib/highrise/curlhelper.rb +27 -0
  8. data/lib/highrise/curly.rb +96 -0
  9. data/lib/highrise/email.rb +9 -0
  10. data/lib/highrise/group.rb +4 -0
  11. data/lib/highrise/kase.rb +8 -0
  12. data/lib/highrise/membership.rb +4 -0
  13. data/lib/highrise/note.rb +9 -0
  14. data/lib/highrise/pagination.rb +29 -0
  15. data/lib/highrise/person.rb +20 -0
  16. data/lib/highrise/subject.rb +15 -0
  17. data/lib/highrise/tag.rb +7 -0
  18. data/lib/highrise/taggable.rb +29 -0
  19. data/lib/highrise/task.rb +11 -0
  20. data/lib/highrise/user.rb +7 -0
  21. data/lib/highrise/version.rb +9 -0
  22. data/lib/highrise.rb +26 -0
  23. data/spec/highrise/base_spec.rb +13 -0
  24. data/spec/highrise/comment_spec.rb +14 -0
  25. data/spec/highrise/companies/16883216.html +1723 -0
  26. data/spec/highrise/company_spec.rb +70 -0
  27. data/spec/highrise/curlhelper_spec.rb +35 -0
  28. data/spec/highrise/curly_spec.rb +36 -0
  29. data/spec/highrise/email_spec.rb +25 -0
  30. data/spec/highrise/group_spec.rb +14 -0
  31. data/spec/highrise/kase_spec.rb +25 -0
  32. data/spec/highrise/membership_spec.rb +14 -0
  33. data/spec/highrise/note_spec.rb +23 -0
  34. data/spec/highrise/pagination_spec.rb +7 -0
  35. data/spec/highrise/people/16887003.html +1733 -0
  36. data/spec/highrise/person_spec.rb +83 -0
  37. data/spec/highrise/subject_spec.rb +43 -0
  38. data/spec/highrise/tag_spec.rb +21 -0
  39. data/spec/highrise/task_spec.rb +23 -0
  40. data/spec/highrise/user_spec.rb +30 -0
  41. data/spec.opts +7 -0
  42. data/spec_helper.rb +20 -0
  43. metadata +99 -0
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 [name of plugin creator]
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.mkdn ADDED
@@ -0,0 +1,69 @@
1
+ # Highrise
2
+
3
+ ## What
4
+
5
+ [Highrise][h] is a gem that provides an easy way to use the [Highrise API][api].
6
+
7
+ ## Installing
8
+
9
+ gem install kmayer-highrise
10
+
11
+ ## Dependencies
12
+
13
+ * ActiveResorce >= 2.2.2
14
+ * ActiveSupport >= 2.1
15
+ * Curb
16
+ * Hpricot
17
+
18
+ ## Configure your key.
19
+
20
+ require 'rubygems'
21
+ require 'highrise'
22
+ Highrise::Base.site = 'http://your_api:login@your_site.highrisehq.com/'
23
+
24
+ ## Usage
25
+
26
+ This gem provides a set of classes to access all available informations on [Highrise][h].
27
+
28
+ These are the list of classes:
29
+
30
+ Comment, Company, Email, Group, Case, Membership, Note, Person, Subject, Tag, Task and User.
31
+
32
+ All these classes are inherited of ActiveResouce::Base. For more informations see the ActiveResouce documentation.
33
+
34
+ ## License
35
+
36
+ This code is free to be used under the terms of the [MIT license][mit].
37
+
38
+ ## Contact
39
+
40
+ Comments are welcome. Send your feedback through [this page][co]
41
+
42
+ ## Authors
43
+
44
+ [Marcos Tapajós][mt]
45
+
46
+ ## Contributors
47
+
48
+ * [Nicolas Bianco][nb]
49
+ * [Luis Gustavo][lg]
50
+ * [Thiago Lelis][tl]
51
+ * [Ken Mayer][km]
52
+
53
+ ## Shameless advertisement
54
+
55
+ (From the original author) This plugin is brought to you by [Improve It][ii].
56
+
57
+ [![Improve It][logo]][ii]
58
+
59
+ [logo]:http://www.improveit.com.br/images/logo/logo_improve_it_screen.gif "Improve It"
60
+ [mt]: http://www.improveit.com.br/en/company/tapajos
61
+ [ii]: http://www.improveit.com.br/en
62
+ [co]: http://github.com/kmayer
63
+ [mit]:http://www.opensource.org/licenses/mit-license.php
64
+ [h]: http://www.highrisehq.com/
65
+ [km]: http://github.com/kmayer
66
+ [lg]: http://github.com/luisbebop
67
+ [nb]: http://github.com/slainer86
68
+ [tl]: http://github.com/ThiagoLelis
69
+ [api]: http://developer.37signals.com/highrise
data/Rakefile ADDED
@@ -0,0 +1,68 @@
1
+ require 'rake'
2
+ require 'rubygems'
3
+ require 'rake/rdoctask'
4
+ require 'rake/testtask'
5
+ require 'spec/rake/spectask'
6
+ require "date"
7
+ require "fileutils"
8
+ require "rubygems"
9
+ require "rake/gempackagetask"
10
+
11
+ require File.join(File.dirname(__FILE__), 'lib', 'highrise', 'version')
12
+
13
+ VERSION=Highrise::VERSION::STRING
14
+
15
+ highrise_gemspec = Gem::Specification.new do |s|
16
+ s.name = "highrise"
17
+ s.version = VERSION
18
+ s.platform = Gem::Platform::RUBY
19
+ s.has_rdoc = true
20
+ s.extra_rdoc_files = ["README.mkdn"]
21
+ s.summary = "Ruby wrapper around Highrise API"
22
+ s.description = %{
23
+ Based on the original API module from DHH, http://developer.37signals.com/highrise/, this
24
+ gem is a cleaned up, tested version of the same. Contributors have added support for tags
25
+ which are not supported by the API directly
26
+
27
+ Configure by adding the following:
28
+
29
+ require 'rubygems'
30
+ require 'highrise'
31
+ Highrise::Base.site = 'http://your_api:login@your_site.highrisehq.com/'
32
+ }
33
+ s.authors = ["Marcos Tapajós", "Ken Mayer"]
34
+ s.email = "kmayer@bitwrangler.com"
35
+ s.homepage = "http://github.com/kmayer/highrise"
36
+ s.require_path = "lib"
37
+ s.autorequire = "highrise"
38
+ s.files = %w(README.mkdn Rakefile MIT-LICENSE spec.opts spec_helper.rb) + Dir.glob("{bin,lib,spec}/**/*")
39
+ end
40
+
41
+ Rake::GemPackageTask.new(highrise_gemspec) do |pkg|
42
+ pkg.gem_spec = highrise_gemspec
43
+ end
44
+
45
+ namespace :gem do
46
+ namespace :spec do
47
+ desc "Update highrise.gemspec"
48
+ task :generate do
49
+ File.open("highrise.gemspec", "w") do |f|
50
+ f.puts(highrise_gemspec.to_ruby)
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ desc "Instal gem"
57
+ task :install => :package do
58
+ sh %{sudo gem install --local pkg/highrise-#{VERSION}}
59
+ end
60
+
61
+ desc 'Default: run unit tests.'
62
+ task :default => :spec
63
+
64
+ desc "Run all specs"
65
+ Spec::Rake::SpecTask.new do |t|
66
+ t.spec_files = FileList['spec/**/*_spec.rb']
67
+ t.spec_opts = ['--options', 'spec.opts']
68
+ end
@@ -0,0 +1,4 @@
1
+ module Highrise
2
+ class Base < ActiveResource::Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Highrise
2
+ class Comment < Base
3
+ end
4
+ end
@@ -0,0 +1,14 @@
1
+ module Highrise
2
+ class Company < Subject
3
+ include Pagination
4
+ include Taggable
5
+
6
+ def self.find_all_across_pages_since(time)
7
+ find_all_across_pages(:params => { :since => time.utc.to_s(:db).gsub(/[^\d]/, '') })
8
+ end
9
+
10
+ def people
11
+ Person.find(:all, :from => "/companies/#{id}/people.xml")
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,27 @@
1
+ module Highrise
2
+ class CurlHelper
3
+
4
+ # thanks mislav ;) http://github.com/mislav/curly/tree/master
5
+
6
+ def initialize
7
+ @curly = Curly.new
8
+ @curly.http_auth_types = Curl::CURLAUTH_BASIC
9
+ end
10
+
11
+ def get_userpwd_from_url(url)
12
+ userpwd_from_url_regex = /(http|https):\/\/((?:[\w\.\-\+%!$&'\(\)*\+,;=]+:)*[\w\.\-\+%!$&'\(\)*\+,;=]+)?/
13
+ match = userpwd_from_url_regex.match(url)
14
+ match.captures[1] unless match.nil?
15
+ end
16
+
17
+ def get_document(url)
18
+ @curly.userpwd = get_userpwd_from_url(url)
19
+ @curly.get(url).doc
20
+ end
21
+
22
+ def get_document_from_id(collection_name_and_id)
23
+ get_document("#{Highrise::Base.site.to_s}#{collection_name_and_id}")
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,96 @@
1
+ class Curly < ActiveSupport::BasicObject
2
+ attr_reader :uri
3
+
4
+ def initialize(uri = nil)
5
+ @curl = Curl::Easy.new
6
+ self.uri = uri
7
+ self.follow_location = true
8
+ yield self if block_given?
9
+ end
10
+
11
+ def uri=(obj)
12
+ case obj
13
+ when String
14
+ unless @uri
15
+ @uri = URI.parse(obj)
16
+ else
17
+ @uri += obj
18
+ end
19
+ when URI::HTTP
20
+ @uri = obj
21
+ when nil
22
+ return
23
+ else
24
+ raise "unsupported URI type (#{obj.class.name} given)"
25
+ end
26
+
27
+ self.url = @uri.to_s
28
+ end
29
+
30
+ def method_missing(method, *args, &block)
31
+ @curl.send(method, *args, &block)
32
+ end
33
+
34
+ def cookiejar=(filename)
35
+ self.enable_cookies = true
36
+ @curl.cookiejar = filename
37
+ end
38
+
39
+ def get(uri = nil)
40
+ self.uri = uri
41
+ http_get
42
+ raise "expected 2xx, got #{response_code} (GET #{url})" unless success?
43
+ self
44
+ end
45
+
46
+ def success?
47
+ response_code >= 200 and response_code < 300
48
+ end
49
+
50
+ def doc
51
+ Hpricot body_unicode
52
+ end
53
+
54
+ def encoding
55
+ return @encoding unless @encoding == false
56
+ @encoding = if body_str =~ /;\s*charset=([\w-]+)\s*['"]/
57
+ $1.downcase
58
+ else
59
+ false
60
+ end
61
+ end
62
+
63
+ def post(params)
64
+ fields = params.map do |key, value|
65
+ Curl::PostField.content(key.to_s, value.to_s)
66
+ end
67
+ http_post *fields
68
+ end
69
+
70
+ class Form
71
+ def initialize(element)
72
+ @node = element
73
+ end
74
+
75
+ def elements
76
+ @node.search('input, button, select, textarea')
77
+ end
78
+ end
79
+
80
+ protected
81
+
82
+ def body_unicode
83
+ body = body_str
84
+ if encoding and encoding != 'utf-8'
85
+ body = Iconv.conv('UTF-8', encoding, body)
86
+ end
87
+ body
88
+ end
89
+
90
+ end
91
+
92
+ Hpricot::Doc.class_eval do
93
+ def forms
94
+ search('form').map { |f| Curly::Form.new(f) }
95
+ end
96
+ end
@@ -0,0 +1,9 @@
1
+ module Highrise
2
+ class Email < Base
3
+ include Pagination
4
+
5
+ def comments
6
+ Comment.find(:all, :from => "/emails/#{email_id}/comments.xml")
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,4 @@
1
+ module Highrise
2
+ class Group < Base
3
+ end
4
+ end
@@ -0,0 +1,8 @@
1
+ module Highrise
2
+ class Kase < Subject
3
+ def close!
4
+ self.closed_at = Time.now.utc
5
+ save
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,4 @@
1
+ module Highrise
2
+ class Membership < Base
3
+ end
4
+ end
@@ -0,0 +1,9 @@
1
+ module Highrise
2
+ class Note < Base
3
+ include Pagination
4
+
5
+ def comments
6
+ Comment.find(:all, :from => "/notes/#{id}/comments.xml")
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,29 @@
1
+ module Highrise
2
+ module Pagination
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ module ClassMethods
8
+ def find_all_across_pages(options = {})
9
+ records = []
10
+ each(options) { |record| records << record }
11
+ records
12
+ end
13
+
14
+ def each(options = {})
15
+ options[:params] ||= {}
16
+ options[:params][:n] = 0
17
+
18
+ loop do
19
+ if (records = self.find(:all, options)).any?
20
+ records.each { |record| yield record }
21
+ options[:params][:n] += records.size
22
+ else
23
+ break # no people included on that page, thus no more people total
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,20 @@
1
+ module Highrise
2
+ class Person < Subject
3
+ include Pagination
4
+ include Taggable
5
+
6
+ def self.find_all_across_pages_since(time)
7
+ find_all_across_pages(:params => { :since => time.utc.to_s(:db).gsub(/[^\d]/, '') })
8
+ end
9
+
10
+ def company
11
+ Company.find(company_id) if company_id
12
+ end
13
+
14
+ def name
15
+ "#{first_name rescue ''} #{last_name rescue ''}".strip
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,15 @@
1
+ module Highrise
2
+ class Subject < Base
3
+ def notes
4
+ Note.find_all_across_pages(:from => "/#{self.class.collection_name}/#{id}/notes.xml")
5
+ end
6
+
7
+ def emails
8
+ Email.find_all_across_pages(:from => "/#{self.class.collection_name}/#{id}/emails.xml")
9
+ end
10
+
11
+ def upcoming_tasks
12
+ Task.find(:all, :from => "/#{self.class.collection_name}/#{id}/tasks.xml")
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ module Highrise
2
+ class Tag < Base
3
+ def ==(object)
4
+ (object.instance_of?(self.class) && object.id == self.id && object.name == self.name)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,29 @@
1
+ module Highrise
2
+ module Taggable
3
+
4
+ # thanks for the user Inka, http://forum.37signals.com/highrise/forums/15/topics/1312?page=2
5
+
6
+ def tag!(tag_name)
7
+ self.post(:tags, :name => tag_name) unless tag_name.blank?
8
+ end
9
+
10
+ def untag!(tag_name)
11
+ to_delete = self.tags.find{|t| t.name==tag_name} unless tag_name.blank?
12
+ self.delete("tags/#{to_delete.id}") unless to_delete.nil?
13
+ end
14
+
15
+ def tags
16
+ tags = []
17
+ get_document.search("#show_tags a").each{ |a|
18
+ tags << Highrise::Tag.new(:id => a['href'].gsub(/.*\//, ''), :name => a.inner_html) if a['class'] == "grey tag"
19
+ }
20
+ tags
21
+ end
22
+
23
+ def get_document
24
+ @curl_helper ||= CurlHelper.new
25
+ @doc = @curl_helper.get_document_from_id("#{self.class.collection_name}/#{self.id}")
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,11 @@
1
+ module Highrise
2
+ class Task < Base
3
+ # find(:all, :from => :upcoming)
4
+ # find(:all, :from => :assigned)
5
+ # find(:all, :from => :completed)
6
+
7
+ def complete!
8
+ load_attributes_from_response(post(:complete))
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ module Highrise
2
+ class User < Base
3
+ def join(group)
4
+ Membership.create(:user_id => id, :group_id => group.id)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ module Highrise
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 7
5
+ TINY = 0
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
data/lib/highrise.rb ADDED
@@ -0,0 +1,26 @@
1
+ require 'rubygems'
2
+ require 'active_resource'
3
+ require 'uri'
4
+ require 'curb'
5
+ gem 'activesupport', '>= 2.1'
6
+ require 'active_support/basic_object'
7
+ require 'hpricot'
8
+ require 'iconv'
9
+
10
+ require File.dirname(__FILE__) + '/highrise/pagination'
11
+ require File.dirname(__FILE__) + '/highrise/curly'
12
+ require File.dirname(__FILE__) + '/highrise/curlhelper'
13
+ require File.dirname(__FILE__) + '/highrise/taggable'
14
+ require File.dirname(__FILE__) + '/highrise/base'
15
+ require File.dirname(__FILE__) + '/highrise/subject'
16
+ require File.dirname(__FILE__) + '/highrise/comment'
17
+ require File.dirname(__FILE__) + '/highrise/company'
18
+ require File.dirname(__FILE__) + '/highrise/email'
19
+ require File.dirname(__FILE__) + '/highrise/group'
20
+ require File.dirname(__FILE__) + '/highrise/kase'
21
+ require File.dirname(__FILE__) + '/highrise/membership'
22
+ require File.dirname(__FILE__) + '/highrise/note'
23
+ require File.dirname(__FILE__) + '/highrise/person'
24
+ require File.dirname(__FILE__) + '/highrise/task'
25
+ require File.dirname(__FILE__) + '/highrise/user'
26
+ require File.dirname(__FILE__) + '/highrise/tag'
@@ -0,0 +1,13 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe Highrise::Base do
4
+
5
+ before(:each) do
6
+ @base = Highrise::Base.new
7
+ end
8
+
9
+ it "should be instance of ActiveResource::Base" do
10
+ @base.kind_of?(ActiveResource::Base).should be_true
11
+ end
12
+
13
+ end
@@ -0,0 +1,14 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe Highrise::Comment do
4
+
5
+ before(:each) do
6
+ @comment = Highrise::Comment.new
7
+ end
8
+
9
+ it "should be instance of Highrise::Base" do
10
+ @comment.kind_of?(Highrise::Base).should be_true
11
+ end
12
+
13
+
14
+ end