capsulecrm 0.0.1

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 ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in capsulecrm.gemspec
4
+ gemspec
data/README ADDED
@@ -0,0 +1,29 @@
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 'capculecrm' # 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.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'bundler'
2
+ require 'rake/testtask'
3
+ Bundler::GemHelper.install_tasks
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << "test"
7
+ t.test_files = FileList['test/*_test.rb']
8
+ t.verbose = true
9
+ end
@@ -0,0 +1,25 @@
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 = "capsulecrm"
7
+ s.version = CapsuleCRM::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Ahmed Adam"]
10
+ s.email = ["ahmed.msgs@gmail.com"]
11
+ s.homepage = ""
12
+ s.summary = %q{CapsuleCRM API Gem}
13
+ s.description = %q{CapsuleCRM API Gem}
14
+
15
+ s.add_dependency 'httparty', '>= 0.7.4'
16
+ s.add_dependency 'activemodel', '>= 3.0.5'
17
+ s.add_dependency 'activesupport', '>= 3.0.5'
18
+
19
+ s.rubyforge_project = "capsulecrm"
20
+
21
+ s.files = `git ls-files`.split("\n")
22
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
23
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
24
+ s.require_paths = ["lib"]
25
+ end
data/examples.rb ADDED
@@ -0,0 +1,65 @@
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
+ # find by email
8
+ person = CapsuleCRM::Person.find_by_email 'foo@example.com'
9
+
10
+ # find all
11
+ person = CapsuleCRM::Person.find :all, :offset => 10, :limit => 5
12
+
13
+ # find by search term (searches name, telephone number and searchable custom fields)
14
+ person = CapsuleCRM::Person.search 'fred', :offset => 10, :limit => 5
15
+
16
+ # CapsuleCRM::Person attributes
17
+ person.id
18
+ person.title
19
+ person.first_name
20
+ person.last_name
21
+ person.job_title
22
+ person.about
23
+
24
+ # update a person object
25
+ person.first_name = "Homer"
26
+ person.last_name = "Simpson"
27
+ person.save # returns true/false
28
+
29
+ # create a person object
30
+ person = CapsuleCRM::Person.new
31
+ person.first_name = "Marge"
32
+ person.last_name = "Simpson"
33
+ person.save # returns true/false
34
+
35
+
36
+ # get the person's organisation
37
+ person.organisation # CapsuleCRM::Organisation
38
+
39
+ # CapsuleCRM::Organisation attributes:
40
+ organisation.about
41
+ organisation.name
42
+
43
+
44
+ # Contacts: CapsuleCRM::Phone (read-only)
45
+ person.phone_numbers # CapsuleCRM::Collection
46
+ person.phone_numbers.first.number # 01234 56789
47
+ person.phone_numbers.first.type # work
48
+
49
+ # Contacts: CapsuleCRM::Email (read-only)
50
+ person.emails # CapsuleCRM::Collection
51
+ person.emails.first.address # 'foo@example.com'
52
+ person.emails.first.type # 'home'
53
+
54
+ # Contacts: CapsuleCRM::Address (read-only)
55
+ person.addresses # CapsuleCRM::Collection
56
+ person.addresses.first.street # 10 Somestreet
57
+ person.addresses.first.city # Manchester
58
+ person.addresses.first.state # Greater Manchester
59
+ person.addresses.first.zip # ME10 7TR
60
+ person.addresses.first.country # United Kingdom
61
+
62
+ # CapsuleCRM::CustomFields (read-only)
63
+ person.custom_fields # CapsuleCRM::Collection
64
+ person.custom_fields.first.label # 'Favourite colour'
65
+ person.custom_fields.first.value # 'Blue'
data/lib/capsulecrm.rb ADDED
@@ -0,0 +1,40 @@
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
+ module CapsuleCRM
7
+
8
+ mattr_accessor :account_name
9
+ mattr_accessor :api_token
10
+
11
+ # nodoc
12
+ def self.base_uri(account_name)
13
+ "https://#{account_name}.capsulecrm.com"
14
+ end
15
+
16
+
17
+ # nodoc
18
+ def self.initialize!
19
+ raise ArgumentError, "CapsuleCRM.account_name not defined" if account_name.nil?
20
+ raise ArgumentError, "CapsuleCRM.api_token not defined" if api_token.nil?
21
+ CapsuleCRM::Base.base_uri base_uri(account_name)
22
+ CapsuleCRM::Base.basic_auth api_token, 'x'
23
+ end
24
+
25
+
26
+ end
27
+
28
+ require 'capsulecrm/record_not_found'
29
+ require 'capsulecrm/base'
30
+ require 'capsulecrm/party'
31
+ require 'capsulecrm/person'
32
+ require 'capsulecrm/organisation'
33
+ require 'capsulecrm/collection'
34
+ require 'capsulecrm/child_collection'
35
+ require 'capsulecrm/child'
36
+ require 'capsulecrm/custom_field'
37
+ require 'capsulecrm/contact'
38
+ require 'capsulecrm/email'
39
+ require 'capsulecrm/phone'
40
+ require 'capsulecrm/address'
@@ -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,175 @@
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={})
111
+ params = query_params(options)
112
+ @@last_response = get(get_path, :query => params)
113
+ init_many(last_response)
114
+ end
115
+
116
+
117
+ # over-ride in sub-classes
118
+ def self.get_path
119
+ end
120
+
121
+
122
+ # capsule API uses :start. :offset is prob more familiar to ruby/active_record users
123
+ def self.query_params(options)
124
+ params = options.dup
125
+ params[:start] = params.delete(:offset) if params.has_key?(:offset)
126
+ params
127
+ end
128
+
129
+
130
+ # nodoc
131
+ def self.raise_404(id)
132
+ err = "Could not find #{name} with id #{id}"
133
+ raise CapsuleCRM::RecordNotFound, err
134
+ end
135
+
136
+
137
+ # creates a new object, and returns the ID
138
+ # returns false if something went wrong (use last_response() to debug)
139
+ def self.create(attributes, options={})
140
+ return false if attributes.empty?
141
+ xml = attributes_to_xml(attributes, options.delete(:root))
142
+ @@last_response = post options[:path], xml_request_options(xml)
143
+ return false unless last_response.code == 201
144
+ last_response.headers['location'].split('/').last
145
+ end
146
+
147
+
148
+ # updates an object with the given id, returns true on success, false
149
+ # on failure.
150
+ def self.update(id, attributes, options={})
151
+ return true if attributes.empty?
152
+ xml = attributes_to_xml(attributes, options.delete(:root))
153
+ @@last_response = put options[:path], xml_request_options(xml)
154
+ last_response.code == 200
155
+ end
156
+
157
+
158
+ # nodoc
159
+ def self.xml_map
160
+ {'id' => 'id'}
161
+ end
162
+
163
+
164
+ # needed for PUT and POST operations
165
+ def self.xml_request_options(xml)
166
+ options = {
167
+ :body => xml,
168
+ :headers => {'Content-Type' => 'text/xml'},
169
+ :parser => Parser::Simple
170
+ }
171
+ end
172
+
173
+
174
+ end
175
+ 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
@@ -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