capsulecrm 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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