soleone-highrise 0.13.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/.gitignore +1 -0
  2. data/CHANGELOG +91 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.mkdn +86 -0
  5. data/Rakefile +46 -0
  6. data/VERSION.yml +4 -0
  7. data/autotest/discover.rb +3 -0
  8. data/examples/config_initializers_highrise.rb +12 -0
  9. data/examples/extending.rb +31 -0
  10. data/examples/sample.rb +10 -0
  11. data/highrise.gemspec +131 -0
  12. data/install.rb +1 -0
  13. data/lib/cachable.rb +77 -0
  14. data/lib/highrise.rb +22 -0
  15. data/lib/highrise/base.rb +9 -0
  16. data/lib/highrise/comment.rb +4 -0
  17. data/lib/highrise/company.rb +14 -0
  18. data/lib/highrise/curlhelper.rb +27 -0
  19. data/lib/highrise/curly.rb +101 -0
  20. data/lib/highrise/deal.rb +35 -0
  21. data/lib/highrise/email.rb +9 -0
  22. data/lib/highrise/group.rb +7 -0
  23. data/lib/highrise/kase.rb +8 -0
  24. data/lib/highrise/membership.rb +4 -0
  25. data/lib/highrise/note.rb +9 -0
  26. data/lib/highrise/pagination.rb +29 -0
  27. data/lib/highrise/person.rb +43 -0
  28. data/lib/highrise/subject.rb +50 -0
  29. data/lib/highrise/tag.rb +18 -0
  30. data/lib/highrise/taggable.rb +33 -0
  31. data/lib/highrise/task.rb +26 -0
  32. data/lib/highrise/user.rb +11 -0
  33. data/spec/cachable_spec.rb +68 -0
  34. data/spec/highrise/base_spec.rb +13 -0
  35. data/spec/highrise/comment_spec.rb +14 -0
  36. data/spec/highrise/companies/16883216.html +1723 -0
  37. data/spec/highrise/company_spec.rb +70 -0
  38. data/spec/highrise/curlhelper_spec.rb +35 -0
  39. data/spec/highrise/curly_spec.rb +36 -0
  40. data/spec/highrise/email_spec.rb +25 -0
  41. data/spec/highrise/group_spec.rb +14 -0
  42. data/spec/highrise/kase_spec.rb +25 -0
  43. data/spec/highrise/membership_spec.rb +14 -0
  44. data/spec/highrise/note_spec.rb +23 -0
  45. data/spec/highrise/pagination_spec.rb +10 -0
  46. data/spec/highrise/people/16887003.html +1733 -0
  47. data/spec/highrise/person_spec.rb +83 -0
  48. data/spec/highrise/subject_spec.rb +43 -0
  49. data/spec/highrise/tag_spec.rb +24 -0
  50. data/spec/highrise/task_spec.rb +23 -0
  51. data/spec/highrise/user_spec.rb +30 -0
  52. data/spec/spec.opts +7 -0
  53. data/spec/spec_helper.rb +20 -0
  54. data/uninstall.rb +1 -0
  55. metadata +176 -0
