capsulecrmii 0.0.5
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.
- 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
@@ -0,0 +1,14 @@
|
|
1
|
+
class CapsuleCRM::Collection < Array
|
2
|
+
|
3
|
+
|
4
|
+
# nodoc
|
5
|
+
def initialize(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(attributes)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class CapsuleCRM::Contact < CapsuleCRM::Child
|
2
|
+
|
3
|
+
attr_accessor :type
|
4
|
+
define_attribute_methods [:type]
|
5
|
+
|
6
|
+
# nodoc
|
7
|
+
def attributes
|
8
|
+
attrs = {}
|
9
|
+
arr = [:type]
|
10
|
+
arr.each do |key|
|
11
|
+
attrs[key] = self.send(key)
|
12
|
+
end
|
13
|
+
attrs
|
14
|
+
end
|
15
|
+
|
16
|
+
# nodoc
|
17
|
+
def type=(value)
|
18
|
+
type_will_change! unless value == type
|
19
|
+
@type = value
|
20
|
+
end
|
21
|
+
|
22
|
+
# nodoc
|
23
|
+
def self.xml_map
|
24
|
+
map = {'type' => 'type'}
|
25
|
+
super.merge map
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class CapsuleCRM::CustomField < CapsuleCRM::Child
|
2
|
+
|
3
|
+
attr_accessor :boolean
|
4
|
+
attr_accessor :date
|
5
|
+
attr_accessor :label
|
6
|
+
attr_accessor :text
|
7
|
+
|
8
|
+
|
9
|
+
# nodoc
|
10
|
+
def boolean=(value)
|
11
|
+
return @boolean = true if value.to_s == 'true'
|
12
|
+
@boolean = false
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
# nodoc
|
17
|
+
def date=(value)
|
18
|
+
value = Time.parse(value) if value.is_a?(String)
|
19
|
+
@date = value
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
# nodoc
|
24
|
+
def value
|
25
|
+
date || text || boolean
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
# nodoc
|
30
|
+
def self.xml_map
|
31
|
+
map = {
|
32
|
+
'label' => 'label',
|
33
|
+
'text' => 'text',
|
34
|
+
'date' => 'date',
|
35
|
+
'boolean' => 'boolean'
|
36
|
+
}
|
37
|
+
super.merge map
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
class CapsuleCRM::Email < CapsuleCRM::Contact
|
2
|
+
|
3
|
+
attr_accessor :address
|
4
|
+
define_attribute_methods [:address]
|
5
|
+
|
6
|
+
|
7
|
+
# nodoc
|
8
|
+
def attributes
|
9
|
+
attrs = {}
|
10
|
+
arr = [:address, :type]
|
11
|
+
arr.each do |key|
|
12
|
+
attrs[key] = self.send(key)
|
13
|
+
end
|
14
|
+
attrs
|
15
|
+
end
|
16
|
+
|
17
|
+
# nodoc
|
18
|
+
def address=(value)
|
19
|
+
address_will_change! unless value == address
|
20
|
+
@address = value
|
21
|
+
end
|
22
|
+
|
23
|
+
# nodoc
|
24
|
+
def dirty_attributes
|
25
|
+
Hash[attributes.select { |k,v| changed.include? k.to_s }]
|
26
|
+
end
|
27
|
+
|
28
|
+
def parent_type
|
29
|
+
return "person" if self.parent.class == CapsuleCRM::Person
|
30
|
+
return "organisation" if self.parent.class == CapsuleCRM::Organisation
|
31
|
+
raise "Unknown Parent Type"
|
32
|
+
end
|
33
|
+
|
34
|
+
# nodoc
|
35
|
+
def save
|
36
|
+
path = ["", "api", parent_type, self.parent.id].join("/")
|
37
|
+
options = {:root => 'person', :path => path}
|
38
|
+
attrs = new_record?? attributes : {:id => id}.merge(dirty_attributes)
|
39
|
+
success = self.class.update id, attrs, options
|
40
|
+
changed_attributes.clear if success
|
41
|
+
success
|
42
|
+
end
|
43
|
+
|
44
|
+
# uses xml_map() to convert :attributes into an xml string
|
45
|
+
def self.attributes_to_xml(attributes, root=nil)
|
46
|
+
xml = {"contacts" => {"email" => {}}}
|
47
|
+
map = xml_map.invert
|
48
|
+
attributes.each do |k,v|
|
49
|
+
key = map[k.to_s]
|
50
|
+
xml["contacts"]["email"][key] = v
|
51
|
+
end
|
52
|
+
xml.to_xml :root => root
|
53
|
+
end
|
54
|
+
|
55
|
+
# nodoc
|
56
|
+
def self.xml_map
|
57
|
+
map = {
|
58
|
+
'emailAddress' => 'address'
|
59
|
+
}
|
60
|
+
super.merge map
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module CapsuleCRM::History
|
2
|
+
# Reload history
|
3
|
+
def history!
|
4
|
+
@history = nil
|
5
|
+
history
|
6
|
+
end
|
7
|
+
|
8
|
+
# Load history if not loaded
|
9
|
+
def history
|
10
|
+
return @history if @history
|
11
|
+
|
12
|
+
path = self.class.get_path
|
13
|
+
path = [path, id, 'history'].join '/'
|
14
|
+
last_response = self.class.get(path)
|
15
|
+
data = last_response['history'].try(:[], 'historyItem')
|
16
|
+
@history = CapsuleCRM::HistoryItem.init_many(self, data)
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def add_history(note)
|
21
|
+
if note
|
22
|
+
path = [self.class.get_path, self.id, 'history'].join '/'
|
23
|
+
self.class.create(Hash[{:note => note}], {:root => 'historyItem', :path => path})
|
24
|
+
|
25
|
+
# TODO : Should be optimized so it doesn't reload history each time
|
26
|
+
@history = nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
alias :add_note :add_history
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class CapsuleCRM::HistoryItem < CapsuleCRM::Child
|
2
|
+
attr_reader :id
|
3
|
+
attr_accessor :type
|
4
|
+
attr_accessor :entry_date
|
5
|
+
attr_accessor :creator
|
6
|
+
attr_accessor :subject
|
7
|
+
attr_accessor :note
|
8
|
+
|
9
|
+
def self.xml_map
|
10
|
+
map = {
|
11
|
+
"id" => 'id',
|
12
|
+
"type" => 'type',
|
13
|
+
"entryDate" => 'entry_date',
|
14
|
+
"creator" => 'creator',
|
15
|
+
"subject" => 'subject',
|
16
|
+
"note" => 'note'
|
17
|
+
}
|
18
|
+
super.merge map
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class CapsuleCRM::Organisation < CapsuleCRM::Party
|
2
|
+
|
3
|
+
attr_accessor :about
|
4
|
+
attr_accessor :name
|
5
|
+
|
6
|
+
|
7
|
+
# nodoc
|
8
|
+
def people
|
9
|
+
return @people if @people
|
10
|
+
path = self.class.get_path
|
11
|
+
path = [path, '/', id, '/people'].join
|
12
|
+
last_response = self.class.get(path)
|
13
|
+
@people = CapsuleCRM::Person.init_many(last_response)
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
# nodoc
|
18
|
+
def self.init_many(response)
|
19
|
+
data = response['parties']['organisation']
|
20
|
+
CapsuleCRM::Collection.new(self, data)
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
# nodoc
|
25
|
+
def self.init_one(response)
|
26
|
+
data = response['organisation']
|
27
|
+
new(attributes_from_xml_hash(data))
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
# nodoc
|
32
|
+
def self.xml_map
|
33
|
+
map = {
|
34
|
+
'about' => 'about',
|
35
|
+
'name' => 'name'
|
36
|
+
}
|
37
|
+
super.merge map
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
class CapsuleCRM::Party < CapsuleCRM::Base
|
2
|
+
include CapsuleCRM::History
|
3
|
+
|
4
|
+
# nodoc
|
5
|
+
def addresses
|
6
|
+
return @addresses if @addresses
|
7
|
+
data = raw_data['contacts'].try(:[], 'address')
|
8
|
+
@addresses = CapsuleCRM::Address.init_many(self, data)
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
# nodoc
|
13
|
+
def custom_fields
|
14
|
+
return @custom_fields if @custom_fields
|
15
|
+
path = self.class.get_path
|
16
|
+
path = [path, '/', id, '/customfield'].join
|
17
|
+
last_response = self.class.get(path)
|
18
|
+
data = last_response['customFields'].try(:[], 'customField')
|
19
|
+
@custom_fields = CapsuleCRM::CustomField.init_many(self, data)
|
20
|
+
end
|
21
|
+
|
22
|
+
def tags
|
23
|
+
return @tags if @tags
|
24
|
+
path = self.class.get_path
|
25
|
+
path = [path, '/', id, '/tag'].join
|
26
|
+
last_response = self.class.get(path)
|
27
|
+
data = last_response['tags'].try(:[], 'tag')
|
28
|
+
@tags = CapsuleCRM::Tag.init_many(self, data)
|
29
|
+
end
|
30
|
+
|
31
|
+
def tag_names
|
32
|
+
tags.map(&:name)
|
33
|
+
end
|
34
|
+
|
35
|
+
# nodoc
|
36
|
+
def emails
|
37
|
+
return @emails if @emails
|
38
|
+
data = raw_data['contacts'].try(:[], 'email')
|
39
|
+
@emails = CapsuleCRM::Email.init_many(self, data)
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
# nodoc
|
44
|
+
def phone_numbers
|
45
|
+
return @phone_numbers if @phone_numbers
|
46
|
+
data = raw_data['contacts'].try(:[], 'phone')
|
47
|
+
@phone_numbers = CapsuleCRM::Phone.init_many(self, data)
|
48
|
+
end
|
49
|
+
|
50
|
+
# nodoc
|
51
|
+
def websites
|
52
|
+
return @websites if @websites
|
53
|
+
data = raw_data['contacts'].try(:[], 'website')
|
54
|
+
@websites = CapsuleCRM::Website.init_many(self, data)
|
55
|
+
end
|
56
|
+
|
57
|
+
def is?(kind)
|
58
|
+
required_class = kind.to_s.camelize
|
59
|
+
self.class.to_s.include? required_class
|
60
|
+
end
|
61
|
+
|
62
|
+
def tag(value)
|
63
|
+
# unset tags so that if anyone were to request tags again, it
|
64
|
+
# requests an update from the server.
|
65
|
+
@tags = nil
|
66
|
+
path = self.class.get_path
|
67
|
+
tag = URI.escape(value.to_s)
|
68
|
+
path = [path, id, 'tag', tag].join('/')
|
69
|
+
req = self.class.post(path)
|
70
|
+
req.response.code == ("201" || "200")
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
def untag(value)
|
75
|
+
# unset tags so that if anyone were to request tags again, it
|
76
|
+
# requests an update from the server.
|
77
|
+
@tags = nil
|
78
|
+
path = self.class.get_path
|
79
|
+
tag = URI.escape(value.to_s)
|
80
|
+
path = [path, id, 'tag', tag].join('/')
|
81
|
+
req = self.class.delete(path)
|
82
|
+
req.response.code == "200"
|
83
|
+
end
|
84
|
+
|
85
|
+
# nodoc
|
86
|
+
def self.get_path
|
87
|
+
'/api/party'
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
def self.find_all_by_email(email, options={})
|
92
|
+
options[:email] = email
|
93
|
+
find_all(options)
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
# nodoc
|
98
|
+
def self.find_by_email(email)
|
99
|
+
find_all_by_email(email, :limit => 1, :offset => 0).first
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
# nodoc
|
104
|
+
def self.search(query, options={})
|
105
|
+
options[:q] = query
|
106
|
+
find_all(options)
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.init_one(response)
|
110
|
+
return CapsuleCRM::Person.init_one(response) if response['person']
|
111
|
+
return CapsuleCRM::Organisation.init_one(response) if response['organisation']
|
112
|
+
raise CapsuleCRM::RecordNotRecognised, "Could not recognise returned entity type: #{response}"
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
class CapsuleCRM::Person < CapsuleCRM::Party
|
2
|
+
|
3
|
+
attr_accessor :about
|
4
|
+
attr_accessor :first_name
|
5
|
+
attr_accessor :job_title
|
6
|
+
attr_accessor :last_name
|
7
|
+
attr_accessor :organisation_id
|
8
|
+
attr_accessor :title
|
9
|
+
attr_accessor :note
|
10
|
+
|
11
|
+
define_attribute_methods [:about, :first_name, :last_name, :job_title, :organisation_id, :title]
|
12
|
+
|
13
|
+
|
14
|
+
# nodoc
|
15
|
+
def attributes
|
16
|
+
attrs = {}
|
17
|
+
arr = [:about, :first_name, :last_name, :title, :job_title]
|
18
|
+
arr.each do |key|
|
19
|
+
attrs[key] = self.send(key)
|
20
|
+
end
|
21
|
+
attrs
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
# nodoc
|
26
|
+
def first_name=(value)
|
27
|
+
first_name_will_change! unless value == first_name
|
28
|
+
@first_name = value
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
# nodoc
|
33
|
+
def last_name=(value)
|
34
|
+
last_name_will_change! unless value == last_name
|
35
|
+
@last_name = value
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
# nodoc
|
40
|
+
def title=(value)
|
41
|
+
title_will_change! unless value == title
|
42
|
+
@title = value
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
# nodoc
|
47
|
+
def organisation
|
48
|
+
return nil if organisation_id.nil?
|
49
|
+
@organisation ||= CapsuleCRM::Organisation.find(organisation_id)
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
# nodoc
|
54
|
+
def save
|
55
|
+
new_record?? create : update
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
|
62
|
+
# nodoc
|
63
|
+
def create
|
64
|
+
path = '/api/person'
|
65
|
+
options = {:root => 'person', :path => path}
|
66
|
+
new_id = self.class.create dirty_attributes, options
|
67
|
+
unless new_id
|
68
|
+
errors << self.class.last_response.response.message
|
69
|
+
return false
|
70
|
+
end
|
71
|
+
@errors = []
|
72
|
+
changed_attributes.clear
|
73
|
+
self.id = new_id
|
74
|
+
self
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
# nodoc
|
79
|
+
def dirty_attributes
|
80
|
+
Hash[attributes.select { |k,v| changed.include? k.to_s }]
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
# nodoc
|
85
|
+
def update
|
86
|
+
path = '/api/person/' + id.to_s
|
87
|
+
options = {:root => 'person', :path => path}
|
88
|
+
success = self.class.update id, dirty_attributes, options
|
89
|
+
changed_attributes.clear if success
|
90
|
+
success
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
# -- Class methods --
|
95
|
+
|
96
|
+
|
97
|
+
# nodoc
|
98
|
+
def self.init_many(response)
|
99
|
+
data = response['parties']['person']
|
100
|
+
CapsuleCRM::Collection.new(self, data)
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
# nodoc
|
105
|
+
def self.init_one(response)
|
106
|
+
data = response['person']
|
107
|
+
new(attributes_from_xml_hash(data))
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
# nodoc
|
112
|
+
def self.xml_map
|
113
|
+
map = {
|
114
|
+
'about' => 'about',
|
115
|
+
'firstName' => 'first_name',
|
116
|
+
'jobTitle' => 'job_title',
|
117
|
+
'lastName' => 'last_name',
|
118
|
+
'organisationId' => 'organisation_id',
|
119
|
+
'title' => 'title',
|
120
|
+
'note' => 'note'
|
121
|
+
}
|
122
|
+
super.merge map
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
end
|