capsulecrmii 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +10 -0
- data/README.rdoc +33 -0
- data/Rakefile +9 -0
- data/capsulecrm.gemspec +23 -0
- data/examples.rb +82 -0
- data/lib/capsulecrm.rb +45 -0
- data/lib/capsulecrm/address.rb +23 -0
- data/lib/capsulecrm/base.rb +176 -0
- data/lib/capsulecrm/child.rb +24 -0
- data/lib/capsulecrm/child_collection.rb +14 -0
- data/lib/capsulecrm/collection.rb +14 -0
- data/lib/capsulecrm/contact.rb +27 -0
- data/lib/capsulecrm/custom_field.rb +41 -0
- data/lib/capsulecrm/email.rb +64 -0
- data/lib/capsulecrm/history.rb +32 -0
- data/lib/capsulecrm/history_item.rb +20 -0
- data/lib/capsulecrm/organisation.rb +41 -0
- data/lib/capsulecrm/party.rb +114 -0
- data/lib/capsulecrm/person.rb +126 -0
- data/lib/capsulecrm/phone.rb +15 -0
- data/lib/capsulecrm/record_not_found.rb +1 -0
- data/lib/capsulecrm/recorn_not_recognised.rb +1 -0
- data/lib/capsulecrm/tag.rb +12 -0
- data/lib/capsulecrm/version.rb +3 -0
- data/lib/capsulecrm/website.rb +19 -0
- data/test/create_person_test.rb +36 -0
- data/test/fixtures/responses/create_person.yml +59 -0
- data/test/fixtures/responses/party_history.yml +45 -0
- data/test/fixtures/responses/party_tags.yml +26 -0
- data/test/fixtures/responses/person_find_all.yml +28 -0
- data/test/fixtures/responses/person_find_all_with_limit.yml +26 -0
- data/test/fixtures/responses/person_find_all_with_offset.yml +28 -0
- data/test/fixtures/responses/person_find_by_id.yml +102 -0
- data/test/fixtures/responses/update_person.yml +85 -0
- data/test/fixtures/responses/update_person_without_changes.yml +28 -0
- data/test/party_dot_find_test.rb +40 -0
- data/test/party_dot_history_test.rb +29 -0
- data/test/party_dot_tags_test.rb +30 -0
- data/test/person_dot_find_all_test.rb +48 -0
- data/test/person_dot_find_by_id_test.rb +61 -0
- data/test/test_helper.rb +52 -0
- data/test/update_person_test.rb +46 -0
- metadata +131 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
= CapsuleCRM
|
2
|
+
|
3
|
+
This is a ruby wrapper for the CapsuleCRM API. It is alpha software and should be considered a work in progress. It currently supports People & Organisations. There is support for CustomFields & Contacts but they are read-only.
|
4
|
+
|
5
|
+
== Installation
|
6
|
+
|
7
|
+
[sudo] gem install capsulecrm
|
8
|
+
|
9
|
+
gem 'capsulecrm' # rails
|
10
|
+
require 'capsulecrm' # non-rails
|
11
|
+
|
12
|
+
== Getting started
|
13
|
+
|
14
|
+
You will need to supply the gem with your CapsuleCRM API token and account name. The account name is the first part of your CapsuleCRM url:
|
15
|
+
|
16
|
+
CapsuleCRM.account_name = "test-account" # http://test-account.capsulecrm.com
|
17
|
+
CapsuleCRM.api_token = 'MY_API_TOKEN'
|
18
|
+
CapsuleCRM.initialize!
|
19
|
+
|
20
|
+
If you're using rails, you can put that in config/initializers/capsulecrm.rb
|
21
|
+
|
22
|
+
|
23
|
+
== Usage
|
24
|
+
|
25
|
+
Please see examples.rb for usage examples
|
26
|
+
|
27
|
+
== Feedback
|
28
|
+
|
29
|
+
Bug reports, feature requests and patches are welcome. Please create an issue on the github issue tracker.
|
30
|
+
|
31
|
+
== License
|
32
|
+
|
33
|
+
MIT License. Copyright 2011 Ahmed Adam
|
data/Rakefile
ADDED
data/capsulecrm.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "capsulecrm/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "capsulecrmii"
|
7
|
+
s.version = CapsuleCRM::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Ahmed Adam", "dsimard"]
|
10
|
+
s.email = ["ahmed.msgs@gmail.com", "dsimard@azanka.ca"]
|
11
|
+
s.homepage = "https://github.com/dsimard/capsulecrm"
|
12
|
+
s.summary = %q{CapsuleCRM API Gem}
|
13
|
+
s.description = %q{CapsuleCRM API Gem}
|
14
|
+
|
15
|
+
s.add_dependency 'httparty', '~> 0.7'
|
16
|
+
s.add_dependency 'activemodel', '~> 3.0'
|
17
|
+
s.add_dependency 'activesupport', '~> 3.0'
|
18
|
+
|
19
|
+
s.files = `git ls-files`.split("\n")
|
20
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
21
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
22
|
+
s.require_paths = ["lib"]
|
23
|
+
end
|
data/examples.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# NOTE: CapsuleCRM::Person and CapsuleCRM::Organisation have virtually identically methods.
|
2
|
+
|
3
|
+
|
4
|
+
# find by id
|
5
|
+
person = CapsuleCRM::Person.find 123
|
6
|
+
|
7
|
+
# or if you don't know what you are searching for Party
|
8
|
+
something = CapsuleCRM::Party.find 123
|
9
|
+
something.is?(:person)
|
10
|
+
something.is?(:organisation)
|
11
|
+
|
12
|
+
# find by email
|
13
|
+
person = CapsuleCRM::Person.find_by_email 'foo@example.com'
|
14
|
+
|
15
|
+
# find all
|
16
|
+
person = CapsuleCRM::Person.find :all, :offset => 10, :limit => 5
|
17
|
+
|
18
|
+
# find by search term (searches name, telephone number and searchable custom fields)
|
19
|
+
person = CapsuleCRM::Person.search 'fred', :offset => 10, :limit => 5
|
20
|
+
|
21
|
+
# CapsuleCRM::Person attributes
|
22
|
+
person.id
|
23
|
+
person.title
|
24
|
+
person.first_name
|
25
|
+
person.last_name
|
26
|
+
person.job_title
|
27
|
+
person.about
|
28
|
+
|
29
|
+
# Add a note to the history
|
30
|
+
person.add_history "This is a note"
|
31
|
+
|
32
|
+
# update a person object
|
33
|
+
person.first_name = "Homer"
|
34
|
+
person.last_name = "Simpson"
|
35
|
+
person.save # returns true/false
|
36
|
+
|
37
|
+
# create a person object
|
38
|
+
person = CapsuleCRM::Person.new
|
39
|
+
person.first_name = "Marge"
|
40
|
+
person.last_name = "Simpson"
|
41
|
+
person.save # returns true/false
|
42
|
+
|
43
|
+
|
44
|
+
# get the person's organisation
|
45
|
+
person.organisation # CapsuleCRM::Organisation
|
46
|
+
|
47
|
+
# CapsuleCRM::Organisation attributes:
|
48
|
+
organisation.about
|
49
|
+
organisation.name
|
50
|
+
|
51
|
+
|
52
|
+
# Contacts: CapsuleCRM::Phone (read-only)
|
53
|
+
person.phone_numbers # CapsuleCRM::Collection
|
54
|
+
person.phone_numbers.first.number # 01234 56789
|
55
|
+
person.phone_numbers.first.type # work
|
56
|
+
|
57
|
+
# Contacts: CapsuleCRM::Website (read-only)
|
58
|
+
party.websites # CapsuleCRM::Collection
|
59
|
+
party.websites.first.url # http://google.com
|
60
|
+
party.websites.first.web_address # http://google.com
|
61
|
+
|
62
|
+
# Contacts: CapsuleCRM::Email (read-only)
|
63
|
+
person.emails # CapsuleCRM::Collection
|
64
|
+
person.emails.first.address # 'foo@example.com'
|
65
|
+
person.emails.first.type # 'home'
|
66
|
+
|
67
|
+
# Contacts: CapsuleCRM::Address (read-only)
|
68
|
+
person.addresses # CapsuleCRM::Collection
|
69
|
+
person.addresses.first.street # 10 Somestreet
|
70
|
+
person.addresses.first.city # Manchester
|
71
|
+
person.addresses.first.state # Greater Manchester
|
72
|
+
person.addresses.first.zip # ME10 7TR
|
73
|
+
person.addresses.first.country # United Kingdom
|
74
|
+
|
75
|
+
# CapsuleCRM::CustomFields (read-only)
|
76
|
+
person.custom_fields # CapsuleCRM::Collection
|
77
|
+
person.custom_fields.first.label # 'Favourite colour'
|
78
|
+
person.custom_fields.first.value # 'Blue'
|
79
|
+
|
80
|
+
# CapsuleCRM::Tag (read-only)
|
81
|
+
person.tags # CapsuleCRM::Collection
|
82
|
+
person.tag_names # ["array", "of all", "tags", "as strings"]
|
data/lib/capsulecrm.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
2
|
+
require 'active_support/core_ext/hash'
|
3
|
+
require 'active_support'
|
4
|
+
require 'active_model'
|
5
|
+
require 'httparty'
|
6
|
+
require 'uri'
|
7
|
+
module CapsuleCRM
|
8
|
+
|
9
|
+
mattr_accessor :account_name
|
10
|
+
mattr_accessor :api_token
|
11
|
+
|
12
|
+
# nodoc
|
13
|
+
def self.base_uri(account_name)
|
14
|
+
"https://#{account_name}.capsulecrm.com"
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
# nodoc
|
19
|
+
def self.initialize!
|
20
|
+
raise ArgumentError, "CapsuleCRM.account_name not defined" if account_name.nil?
|
21
|
+
raise ArgumentError, "CapsuleCRM.api_token not defined" if api_token.nil?
|
22
|
+
CapsuleCRM::Base.base_uri base_uri(account_name)
|
23
|
+
CapsuleCRM::Base.basic_auth api_token, 'x'
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
require 'capsulecrm/record_not_found'
|
30
|
+
require 'capsulecrm/base'
|
31
|
+
require 'capsulecrm/history'
|
32
|
+
require 'capsulecrm/party'
|
33
|
+
require 'capsulecrm/person'
|
34
|
+
require 'capsulecrm/organisation'
|
35
|
+
require 'capsulecrm/collection'
|
36
|
+
require 'capsulecrm/child_collection'
|
37
|
+
require 'capsulecrm/child'
|
38
|
+
require 'capsulecrm/custom_field'
|
39
|
+
require 'capsulecrm/contact'
|
40
|
+
require 'capsulecrm/email'
|
41
|
+
require 'capsulecrm/phone'
|
42
|
+
require 'capsulecrm/address'
|
43
|
+
require 'capsulecrm/tag'
|
44
|
+
require 'capsulecrm/history_item'
|
45
|
+
require 'capsulecrm/website'
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class CapsuleCRM::Address < CapsuleCRM::Contact
|
2
|
+
|
3
|
+
attr_accessor :street
|
4
|
+
attr_accessor :city
|
5
|
+
attr_accessor :state
|
6
|
+
attr_accessor :zip
|
7
|
+
attr_accessor :country
|
8
|
+
|
9
|
+
|
10
|
+
# nodoc
|
11
|
+
def self.xml_map
|
12
|
+
map = {
|
13
|
+
'street' => 'street',
|
14
|
+
'city' => 'city',
|
15
|
+
'state' => 'state',
|
16
|
+
'zip' => 'zip',
|
17
|
+
'country' => 'country'
|
18
|
+
}
|
19
|
+
super.merge map
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
module CapsuleCRM
|
2
|
+
class Base
|
3
|
+
include HTTParty
|
4
|
+
include ActiveModel::Dirty
|
5
|
+
|
6
|
+
|
7
|
+
# This is needed for PUT and POST requests because the capsule API returns html
|
8
|
+
# if the request is unsuccessful. HTTParty's default xlm parser crashes if fed
|
9
|
+
# html so this parser is used for PUT/POST
|
10
|
+
class Parser::Simple < HTTParty::Parser
|
11
|
+
def parse; body; end
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
# -- Attributes --
|
16
|
+
attr_accessor :id
|
17
|
+
attr_accessor :raw_data
|
18
|
+
@@last_response = nil
|
19
|
+
|
20
|
+
|
21
|
+
# -- HttpParty --
|
22
|
+
format :xml
|
23
|
+
headers 'User-Agent' => 'CapsuleCRM ruby gem'
|
24
|
+
|
25
|
+
|
26
|
+
# nodoc
|
27
|
+
def initialize(attributes={})
|
28
|
+
attributes.each do |name, value|
|
29
|
+
send("#{name}=", value)
|
30
|
+
end
|
31
|
+
changed_attributes.clear
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
# nodoc
|
36
|
+
def ==(other)
|
37
|
+
return false if other.nil?
|
38
|
+
id == other.id
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
# nodoc
|
43
|
+
def id
|
44
|
+
return nil if @id.nil?
|
45
|
+
@id.to_i
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
# nodoc
|
50
|
+
def errors
|
51
|
+
@errors ||= []
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
# nodoc
|
56
|
+
def new_record?
|
57
|
+
id.nil?
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
# -- Class Methods --
|
62
|
+
|
63
|
+
|
64
|
+
# nodoc
|
65
|
+
def self.find(what, options={})
|
66
|
+
return find_all(options) if what == :all
|
67
|
+
find_one(what)
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
# for debugging
|
72
|
+
def self.last_response
|
73
|
+
@@last_response
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
|
80
|
+
# uses xml_map() to convert the xml hash from response.data into an attributes hash
|
81
|
+
def self.attributes_from_xml_hash(hash)
|
82
|
+
attributes = {:raw_data => hash}
|
83
|
+
xml_map.each { |k,v| attributes[v] = hash[k] }
|
84
|
+
attributes
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
# uses xml_map() to convert :attributes into an xml string
|
89
|
+
def self.attributes_to_xml(attributes, root=nil)
|
90
|
+
xml = {}
|
91
|
+
map = xml_map.invert
|
92
|
+
attributes.each do |k,v|
|
93
|
+
key = map[k.to_s]
|
94
|
+
xml[key] = v
|
95
|
+
end
|
96
|
+
xml.to_xml :root => root
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
# nodoc
|
101
|
+
def self.find_one(id)
|
102
|
+
path = get_path + '/' + id.to_s
|
103
|
+
@@last_response = get(path)
|
104
|
+
raise_404(id) if last_response.code == 404
|
105
|
+
init_one(last_response)
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
# nodoc
|
110
|
+
def self.find_all(options={}, path=nil)
|
111
|
+
path ||= get_path
|
112
|
+
params = query_params(options)
|
113
|
+
@@last_response = get(path, :query => params)
|
114
|
+
init_many(last_response)
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
# over-ride in sub-classes
|
119
|
+
def self.get_path
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
# capsule API uses :start. :offset is prob more familiar to ruby/active_record users
|
124
|
+
def self.query_params(options)
|
125
|
+
params = options.dup
|
126
|
+
params[:start] = params.delete(:offset) if params.has_key?(:offset)
|
127
|
+
params
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
# nodoc
|
132
|
+
def self.raise_404(id)
|
133
|
+
err = "Could not find #{name} with id #{id}"
|
134
|
+
raise CapsuleCRM::RecordNotFound, err
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
# creates a new object, and returns the ID
|
139
|
+
# returns false if something went wrong (use last_response() to debug)
|
140
|
+
def self.create(attributes, options={})
|
141
|
+
return false if attributes.empty?
|
142
|
+
xml = attributes_to_xml(attributes, options.delete(:root))
|
143
|
+
@@last_response = post options[:path], xml_request_options(xml)
|
144
|
+
return false unless last_response.code == 201
|
145
|
+
last_response.headers['location'].split('/').last
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
# updates an object with the given id, returns true on success, false
|
150
|
+
# on failure.
|
151
|
+
def self.update(id, attributes, options={})
|
152
|
+
return true if attributes.empty?
|
153
|
+
xml = attributes_to_xml(attributes, options.delete(:root))
|
154
|
+
@@last_response = put options[:path], xml_request_options(xml)
|
155
|
+
last_response.code == 200
|
156
|
+
end
|
157
|
+
|
158
|
+
|
159
|
+
# nodoc
|
160
|
+
def self.xml_map
|
161
|
+
{'id' => 'id'}
|
162
|
+
end
|
163
|
+
|
164
|
+
|
165
|
+
# needed for PUT and POST operations
|
166
|
+
def self.xml_request_options(xml)
|
167
|
+
options = {
|
168
|
+
:body => xml,
|
169
|
+
:headers => {'Content-Type' => 'text/xml'},
|
170
|
+
:parser => Parser::Simple
|
171
|
+
}
|
172
|
+
end
|
173
|
+
|
174
|
+
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class CapsuleCRM::Child < CapsuleCRM::Base
|
2
|
+
attr_accessor :parent
|
3
|
+
|
4
|
+
|
5
|
+
# nodoc
|
6
|
+
def initialize(parent, attributes={})
|
7
|
+
@parent = parent
|
8
|
+
super(attributes)
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
# nodoc
|
13
|
+
def self.init_many(parent, data)
|
14
|
+
CapsuleCRM::ChildCollection.new(parent, self, data)
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
# nodoc
|
19
|
+
def self.init_one(parent, data)
|
20
|
+
new(parent, attributes_from_xml_map(data))
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class CapsuleCRM::ChildCollection < CapsuleCRM::Collection
|
2
|
+
|
3
|
+
|
4
|
+
# nodoc
|
5
|
+
def initialize(parent, klass, data)
|
6
|
+
return if data.nil?
|
7
|
+
[data].flatten.each do |attributes|
|
8
|
+
attributes = klass.attributes_from_xml_hash(attributes)
|
9
|
+
self.push klass.new(parent, attributes)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
end
|