capsulecrmii 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/.gitignore +4 -0
  2. data/Gemfile +10 -0
  3. data/README.rdoc +33 -0
  4. data/Rakefile +9 -0
  5. data/capsulecrm.gemspec +23 -0
  6. data/examples.rb +82 -0
  7. data/lib/capsulecrm.rb +45 -0
  8. data/lib/capsulecrm/address.rb +23 -0
  9. data/lib/capsulecrm/base.rb +176 -0
  10. data/lib/capsulecrm/child.rb +24 -0
  11. data/lib/capsulecrm/child_collection.rb +14 -0
  12. data/lib/capsulecrm/collection.rb +14 -0
  13. data/lib/capsulecrm/contact.rb +27 -0
  14. data/lib/capsulecrm/custom_field.rb +41 -0
  15. data/lib/capsulecrm/email.rb +64 -0
  16. data/lib/capsulecrm/history.rb +32 -0
  17. data/lib/capsulecrm/history_item.rb +20 -0
  18. data/lib/capsulecrm/organisation.rb +41 -0
  19. data/lib/capsulecrm/party.rb +114 -0
  20. data/lib/capsulecrm/person.rb +126 -0
  21. data/lib/capsulecrm/phone.rb +15 -0
  22. data/lib/capsulecrm/record_not_found.rb +1 -0
  23. data/lib/capsulecrm/recorn_not_recognised.rb +1 -0
  24. data/lib/capsulecrm/tag.rb +12 -0
  25. data/lib/capsulecrm/version.rb +3 -0
  26. data/lib/capsulecrm/website.rb +19 -0
  27. data/test/create_person_test.rb +36 -0
  28. data/test/fixtures/responses/create_person.yml +59 -0
  29. data/test/fixtures/responses/party_history.yml +45 -0
  30. data/test/fixtures/responses/party_tags.yml +26 -0
  31. data/test/fixtures/responses/person_find_all.yml +28 -0
  32. data/test/fixtures/responses/person_find_all_with_limit.yml +26 -0
  33. data/test/fixtures/responses/person_find_all_with_offset.yml +28 -0
  34. data/test/fixtures/responses/person_find_by_id.yml +102 -0
  35. data/test/fixtures/responses/update_person.yml +85 -0
  36. data/test/fixtures/responses/update_person_without_changes.yml +28 -0
  37. data/test/party_dot_find_test.rb +40 -0
  38. data/test/party_dot_history_test.rb +29 -0
  39. data/test/party_dot_tags_test.rb +30 -0
  40. data/test/person_dot_find_all_test.rb +48 -0
  41. data/test/person_dot_find_by_id_test.rb +61 -0
  42. data/test/test_helper.rb +52 -0
  43. data/test/update_person_test.rb +46 -0
  44. metadata +131 -0
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in capsulecrm.gemspec
4
+ gemspec
5
+
6
+ group :development do
7
+ gem 'webmock'
8
+ gem 'vcr'
9
+ end
10
+
@@ -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
@@ -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,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
@@ -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"]
@@ -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