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.
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