@@ -0,0 +1 @@
1
+ # Install hook code here
@@ -0,0 +1,77 @@
1
+ # Caching is a way to speed up slow ActiveResource queries by keeping the result of
2
+ # a request around to be reused by subsequent requests.
3
+ #
4
+ # Caching is turned OFF by default.
5
+ #
6
+ # == Usage
7
+ #
8
+ # require 'cachable'
9
+ #
10
+ # module CachedResource
11
+ # class Base < ActiveResource::Base
12
+ # end
13
+ # class ActiveResource::Connection
14
+ # include Cachable
15
+ # end
16
+ # end
17
+ #
18
+ #
19
+ # == Caching stores
20
+ #
21
+ # All the caching stores from ActiveSupport::Cache are available
22
+ # as backends for caching. See the Rails rdoc for more information on
23
+ # these stores
24
+ #
25
+ # === Configuration examples ('off' is the default):
26
+ # CachedResource.connection.cache_store = ActiveSupport::Cache.lookup_store :memory_store
27
+ # CachedResource.connection.cache_store = ActiveSupport::Cache.lookup_store :file_store, "/path/to/cache/directory"
28
+ # CachedResource.connection.cache_store = ActiveSupport::Cache.lookup_store :drb_store, "druby://localhost:9192"
29
+ # CachedResource.connection.cache_store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost"
30
+ # CachedResource.connection.cache_store = MyOwnStore.new("parameter")
31
+ #
32
+ # === If you are using a store that has write options, you can set them
33
+ # CachedResource.connection.store_options = { :expires_in => 60.seconds }
34
+ #
35
+ # Note: To ensure that caching is turned off, set CachedResource.connection.cache_store = nil
36
+ #
37
+ # FYI: You can use this with *any* active resource interface, not just Highrise.
38
+
39
+ module Cachable
40
+ def self.included(base)
41
+ base.class_eval do
42
+ include InstanceMethods
43
+ alias_method_chain :get, :cache
44
+ end
45
+ end
46
+
47
+ module InstanceMethods
48
+ attr_writer :cache_store, :store_options
49
+
50
+ def cache_store
51
+ @cache_store ||= nil
52
+ end
53
+
54
+ def store_options
55
+ @store_options ||= {}
56
+ end
57
+
58
+ def is_caching?
59
+ !@cache_store.nil?
60
+ end
61
+
62
+ private
63
+
64
+ def get_with_cache(path, headers = {})
65
+ return get_without_cache(path, headers) unless is_caching?
66
+ fetch(path) { get_without_cache(path, headers) }
67
+ end
68
+
69
+ def cache_key(*args)
70
+ args.to_s
71
+ end
72
+
73
+ def fetch(args, &block)
74
+ cache_store.fetch(cache_key(args), store_options, &block).dup
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,22 @@
1
+ require 'active_resource'
2
+ require 'activesupport'
3
+ require 'active_support/basic_object'
4
+
5
+ require File.dirname(__FILE__) + '/highrise/base'
6
+ require File.dirname(__FILE__) + '/highrise/pagination'
7
+ require File.dirname(__FILE__) + '/highrise/curly'
8
+ require File.dirname(__FILE__) + '/highrise/curlhelper'
9
+ require File.dirname(__FILE__) + '/highrise/taggable'
10
+ require File.dirname(__FILE__) + '/highrise/subject'
11
+ require File.dirname(__FILE__) + '/highrise/comment'
12
+ require File.dirname(__FILE__) + '/highrise/company'
13
+ require File.dirname(__FILE__) + '/highrise/email'
14
+ require File.dirname(__FILE__) + '/highrise/group'
15
+ require File.dirname(__FILE__) + '/highrise/kase'
16
+ require File.dirname(__FILE__) + '/highrise/membership'
17
+ require File.dirname(__FILE__) + '/highrise/note'
18
+ require File.dirname(__FILE__) + '/highrise/person'
19
+ require File.dirname(__FILE__) + '/highrise/task'
20
+ require File.dirname(__FILE__) + '/highrise/user'
21
+ require File.dirname(__FILE__) + '/highrise/tag'
22
+ require File.dirname(__FILE__) + '/highrise/deal'
@@ -0,0 +1,9 @@
1
+ require File.dirname(__FILE__) + '/../cachable'
2
+
3
+ module Highrise
4
+ class Base < ActiveResource::Base
5
+ end
6
+ class ActiveResource::Connection
7
+ include Cachable
8
+ end
9
+ 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,101 @@
1
+ require 'hpricot'
2
+ require 'iconv'
3
+ require 'curb'
4
+ require 'uri'
5
+
6
+ class Curly < ActiveSupport::BasicObject
7
+ attr_reader :uri
8
+
9
+ def initialize(uri = nil)
10
+ @curl = Curl::Easy.new
11
+ self.uri = uri
12
+ self.follow_location = true
13
+ yield self if block_given?
14
+ end
15
+
16
+ def uri=(obj)
17
+ case obj
18
+ when String
19
+ unless @uri
20
+ @uri = URI.parse(obj)
21
+ else
22
+ @uri += obj
23
+ end
24
+ when URI::HTTP
25
+ @uri = obj
26
+ when nil
27
+ return
28
+ else
29
+ raise "unsupported URI type (#{obj.class.name} given)"
30
+ end
31
+
32
+ self.url = @uri.to_s
33
+ end
34
+
35
+ def method_missing(method, *args, &block)
36
+ @curl.send(method, *args, &block)
37
+ end
38
+
39
+ def cookiejar=(filename)
40
+ self.enable_cookies = true
41
+ @curl.cookiejar = filename
42
+ end
43
+
44
+ def get(uri = nil)
45
+ self.uri = uri
46
+ http_get
47
+ raise "expected 2xx, got #{response_code} (GET #{url})" unless success?
48
+ self
49
+ end
50
+
51
+ def success?
52
+ response_code >= 200 and response_code < 300
53
+ end
54
+
55
+ def doc
56
+ Hpricot body_unicode
57
+ end
58
+
59
+ def encoding
60
+ return @encoding unless @encoding == false
61
+ @encoding = if body_str =~ /;\s*charset=([\w-]+)\s*['"]/
62
+ $1.downcase
63
+ else
64
+ false
65
+ end
66
+ end
67
+
68
+ def post(params)
69
+ fields = params.map do |key, value|
70
+ Curl::PostField.content(key.to_s, value.to_s)
71
+ end
72
+ http_post *fields
73
+ end
74
+
75
+ class Form
76
+ def initialize(element)
77
+ @node = element
78
+ end
79
+
80
+ def elements
81
+ @node.search('input, button, select, textarea')
82
+ end
83
+ end
84
+
85
+ protected
86
+
87
+ def body_unicode
88
+ body = body_str
89
+ if encoding and encoding != 'utf-8'
90
+ body = Iconv.conv('UTF-8', encoding, body)
91
+ end
92
+ body
93
+ end
94
+
95
+ end
96
+
97
+ Hpricot::Doc.class_eval do
98
+ def forms
99
+ search('form').map { |f| Curly::Form.new(f) }
100
+ end
101
+ end
@@ -0,0 +1,35 @@
1
+ module Highrise
2
+ # name
3
+ # background
4
+ # status (pending, won, lost)
5
+ # category-id
6
+ # ---------------------------
7
+ # account-id
8
+ # author-id
9
+ # created-at
10
+ # currency
11
+ # duration
12
+ # group-id
13
+ # owner-id
14
+ # party-id
15
+ # price type
16
+ # price-type
17
+ # responsible-party-id
18
+ # status-changed-on
19
+ # updated-at
20
+ # visible-to
21
+ class Deal < Subject
22
+
23
+ class DealCategory < Base
24
+ def self.find_by_name(name)
25
+ find(:all).select{|category| category.name == name}.first
26
+ end
27
+
28
+ def self.delete!(name)
29
+ category = self.find_by_name(name)
30
+ delete(category.id) if category
31
+ end
32
+ end
33
+
34
+ end
35
+ 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,7 @@
1
+ module Highrise
2
+ class Group < Base
3
+ def self.find_by_name(name)
4
+ find(:all).select{|category| category.name == name}.first
5
+ end
6
+ end
7
+ 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,43 @@
1
+ module Highrise
2
+ class Person < Subject
3
+ DATE_OCCASIONS = ["Birthday", "Anniversary", "First met", "Hired", "Fired"]
4
+
5
+ include Pagination
6
+ include Taggable
7
+
8
+ def self.search_by_name(name)
9
+ find(:first, :from => "/people/search.xml", :params => {:term => name})
10
+ end
11
+
12
+ def self.find_all_by_tag(tag_name)
13
+ tag = Tag.find_by_name(tag_name)
14
+ find(:all, :from => "/tags/#{tag.id}.xml")
15
+ end
16
+
17
+ def self.find_all_across_pages_since(time)
18
+ find_all_across_pages(:params => { :since => time.utc.to_s(:db).gsub(/[^\d]/, '') })
19
+ end
20
+
21
+ def company
22
+ Company.find(company_id) if company_id
23
+ end
24
+
25
+ def name
26
+ "#{first_name rescue ''} #{last_name rescue ''}".strip
27
+ end
28
+
29
+
30
+ def add_date!(description, date, task_owner_id=nil)
31
+ description_key = DATE_OCCASIONS.include?(description) ? :description : :custom_description
32
+ custom_date = { description_key => description, :month => date.month, :day => date.day, :year => date.year }
33
+ if task_owner_id
34
+ custom_date[:assign_task] = true
35
+ custom_date[:task_owner_id] = task_owner_id
36
+ end
37
+ # TODO: use different way to add date (current throws 406 from Highrise, but works anyways)
38
+ self.post(:contact_dates, :contact_date => custom_date) rescue nil
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -0,0 +1,50 @@
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
+
15
+ def add_note!(note_body)
16
+ return if note_body.blank?
17
+ note = Note.new(:body => note_body)
18
+ self.post(:notes, {}, note.to_xml)
19
+ end
20
+
21
+ def add_deal!(name, category, status=nil, background=nil)
22
+ category_object = Deal::DealCategory.find_by_name(category)
23
+ category_id = category_object ? category_object.id : nil
24
+ deal = Deal.new(:name => name, :background => background, :status => status || "pending", :category_id => category_id)
25
+ deal.price_type = 'fixed'
26
+ deal.party_id = self.id
27
+ deal.save
28
+ end
29
+
30
+ def add_task!(body, due_date, arguments = {})
31
+ category = Task::TaskCategory.find_by_name(arguments[:category])
32
+ category_id = category ? category.id : nil
33
+
34
+ task = Task.new(arguments.merge(:body => body, :category_id => category_id))
35
+
36
+ if Task::DUE_DATES.include?(due_date.to_s)
37
+ task.frame = due_date
38
+ else
39
+ task.frame = 'specific'
40
+ task.due_at = due_date
41
+ end
42
+
43
+ task.subject_type = self.instance_of?(Kase) ? 'Kase' : 'Party'
44
+ task.subject_id = self.id
45
+ task.save
46
+ end
47
+
48
+
49
+ end
50
+ end