peterosullivan-highrise 3.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/.travis.yml +9 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +46 -0
- data/MIT-LICENSE +20 -0
- data/README.md +89 -0
- data/Rakefile +14 -0
- data/autotest/discover.rb +1 -0
- data/examples/config_initializers_highrise.rb +4 -0
- data/examples/extending.rb +31 -0
- data/examples/sample.rb +9 -0
- data/highrise.gemspec +33 -0
- data/lib/highrise/account.rb +7 -0
- data/lib/highrise/base.rb +25 -0
- data/lib/highrise/comment.rb +3 -0
- data/lib/highrise/company.rb +15 -0
- data/lib/highrise/deal.rb +8 -0
- data/lib/highrise/deal_category.rb +3 -0
- data/lib/highrise/email.rb +9 -0
- data/lib/highrise/group.rb +3 -0
- data/lib/highrise/kase.rb +19 -0
- data/lib/highrise/membership.rb +3 -0
- data/lib/highrise/note.rb +9 -0
- data/lib/highrise/pagination.rb +72 -0
- data/lib/highrise/party.rb +11 -0
- data/lib/highrise/person.rb +45 -0
- data/lib/highrise/recording.rb +5 -0
- data/lib/highrise/rfc822.rb +30 -0
- data/lib/highrise/searchable.rb +23 -0
- data/lib/highrise/subject.rb +31 -0
- data/lib/highrise/tag.rb +12 -0
- data/lib/highrise/taggable.rb +20 -0
- data/lib/highrise/task.rb +11 -0
- data/lib/highrise/task_category.rb +3 -0
- data/lib/highrise/user.rb +17 -0
- data/lib/highrise/version.rb +3 -0
- data/lib/highrise.rb +22 -0
- data/spec/highrise/account_spec.rb +10 -0
- data/spec/highrise/base_spec.rb +48 -0
- data/spec/highrise/comment_spec.rb +5 -0
- data/spec/highrise/company_spec.rb +17 -0
- data/spec/highrise/deal_category_spec.rb +13 -0
- data/spec/highrise/deal_spec.rb +23 -0
- data/spec/highrise/email_spec.rb +13 -0
- data/spec/highrise/group_spec.rb +5 -0
- data/spec/highrise/kase_spec.rb +17 -0
- data/spec/highrise/membership_spec.rb +5 -0
- data/spec/highrise/note_spec.rb +14 -0
- data/spec/highrise/pagination_behavior.rb +50 -0
- data/spec/highrise/pagination_spec.rb +8 -0
- data/spec/highrise/party_spec.rb +16 -0
- data/spec/highrise/person_spec.rb +33 -0
- data/spec/highrise/recording_spec.rb +7 -0
- data/spec/highrise/searchable_behavior.rb +13 -0
- data/spec/highrise/searchable_spec.rb +8 -0
- data/spec/highrise/subject_spec.rb +34 -0
- data/spec/highrise/tag_spec.rb +18 -0
- data/spec/highrise/taggable_behavior.rb +27 -0
- data/spec/highrise/taggable_spec.rb +9 -0
- data/spec/highrise/task_category_spec.rb +13 -0
- data/spec/highrise/task_spec.rb +11 -0
- data/spec/highrise/user_spec.rb +18 -0
- data/spec/spec_helper.rb +11 -0
- metadata +182 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
highrise (3.0.1)
|
5
|
+
activeresource (~> 3.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activemodel (3.1.0)
|
11
|
+
activesupport (= 3.1.0)
|
12
|
+
bcrypt-ruby (~> 3.0.0)
|
13
|
+
builder (~> 3.0.0)
|
14
|
+
i18n (~> 0.6)
|
15
|
+
activeresource (3.1.0)
|
16
|
+
activemodel (= 3.1.0)
|
17
|
+
activesupport (= 3.1.0)
|
18
|
+
activesupport (3.1.0)
|
19
|
+
multi_json (~> 1.0)
|
20
|
+
bcrypt-ruby (3.0.0)
|
21
|
+
bcrypt-ruby (3.0.0-java)
|
22
|
+
builder (3.0.0)
|
23
|
+
diff-lcs (1.1.2)
|
24
|
+
i18n (0.6.0)
|
25
|
+
multi_json (1.0.3)
|
26
|
+
rake (0.8.7)
|
27
|
+
rspec (2.0.1)
|
28
|
+
rspec-core (~> 2.0.1)
|
29
|
+
rspec-expectations (~> 2.0.1)
|
30
|
+
rspec-mocks (~> 2.0.1)
|
31
|
+
rspec-core (2.0.1)
|
32
|
+
rspec-expectations (2.0.1)
|
33
|
+
diff-lcs (>= 1.1.2)
|
34
|
+
rspec-mocks (2.0.1)
|
35
|
+
rspec-core (~> 2.0.1)
|
36
|
+
rspec-expectations (~> 2.0.1)
|
37
|
+
|
38
|
+
PLATFORMS
|
39
|
+
java
|
40
|
+
ruby
|
41
|
+
|
42
|
+
DEPENDENCIES
|
43
|
+
activeresource (~> 3.0)
|
44
|
+
highrise!
|
45
|
+
rake (= 0.8.7)
|
46
|
+
rspec (~> 2.0.1)
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Ken Mayer & Marcos Tapajos
|
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.md
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
##whats in this fork
|
2
|
+
|
3
|
+
This fork is very close to the orginal gem. I have added a few features and shortcut methods;
|
4
|
+
|
5
|
+
|
6
|
+
Highrise::Tag.delete_by_name('tag_name')
|
7
|
+
|
8
|
+
Highrise::Person.url_for(123)
|
9
|
+
Highrise::Person.find(123).tagged?('tag_name')
|
10
|
+
Highrise::Person.find(123).email_address
|
11
|
+
Highrise::Person.find(123).email_valid?
|
12
|
+
Highrise::Person.find(123).phone_number
|
13
|
+
|
14
|
+
# Highrise (3.0.0) [![Build Status](https://secure.travis-ci.org/tapajos/highrise.png)](http://travis-ci.org/tapajos/highrise)
|
15
|
+
|
16
|
+
## What is it?
|
17
|
+
|
18
|
+
This gem provides a set of classes to access information on [Highrise][h] via the published [API][api]:
|
19
|
+
|
20
|
+
Account, Comment, Company, Deal, DealCategory, Email, Group, Case, Membership, Note, Party, Person, Recording, Subject, Tag, Task, TaskCategory and User.
|
21
|
+
|
22
|
+
All these classes are inherited from ActiveResouce::Base. Refer to the [ActiveResouce][ar] documentation for more information.
|
23
|
+
|
24
|
+
## Installing
|
25
|
+
|
26
|
+
gem install peterosullivan-highrise
|
27
|
+
|
28
|
+
### Dependencies (see <code>highrise.gemspec</code> or run <code>bundle check</code>)
|
29
|
+
|
30
|
+
### Documentation
|
31
|
+
|
32
|
+
I'm on [rdoc.info][rdoc]
|
33
|
+
|
34
|
+
### Configure your key
|
35
|
+
|
36
|
+
require 'highrise'
|
37
|
+
|
38
|
+
Highrise::Base.site = 'https://your_site.highrisehq.com'
|
39
|
+
Highrise::Base.user = 'api-auth-token'
|
40
|
+
|
41
|
+
If you are using this in a Rails application, putting this code in a config/initializers/highrise.rb
|
42
|
+
file is recommended. See config_initializers_highrise.rb in the examples/ directory.
|
43
|
+
|
44
|
+
## Usage
|
45
|
+
|
46
|
+
@tags = Highrise::Tag.find(:all)
|
47
|
+
|
48
|
+
@people = Highrise::Person.find_all_across_pages(:params => {:tag_id => 12345})
|
49
|
+
|
50
|
+
@person.tag!("VIP")
|
51
|
+
|
52
|
+
## License
|
53
|
+
|
54
|
+
This code is free to be used under the terms of the [MIT license][mit].
|
55
|
+
|
56
|
+
## Bugs, Issues, Kudos and Catcalls
|
57
|
+
|
58
|
+
Comments are welcome. Send your feedback through the [issue tracker on GitHub][i]
|
59
|
+
|
60
|
+
If you have fixes: Submit via pull requests. Do not include version changes to the
|
61
|
+
version file.
|
62
|
+
|
63
|
+
## Authors
|
64
|
+
|
65
|
+
* [Marcos Tapajós][tapajos]
|
66
|
+
* [Ken Mayer][kmayer]
|
67
|
+
|
68
|
+
## Contributors
|
69
|
+
|
70
|
+
* [Nicolas Bianco][slainer86]
|
71
|
+
* [Luis Gustavo][luisbebop]
|
72
|
+
* [Thiago Lelis][ThiagoLelis]
|
73
|
+
* [Denis Odorcic][odorcicd]
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
[api]: http://developer.37signals.com/highrise
|
78
|
+
[ar]: http://api.rubyonrails.org/classes/ActiveResource/Base.html
|
79
|
+
[c]: http://api.rubyonrails.org/classes/ActiveSupport/Cache
|
80
|
+
[h]: http://www.highrisehq.com/
|
81
|
+
[i]: https://github.com/tapajos/highrise/issues
|
82
|
+
[kmayer]: https://github.com/kmayer
|
83
|
+
[luisbebop]: https://github.com/luisbebop
|
84
|
+
[mit]:http://www.opensource.org/licenses/mit-license.php
|
85
|
+
[slainer86]: https://github.com/slainer86
|
86
|
+
[odorcicd]: https://github.com/odorcicd
|
87
|
+
[rdoc]: http://rdoc.info/projects/tapajos/highrise
|
88
|
+
[tapajos]: http://www.improveit.com.br/en/company/tapajos
|
89
|
+
[ThiagoLelis]: https://github.com/ThiagoLelis
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'bundler'
|
3
|
+
Bundler::GemHelper.install_tasks
|
4
|
+
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
|
7
|
+
desc 'Default: run unit tests.'
|
8
|
+
task :default => :spec
|
9
|
+
|
10
|
+
desc "Run all specs"
|
11
|
+
RSpec::Core::RakeTask.new do |t|
|
12
|
+
t.pattern = 'spec/**/*_spec.rb'
|
13
|
+
t.rspec_opts = ["-c", "-f progress"]
|
14
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
Autotest.add_discovery { "rspec2" }
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#
|
2
|
+
# Example of extending a class when you need to synthesize an attribute.
|
3
|
+
#
|
4
|
+
# Adds Highrise::Person.{phone,fax,email} to the Person class inside your
|
5
|
+
# module
|
6
|
+
#
|
7
|
+
|
8
|
+
module MyModule
|
9
|
+
include Highrise
|
10
|
+
|
11
|
+
Highrise::Person.class_eval do
|
12
|
+
class << self
|
13
|
+
def lookup(id, list, item, location)
|
14
|
+
module_eval <<-EOT
|
15
|
+
def #{id}
|
16
|
+
contact_data.#{list}.each do |i|
|
17
|
+
return i.#{item}.strip if i.location == "#{location}"
|
18
|
+
end
|
19
|
+
''
|
20
|
+
end
|
21
|
+
EOT
|
22
|
+
end
|
23
|
+
|
24
|
+
private :lookup
|
25
|
+
end
|
26
|
+
|
27
|
+
lookup(:phone, 'phone_numbers', 'number', 'Work')
|
28
|
+
lookup(:fax, 'phone_numbers', 'number', 'Fax')
|
29
|
+
lookup(:email, 'email_addresses', 'address', 'Work')
|
30
|
+
end
|
31
|
+
end
|
data/examples/sample.rb
ADDED
data/highrise.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "highrise/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "peterosullivan-highrise"
|
7
|
+
s.version = Highrise::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
|
10
|
+
s.required_rubygems_version = ">= 1.3.6"
|
11
|
+
s.add_dependency "activeresource", "~>3.0"
|
12
|
+
s.add_development_dependency "rspec", "~>2.0.1"
|
13
|
+
s.add_development_dependency "rake", "=0.8.7"
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features,examples}/*`.split("\n")
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
s.authors = ["Marcos Tapaj\303\263s", "Ken Mayer", "Peter O'Sullivan"]
|
20
|
+
s.email = ["marcos@tapajos.me", "kmayer@bitwrangler.com", "peter@peterosullivan.net"]
|
21
|
+
s.homepage = "https://github.com/peterosullivan/highrise"
|
22
|
+
s.summary = %q{Ruby wrapper around Highrise API}
|
23
|
+
s.description = <<-EOT
|
24
|
+
Based on the original API module from DHH, http://developer.37signals.com/highrise/, this
|
25
|
+
gem is a cleaned up, tested version of the same. A fork of Tapaj gem. See the homepage for add features.
|
26
|
+
|
27
|
+
Configure by adding the following:
|
28
|
+
|
29
|
+
require 'highrise'
|
30
|
+
Highrise::Base.site = 'http://your_site.highrisehq.com/'
|
31
|
+
Highrise::Base.user = 'your_api_auth_token'
|
32
|
+
EOT
|
33
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'active_resource'
|
2
|
+
|
3
|
+
module Highrise
|
4
|
+
class Base < ActiveResource::Base
|
5
|
+
|
6
|
+
def self.url_for(n)
|
7
|
+
base = site.to_s.split('@')[1]
|
8
|
+
File.join('https://', base, element_path(n)).gsub(".xml",'')
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
# Dynamic finder for attributes
|
13
|
+
def self.method_missing(method, *args)
|
14
|
+
if method.to_s =~ /^find_(all_)?by_([_a-zA-Z]\w*)$/
|
15
|
+
raise ArgumentError, "Dynamic finder method must take an argument." if args.empty?
|
16
|
+
options = args.extract_options!
|
17
|
+
resources = respond_to?(:find_all_across_pages) ? send(:find_all_across_pages, options) : send(:find, :all)
|
18
|
+
resources.send($1 == 'all_' ? 'select' : 'detect') { |container| container.send($2) == args.first }
|
19
|
+
else
|
20
|
+
super
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Highrise
|
2
|
+
class Kase < Subject
|
3
|
+
def open!
|
4
|
+
update_attribute(:closed_at, nil)
|
5
|
+
end
|
6
|
+
|
7
|
+
def close!
|
8
|
+
update_attribute(:closed_at, Time.now.utc)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.open
|
12
|
+
Kase.find(:all, :from => "/kases/open.xml")
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.closed
|
16
|
+
Kase.find(:all, :from => "/kases/closed.xml")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,72 @@
|
|
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
|
+
# This only is usefull for company, person & recordings, but should be safely ignored by other classes
|
15
|
+
def find_all_across_pages_since(time)
|
16
|
+
find_all_across_pages(:params => { :since => time.utc.strftime("%Y%m%d%H%M%S") })
|
17
|
+
end
|
18
|
+
|
19
|
+
# This is useful only for Company, Person, Note, Comment, Email and Task, but should be safely ignored by other classes
|
20
|
+
def find_all_deletions_across_pages(options = {})
|
21
|
+
# point to the global deletions feed
|
22
|
+
options[:from] = '/deletions.xml'
|
23
|
+
|
24
|
+
records = []
|
25
|
+
each_deletions(options) { |record| records << record }
|
26
|
+
records
|
27
|
+
end
|
28
|
+
|
29
|
+
# This is useful only for Company, Person, Note, Comment, Email and Task, but should be safely ignored by other classes
|
30
|
+
def find_all_deletions_across_pages_since(time)
|
31
|
+
find_all_deletions_across_pages(:params => { :since => time.utc.strftime("%Y%m%d%H%M%S") })
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def each(options = {})
|
37
|
+
options[:params] ||= {}
|
38
|
+
options[:params][:n] = 0
|
39
|
+
|
40
|
+
loop do
|
41
|
+
if (records = self.find(:all, options)).try(:any?)
|
42
|
+
records.each { |record| yield record }
|
43
|
+
options[:params][:n] += records.size
|
44
|
+
else
|
45
|
+
break # no people included on that page, thus no more people total
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def each_deletions(options = {})
|
51
|
+
options[:params] ||= {}
|
52
|
+
# first index for deletions is 1
|
53
|
+
options[:params][:n] = 1
|
54
|
+
|
55
|
+
loop do
|
56
|
+
if (records = self.find(:all, options)).try(:any?)
|
57
|
+
# reject the records whose resource type is different from self
|
58
|
+
records.reject!{|r| r.class.to_s.split('::').last != self.to_s.split('::').last}
|
59
|
+
|
60
|
+
records.each{ |record| yield record }
|
61
|
+
|
62
|
+
# index increment for deletions is 1 per page of 500 resources
|
63
|
+
options[:params][:n] += 1
|
64
|
+
else
|
65
|
+
break # no deletions included on that page, thus no more deletions
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Highrise
|
2
|
+
class Party < Base
|
3
|
+
def self.recently_viewed
|
4
|
+
find(:all, :from => "/parties/recently_viewed.xml")
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.deletions_since(time)
|
8
|
+
find(:all, :from => "/parties/deletions.xml", :params => { :since => time.utc.strftime("%Y%m%d%H%M%S") })
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Highrise
|
2
|
+
class Person < Subject
|
3
|
+
require 'highrise/rfc822'
|
4
|
+
include Pagination
|
5
|
+
include Taggable
|
6
|
+
include Searchable
|
7
|
+
|
8
|
+
def company
|
9
|
+
Company.find(company_id) if company_id
|
10
|
+
end
|
11
|
+
|
12
|
+
def name
|
13
|
+
"#{first_name rescue ''} #{last_name rescue ''}".strip
|
14
|
+
end
|
15
|
+
|
16
|
+
def address
|
17
|
+
contact_data.addresses.first
|
18
|
+
end
|
19
|
+
|
20
|
+
def web_address
|
21
|
+
contact_data.web_addresses.first
|
22
|
+
end
|
23
|
+
|
24
|
+
def label
|
25
|
+
'Party'
|
26
|
+
end
|
27
|
+
|
28
|
+
def phone_number
|
29
|
+
contact_data.phone_numbers.first.number rescue nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def email_valid?
|
33
|
+
!!(email_address && (email_address =~ RFC822::EmailAddress))
|
34
|
+
end
|
35
|
+
|
36
|
+
def email_address
|
37
|
+
contact_data.email_addresses.first.address rescue nil
|
38
|
+
end
|
39
|
+
|
40
|
+
def tagged? name
|
41
|
+
tags.any?{ | tag | tag['name'].to_s == name}
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#
|
2
|
+
# RFC822 Email Address Regex
|
3
|
+
# --------------------------
|
4
|
+
#
|
5
|
+
# Originally written by Cal Henderson
|
6
|
+
# c.f. http://iamcal.com/publish/articles/php/parsing_email/
|
7
|
+
#
|
8
|
+
# Translated to Ruby by Tim Fletcher, with changes suggested by Dan Kubb.
|
9
|
+
#
|
10
|
+
# Licensed under a Creative Commons Attribution-ShareAlike 2.5 License
|
11
|
+
# http://creativecommons.org/licenses/by-sa/2.5/
|
12
|
+
#
|
13
|
+
module RFC822
|
14
|
+
EmailAddress = begin
|
15
|
+
qtext = '[^\\x0d\\x22\\x5c\\x80-\\xff]'
|
16
|
+
dtext = '[^\\x0d\\x5b-\\x5d\\x80-\\xff]'
|
17
|
+
atom = '[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-' +
|
18
|
+
'\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+'
|
19
|
+
quoted_pair = '\\x5c[\\x00-\\x7f]'
|
20
|
+
domain_literal = "\\x5b(?:#{dtext}|#{quoted_pair})*\\x5d"
|
21
|
+
quoted_string = "\\x22(?:#{qtext}|#{quoted_pair})*\\x22"
|
22
|
+
domain_ref = atom
|
23
|
+
sub_domain = "(?:#{domain_ref}|#{domain_literal})"
|
24
|
+
word = "(?:#{atom}|#{quoted_string})"
|
25
|
+
domain = "#{sub_domain}(?:\\x2e#{sub_domain})*"
|
26
|
+
local_part = "#{word}(?:\\x2e#{word})*"
|
27
|
+
addr_spec = "#{local_part}\\x40#{domain}"
|
28
|
+
pattern = /\A#{addr_spec}\z/
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Highrise
|
2
|
+
module Searchable
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
# List By Search Criteria
|
9
|
+
# Ex: Highrise::Person.search(:email => "john.doe@example.com", :country => "CA")
|
10
|
+
# Available criteria are: city, state, country, zip, phone, email
|
11
|
+
def search(options = {})
|
12
|
+
raise ArgumentError, "cannot convert #{options}:#{options.class} to hash" if options.kind_of?(String)
|
13
|
+
search_params = options.inject({}) { |h, (k, v)| h["criteria[#{k}]"] = v; h }
|
14
|
+
# This might have to be changed in the future if other non-pagable resources become searchable
|
15
|
+
if self.respond_to?(:find_all_across_pages)
|
16
|
+
self.find_all_across_pages(:from => "/#{self.collection_name}/search.xml", :params => search_params)
|
17
|
+
else
|
18
|
+
self.find(:all, {:from => "/#{self.collection_name}/search.xml", :params => search_params})
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,31 @@
|
|
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 add_note(attrs={})
|
8
|
+
attrs[:subject_id] = self.id
|
9
|
+
attrs[:subject_type] = self.label
|
10
|
+
Note.create attrs
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_task(attrs={})
|
14
|
+
attrs[:subject_id] = self.id
|
15
|
+
attrs[:subject_type] = self.label
|
16
|
+
Task.create attrs
|
17
|
+
end
|
18
|
+
|
19
|
+
def emails
|
20
|
+
Email.find_all_across_pages(:from => "/#{self.class.collection_name}/#{id}/emails.xml")
|
21
|
+
end
|
22
|
+
|
23
|
+
def upcoming_tasks
|
24
|
+
Task.find(:all, :from => "/#{self.class.collection_name}/#{id}/tasks.xml")
|
25
|
+
end
|
26
|
+
|
27
|
+
def label
|
28
|
+
self.class.name.split('::').last
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/highrise/tag.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
module Highrise
|
2
|
+
module Taggable
|
3
|
+
def tags
|
4
|
+
self.get(:tags)
|
5
|
+
end
|
6
|
+
|
7
|
+
def tag!(tag_name)
|
8
|
+
self.post(:tags, :name => tag_name) unless tag_name.blank?
|
9
|
+
end
|
10
|
+
|
11
|
+
def untag!(tag_name)
|
12
|
+
to_delete = self.tags.find{|tag| tag['name'] == tag_name} unless tag_name.blank?
|
13
|
+
self.untag_id!(to_delete['id']) unless to_delete.nil?
|
14
|
+
end
|
15
|
+
protected
|
16
|
+
def untag_id!(tag_id)
|
17
|
+
self.delete("tags/#{tag_id}")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|