kmayer-highrise 0.7.0

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.
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