civicrm 1.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.
@@ -0,0 +1,2 @@
1
+ /pkg
2
+ .rvmrc
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
6
+ - 2.0.0
@@ -0,0 +1 @@
1
+ Iskander Haziev <gvalmon@gmail.com>
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "https://rubygems.org"
2
+ gemspec
@@ -0,0 +1,47 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ civicrm (1.0.1)
5
+ activemodel
6
+ activesupport
7
+ nokogiri
8
+ rest-client (~> 1.4)
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ activemodel (3.2.13)
14
+ activesupport (= 3.2.13)
15
+ builder (~> 3.0.0)
16
+ activesupport (3.2.13)
17
+ i18n (= 0.6.1)
18
+ multi_json (~> 1.0)
19
+ builder (3.0.4)
20
+ diff-lcs (1.2.1)
21
+ i18n (0.6.1)
22
+ metaclass (0.0.1)
23
+ mime-types (1.22)
24
+ mocha (0.10.5)
25
+ metaclass (~> 0.0.1)
26
+ multi_json (1.7.2)
27
+ nokogiri (1.5.9)
28
+ rake (10.0.3)
29
+ rest-client (1.6.7)
30
+ mime-types (>= 1.16)
31
+ rspec (2.13.0)
32
+ rspec-core (~> 2.13.0)
33
+ rspec-expectations (~> 2.13.0)
34
+ rspec-mocks (~> 2.13.0)
35
+ rspec-core (2.13.1)
36
+ rspec-expectations (2.13.0)
37
+ diff-lcs (>= 1.1.3, < 2.0)
38
+ rspec-mocks (2.13.0)
39
+
40
+ PLATFORMS
41
+ ruby
42
+
43
+ DEPENDENCIES
44
+ civicrm!
45
+ mocha
46
+ rake
47
+ rspec
@@ -0,0 +1,11 @@
1
+ === 1.0 2013-04-8
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
5
+
6
+ === 1.1 2013-05-8
7
+
8
+ * 3 minor enhancements:
9
+ * Fix gem specs
10
+ * added CiviCrm::XML.encode method
11
+ * CiviCrm::Client.request not accepts only 2 params, "path" option is removed
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2013 CiviCrm.com. (https://civicrm.com)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,31 @@
1
+ ## Installation
2
+
3
+ * $ gem install civicrm
4
+
5
+ ## Getting started
6
+
7
+ ```ruby
8
+ * CiviCrm.site_key = 'YOUR_SITE_KEY'
9
+ * // More info about site_key:
10
+ * // http://wiki.civicrm.org/confluence/display/CRMDOC43/Managing+Scheduled+Jobs
11
+ * CiviCrm.api_base = 'https://www.example.org/path/to/civi/codebase/'
12
+ * CiviCrm.authenticate('demo', 'demo')
13
+ ```
14
+
15
+ ## CiviCrm Objects
16
+
17
+ ```ruby
18
+ * CiviCrm::Contact.all # get list of contacts
19
+ * CiviCrm::Contact.create(contact_type: 'Organization', organization_name: 'test') # create contact
20
+ * CiviCrm::Contact.find(1).delete # find and delete
21
+ ```
22
+
23
+ ## Testing
24
+ ```
25
+ rspec spec
26
+ ```
27
+
28
+ ## Useful links
29
+
30
+ * http://wiki.civicrm.org/confluence/display/CRMDOC43/REST+interface
31
+ * http://drupal.demo.civicrm.org/civicrm/api/explorer
@@ -0,0 +1,12 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rake'
5
+ require 'rspec/core/rake_task'
6
+
7
+ desc "Run all examples"
8
+ RSpec::Core::RakeTask.new(:spec) do |t|
9
+ t.rspec_opts = %w[--color]
10
+ end
11
+
12
+ task :default => [:spec]
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
3
+
4
+ libs = " -r irb/completion"
5
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/civicrm'}"
6
+ puts "Loading CiviCrm gem"
7
+ exec "#{irb} #{libs} --simple-prompt"
@@ -0,0 +1,29 @@
1
+ $:.unshift(File.join(File.dirname(__FILE__), 'lib'))
2
+
3
+ require 'civicrm/version'
4
+
5
+ spec = Gem::Specification.new do |s|
6
+ s.name = 'civicrm'
7
+ s.version = CiviCrm::VERSION
8
+ s.summary = 'Ruby bindings for the CiviCRM API'
9
+ s.description = 'See http://civicrm.org for details.'
10
+ s.authors = ['Iskander Haziev']
11
+ s.email = ['gvalmon@gmail.com']
12
+ s.homepage = 'http://civicrm.org'
13
+ s.executables = 'civicrm'
14
+ s.require_paths = %w{lib}
15
+
16
+ s.add_dependency('rest-client', '~> 1.4')
17
+ s.add_dependency('activesupport')
18
+ s.add_dependency('activemodel')
19
+ s.add_dependency('nokogiri')
20
+
21
+ s.add_development_dependency('rake')
22
+ s.add_development_dependency('rspec')
23
+ s.add_development_dependency('mocha')
24
+
25
+ s.files = `git ls-files`.split("\n")
26
+ s.test_files = `git ls-files -- test/*`.split("\n")
27
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
28
+ s.require_paths = ['lib']
29
+ end
@@ -0,0 +1,47 @@
1
+ require 'active_support/deprecation'
2
+ require 'active_support/core_ext/module'
3
+ require 'active_support/core_ext/hash'
4
+ require 'active_support/inflector'
5
+ require 'rest-client'
6
+ require 'nokogiri'
7
+
8
+ # utils
9
+ require 'civicrm/client'
10
+ require 'civicrm/xml'
11
+ require 'civicrm/resource'
12
+ require 'civicrm/version'
13
+
14
+ # actions
15
+ require 'civicrm/actions/list'
16
+ require 'civicrm/actions/create'
17
+ require 'civicrm/actions/update'
18
+ require 'civicrm/actions/destroy'
19
+ require 'civicrm/actions/find'
20
+
21
+ # exceptions
22
+ require 'civicrm/errors'
23
+
24
+ # resources
25
+ require 'civicrm/resources/base'
26
+
27
+ module CiviCrm
28
+ @@api_key = nil
29
+ @@site_key = nil
30
+ @@api_base = 'https://www.example.org/path/to/civi/codebase'
31
+ @@api_version = 'v3'
32
+ @@user_authenticated = false
33
+
34
+ mattr_accessor :api_key, :api_base, :api_version, :site_key
35
+
36
+ def self.api_url(path = '')
37
+ base = "#{api_base}/civicrm/extern/rest.php?#{path}"
38
+ base += "&api_key=#{@@api_key}" if @@api_key
39
+ base += "&key=#{@@site_key}" if @@site_key
40
+ base
41
+ end
42
+
43
+ def self.authenticate(name, password)
44
+ auth = Client.request(:post, q: 'civicrm/login', name: name, pass: password)
45
+ @@api_key = auth[0]['api_key']
46
+ end
47
+ end
@@ -0,0 +1,17 @@
1
+ module CiviCrm
2
+ module Actions
3
+ module Create
4
+ module ClassMethods
5
+ def create(attrs = {})
6
+ params = {'entity' => entity_class_name, 'action' => 'create'}
7
+ response = CiviCrm::Client.request(:post, params.merge(attrs))
8
+ Resource.build_from(response.first, params)
9
+ end
10
+ end
11
+
12
+ def self.included(base)
13
+ base.extend(ClassMethods)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ module CiviCrm
2
+ module Actions
3
+ module Destroy
4
+ def delete
5
+ params = {'entity' => self.class.entity_class_name, 'action' => 'delete', 'id' => id}
6
+ response = CiviCrm::Client.request(:post, params)
7
+ refresh_from(response.first.to_hash)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+ module CiviCrm
2
+ module Actions
3
+ module Find
4
+ module ClassMethods
5
+ def find(id)
6
+ params = {'entity' => entity_class_name, 'action' => 'getsingle', 'id' => id}
7
+ response = CiviCrm::Client.request(:get, params)
8
+ Resource.build_from(response.first, params)
9
+ end
10
+ end
11
+
12
+ def self.included(base)
13
+ base.extend(ClassMethods)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,29 @@
1
+ module CiviCrm
2
+ module Actions
3
+ module List
4
+ module ClassMethods
5
+ def all(params = {})
6
+ params.merge!('entity' => entity_class_name, 'action' => 'get')
7
+ response = CiviCrm::Client.request(:get, params)
8
+ Resource.build_from(response, params)
9
+ end
10
+
11
+ def count
12
+ all.count
13
+ end
14
+
15
+ def first
16
+ all.first
17
+ end
18
+
19
+ def last
20
+ all.last
21
+ end
22
+ end
23
+
24
+ def self.included(base)
25
+ base.extend(ClassMethods)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,19 @@
1
+ module CiviCrm
2
+ module Actions
3
+ module Update
4
+ # TODO: this method doesn't work yet
5
+ def update(attrs = {})
6
+ params = {'entity' => self.class.entity_class_name, 'action' => 'replace', 'id' => id}
7
+ new_attrs = attributes.merge(attrs)
8
+ response = CiviCrm::Client.request(:post, params.merge(values: new_attrs))
9
+ refresh_from(response.first.to_hash)
10
+ end
11
+
12
+ def save
13
+ @previously_changed = changes
14
+ @changed_attributes.clear
15
+ update()
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,70 @@
1
+ module CiviCrm
2
+ class Client
3
+ class << self
4
+
5
+ # Returns parsed class inherited from CiviCrm::Resource
6
+ def request(method, params = {})
7
+ unless CiviCrm.site_key
8
+ raise CiviCrm::Errors::Unauthorized, "Please specify CiviCrm.site_key"
9
+ end
10
+ headers = {
11
+ :user_agent => "CiviCrm RubyClient/#{CiviCrm::VERSION}"
12
+ }
13
+
14
+ opts = {
15
+ :method => method,
16
+ :timeout => 80,
17
+ :headers => headers
18
+ }
19
+
20
+ # build params
21
+ case method.to_s.downcase.to_sym
22
+ when :get, :head, :delete
23
+ path = params.count > 0 ? stringify_params(params) : ''
24
+ else
25
+ opts[:payload] = stringify_params(params)
26
+ end
27
+ opts[:url] = CiviCrm.api_url(path)
28
+ response = execute(opts)
29
+ body, code = response.body, response.code
30
+ CiviCrm::XML.parse(body)
31
+ end
32
+
33
+ def execute(opts)
34
+ RestClient::Request.execute(opts)
35
+ rescue RuntimeError => e
36
+ case e.http_code.to_i
37
+ when 400
38
+ raise CiviCrm::Errors::BadRequest, e.http_body
39
+ when 401
40
+ raise CiviCrm::Errors::Unauthorized, e.http_body
41
+ when 403
42
+ raise CiviCrm::Errors::Forbidden, e.http_body
43
+ when 404
44
+ raise CiviCrm::Errors::NotFound, e.http_body
45
+ when 500
46
+ raise CiviCrm::Errors::InternalError, e.http_body
47
+ else
48
+ raise e
49
+ end
50
+ end
51
+
52
+ def uri_escape(key)
53
+ URI.escape(key.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
54
+ end
55
+
56
+ def flatten_params(params, parent_key = nil)
57
+ result = []
58
+ params.each do |key, value|
59
+ flatten_key = parent_key ? "#{parent_key}[#{uri_escape(key)}]" : uri_escape(key)
60
+ result += value.is_a?(Hash) ? flatten_params(value, flatten_key) : [[flatten_key, value]]
61
+ end
62
+ result
63
+ end
64
+
65
+ def stringify_params(params)
66
+ flatten_params(params).collect{|key, value| "#{key}=#{uri_escape(value)}"}.join('&')
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,9 @@
1
+ module CiviCrm
2
+ module Errors
3
+ class Unauthorized < Exception; end;
4
+ class InternalError < Exception; end;
5
+ class Forbidden < Exception; end;
6
+ class BadRequest < Exception; end;
7
+ class NotFound < Exception; end;
8
+ end
9
+ end
@@ -0,0 +1,84 @@
1
+ require 'active_model/dirty'
2
+ module CiviCrm
3
+ class Resource
4
+ include ActiveModel::Dirty
5
+ class_attribute :entity_name
6
+
7
+ def initialize(values = {})
8
+ @values = {}
9
+ @id = values['id'] if values['id']
10
+ refresh_from(values)
11
+ self.class.define_attribute_methods(attributes.keys)
12
+ end
13
+
14
+ # we will use this method for creating nested resources
15
+ def refresh_from(values)
16
+ values.each do |key, value|
17
+ @values[key] = value
18
+ end
19
+ end
20
+
21
+ def to_s(*opts)
22
+ @values.to_json
23
+ end
24
+
25
+ def inspect
26
+ id_string = !@id.nil? ? " id=#{@id}" : ""
27
+ "#<#{self.class}:0x#{self.object_id.to_s(16)}#{id_string}> #{attributes}"
28
+ end
29
+
30
+ def method_missing(name, *opts)
31
+ if name[-1] == '='
32
+ name = name.to_s.gsub(/\=$/, '')
33
+ send(:"#{name}_will_change!")
34
+ @values[name.to_s] = opts.first if @values.has_key?(name.to_s)
35
+ else
36
+ @values[name.to_s] if @values.has_key?(name.to_s)
37
+ end
38
+ end
39
+
40
+ def to_json(*opts)
41
+ CiviCrm::JSON.encode(@values)
42
+ end
43
+
44
+ def as_json(*opts)
45
+ @values.as_json(*a)
46
+ end
47
+
48
+ def to_hash
49
+ @values
50
+ end
51
+
52
+ def attributes
53
+ to_hash.reject{ |k, v| v.is_a? (CiviCrm::Resource)}
54
+ end
55
+
56
+ def attribute(key)
57
+ to_hash[key]
58
+ end
59
+
60
+ class << self
61
+ def entity(name = nil)
62
+ self.entity_name = name
63
+ end
64
+
65
+ def entity_class_name
66
+ self.entity_name.to_s.classify
67
+ end
68
+
69
+ def build_from(resp, request_params = {})
70
+ entity = request_params['entity']
71
+ case resp
72
+ when Array
73
+ resp.map { |values| build_from(values, request_params) }
74
+ when Hash
75
+ klass = "CiviCrm::#{entity.classify}".constantize
76
+ resource = klass.new(resp)
77
+ resource
78
+ else
79
+ resp
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,140 @@
1
+ module CiviCrm
2
+ class BaseResource < CiviCrm::Resource
3
+ include CiviCrm::Actions::List
4
+ include CiviCrm::Actions::Find
5
+ include CiviCrm::Actions::Create
6
+ include CiviCrm::Actions::Update
7
+ include CiviCrm::Actions::Destroy
8
+ end
9
+ end
10
+ module CiviCrm
11
+ class Activity < BaseResource
12
+ entity :activity
13
+ end
14
+ class ActivityType < BaseResource
15
+ entity :activity_type
16
+ end
17
+ class Address < BaseResource
18
+ entity :address
19
+ end
20
+ class Batch < BaseResource
21
+ entity :batch
22
+ end
23
+ class Campaign < BaseResource
24
+ entity :campaign
25
+ end
26
+ class Case < BaseResource
27
+ entity :case
28
+ end
29
+ class Constant < BaseResource
30
+ entity :constant
31
+ end
32
+ class Contact < BaseResource
33
+ entity :contact
34
+ end
35
+ class ContactType < BaseResource
36
+ entity :contact_type
37
+ end
38
+ class Contribution < BaseResource
39
+ entity :contribution
40
+ end
41
+ class ContributionPage < BaseResource
42
+ entity :contribution_page
43
+ end
44
+ class ContributionRecur < BaseResource
45
+ entity :contribution_recur
46
+ end
47
+ class CustomField < BaseResource
48
+ entity :custom_field
49
+ end
50
+ class CustomGroup < BaseResource
51
+ entity :custom_group
52
+ end
53
+ class CustomSearch < BaseResource
54
+ entity :custom_search
55
+ end
56
+ class CustomValue < BaseResource
57
+ entity :custom_value
58
+ end
59
+ class Domain < BaseResource
60
+ entity :domain
61
+ end
62
+ class Email < BaseResource
63
+ entity :email
64
+ end
65
+ class Entity < BaseResource
66
+ entity :entity
67
+ end
68
+ class EntityTag < BaseResource
69
+ entity :entity_tag
70
+ end
71
+ class Event < BaseResource
72
+ entity :event
73
+ end
74
+ class Extension < BaseResource
75
+ entity :extension
76
+ end
77
+ class File < BaseResource
78
+ entity :file
79
+ end
80
+ class Grant < BaseResource
81
+ entity :grant
82
+ end
83
+ class Group < BaseResource
84
+ entity :group
85
+ end
86
+ class GroupContact < BaseResource
87
+ entity :group_contact
88
+ end
89
+ class GroupNesting < BaseResource
90
+ entity :group_nesting
91
+ end
92
+ class GroupOrganization < BaseResource
93
+ entity :group_organization
94
+ end
95
+ class Im < BaseResource
96
+ entity :im
97
+ end
98
+ class Job < BaseResource
99
+ entity :job
100
+ end
101
+ class LineItem < BaseResource
102
+ entity :line_item
103
+ end
104
+ class LocBlock < BaseResource
105
+ entity :loc_block
106
+ end
107
+ class Location < BaseResource
108
+ entity :location
109
+ end
110
+ class LocationType < BaseResource
111
+ entity :location_type
112
+ end
113
+ class MailSettings < BaseResource
114
+ entity :mail_settings
115
+ end
116
+ class Mailing < BaseResource
117
+ entity :mailing
118
+ end
119
+ class Membership < BaseResource
120
+ entity :membership
121
+ end
122
+ class Note < BaseResource
123
+ entity :note
124
+ end
125
+ class Phone < BaseResource
126
+ entity :phone
127
+ end
128
+ class Profile < BaseResource
129
+ entity :profile
130
+ end
131
+ class Setting < BaseResource
132
+ entity :setting
133
+ end
134
+ class Survey < BaseResource
135
+ entity :survey
136
+ end
137
+ class SurveyRespondant < BaseResource
138
+ entity :survey_respondant
139
+ end
140
+ end
@@ -0,0 +1,3 @@
1
+ module CiviCrm
2
+ VERSION = '1.0.1'
3
+ end
@@ -0,0 +1,33 @@
1
+ module CiviCrm
2
+ class XML
3
+ class << self
4
+ def parse(text)
5
+ doc = Nokogiri::XML.parse(text.to_s.gsub("\n", ''))
6
+ results = doc.xpath('//Result')
7
+ results.map do |result|
8
+ hash = {}
9
+ result.children.each do |attribute|
10
+ next unless attribute.is_a?(Nokogiri::XML::Element)
11
+ hash[attribute.name] = attribute.children[0].text rescue nil
12
+ end
13
+ hash
14
+ end
15
+ end
16
+ def encode(resources)
17
+ builder = Nokogiri::XML::Builder.new do |xml|
18
+ xml.ResultSet do
19
+ Array.wrap(resources).each do |resource|
20
+ attributes = resource.respond_to?(:attributes) ? resource.attributes : resource
21
+ xml.Result do
22
+ attributes.each do |key, value|
23
+ xml.send key.to_sym, value
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ builder.to_xml
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+ require 'rest_client'
3
+ require 'ostruct'
4
+ describe "API Bindings" do
5
+
6
+ before :all do
7
+ @client = authorized_civicrm_client
8
+ end
9
+
10
+ it "should not fetch from network while initializing a new Resource" do
11
+ @client.expects(:get).never
12
+ CiviCrm::Contact.new(id: "someid")
13
+ end
14
+
15
+ it "should run authentication" do
16
+ @client.expects(:post).once.returns(test_response(api_key: 'test'))
17
+ CiviCrm.authenticate("test", "test")
18
+ end
19
+
20
+ describe "exception handler" do
21
+
22
+ before :all do
23
+ @client = CiviCrm::Client
24
+ end
25
+
26
+ it "should raise CiviCrm::Errors::BadRequest on 400" do
27
+ exception = RestClient::Exception.new(OpenStruct.new(code: 400, body: 'message'))
28
+ RestClient::Request.stubs(:execute).raises(exception)
29
+ expect { @client.execute!({}) }.to raise_error(CiviCrm::Errors::BadRequest)
30
+ end
31
+
32
+ it "should raise CiviCrm::Errors::Unauthorized on 401" do
33
+ exception = RestClient::Exception.new(OpenStruct.new(code: 401, body: 'message'))
34
+ RestClient::Request.stubs(:execute).raises(exception)
35
+ expect { @client.execute!({}) }.to raise_error(CiviCrm::Errors::Unauthorized)
36
+ end
37
+
38
+ it "should raise CiviCrm::Errors::Forbidden on 403" do
39
+ exception = RestClient::Exception.new(OpenStruct.new(code: 403, body: 'message'))
40
+ RestClient::Request.stubs(:execute).raises(exception)
41
+ expect { @client.execute!({}) }.to raise_error(CiviCrm::Errors::Forbidden)
42
+ end
43
+
44
+ it "should raise CiviCrm::Errors::NotFound on 404" do
45
+ exception = RestClient::Exception.new(OpenStruct.new(code: 404, body: 'message'))
46
+ RestClient::Request.stubs(:execute).raises(exception)
47
+ expect { @client.execute!({}) }.to raise_error(CiviCrm::Errors::NotFound)
48
+ end
49
+
50
+ it "should raise CiviCrm::Errors::InternalError on 500" do
51
+ exception = RestClient::Exception.new(OpenStruct.new(code: 500, body: 'message'))
52
+ RestClient::Request.stubs(:execute).raises(exception)
53
+ expect { @client.execute!({}) }.to raise_error(CiviCrm::Errors::InternalError)
54
+ end
55
+
56
+ it "should raise CiviCrm::Errors::Unauthorized if site_key is not provided" do
57
+ CiviCrm.site_key = nil
58
+ RestClient::Request.stubs(:execute).returns('test')
59
+ expect { @client.request('get', 'test') }.to raise_error(CiviCrm::Errors::Unauthorized)
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+ describe CiviCrm::Contact do
3
+ it { should be_listable_resource }
4
+ #it { should be_updatable_resource }
5
+
6
+ it "should return contact on create" do
7
+ client = authorized_civicrm_client
8
+
9
+ client.expects(:post).once.returns(test_response(test_contact))
10
+ c = CiviCrm::Contact.create
11
+ c.should be_a_kind_of(CiviCrm::Contact)
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ require 'civicrm'
2
+ require 'support/civicrm'
3
+ require 'support/civicrm_responses'
4
+ require 'support/test_matchers'
5
+ RSpec.configure do |config|
6
+ include CiviCrm::TestResponses
7
+ config.mock_with :mocha
8
+ end
@@ -0,0 +1,27 @@
1
+ module CiviCrm
2
+ class Client
3
+ class << self
4
+ @mock_rest_client = nil
5
+ def mock_rest_client(mock_client)
6
+ @mock_rest_client = mock_client
7
+ end
8
+
9
+ alias_method :execute!, :execute
10
+ def execute(opts)
11
+ get_params = (opts[:headers] || {})[:params]
12
+ post_params = opts[:payload]
13
+ case opts[:method]
14
+ when :get then @mock_rest_client.get opts[:url], get_params, post_params
15
+ when :post then @mock_rest_client.post opts[:url], get_params, post_params
16
+ when :put then @mock_rest_client.put opts[:url], get_params, post_params
17
+ when :delete then @mock_rest_client.delete opts[:url], get_params, post_params
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ def authorized_civicrm_client
24
+ CiviCrm.api_key = 'civicrm'
25
+ CiviCrm.site_key = 'civicrm'
26
+ CiviCrm::Client.mock_rest_client(mock)
27
+ end
@@ -0,0 +1,31 @@
1
+ module CiviCrm::TestResponses
2
+ def test_response(body, code = 200)
3
+ body = CiviCrm::XML.encode(body) if !(body.kind_of? String)
4
+ m = mock
5
+ m.instance_variable_set('@data', { body: body, code: code})
6
+ def m.body; @data[:body]; end
7
+ def m.code; @data[:code]; end
8
+ m
9
+ end
10
+
11
+ def test_contact(params = {})
12
+ {
13
+ :id => '1',
14
+ :contact_id => '1',
15
+ :contact_type => 'Organization',
16
+ :sort_name => 'Inner City Arts',
17
+ :display_name => 'Inner City Arts',
18
+ :name => 'Bruce Lee',
19
+ :description => 'Foo bar',
20
+ :created => '1345537759',
21
+ :livemode => false
22
+ }.merge(params)
23
+ end
24
+
25
+ def test_contact_array
26
+ {
27
+ :count => 3, :is_error => 0,
28
+ :values => [test_contact, test_contact, test_contact]
29
+ }
30
+ end
31
+ end
@@ -0,0 +1,37 @@
1
+ RSpec::Matchers.define :be_listable_resource do |expected|
2
+ match do |actual|
3
+ client = authorized_civicrm_client
4
+ subject = actual.class
5
+ test_response_hash = send(:"test_#{subject.name.demodulize.underscore}_array")
6
+
7
+ client.expects(:get).once.returns(test_response(test_response_hash))
8
+ c = subject.all
9
+
10
+ c.should be_a_kind_of(Array)
11
+ end
12
+ end
13
+ RSpec::Matchers.define :be_updatable_resource do |expected|
14
+ match do |actual|
15
+ client = authorized_civicrm_client
16
+ subject = actual.class
17
+
18
+ client.expects(:get).once.returns(test_response(test_contact({name: "foo"})))
19
+ client.expects(:put).once.returns(test_response(test_contact({name: "bar"})))
20
+ c = subject.find("resource_id")
21
+ c.name.should == "foo"
22
+ c.name = "bar"
23
+ c.save
24
+ c.name.should == "bar"
25
+ end
26
+ end
27
+ RSpec::Matchers.define :be_deleteable_resource do |expected|
28
+ match do |actual|
29
+ client = authorized_civicrm_client
30
+ subject = actual.class
31
+
32
+ client.expects(:get).once.returns(test_response(test_contact({name: "foo"})))
33
+ client.expects(:delete).once.returns(test_response(test_contact({name: "bar"})))
34
+ c = subject.find("resource_id")
35
+ c.delete
36
+ end
37
+ end
metadata ADDED
@@ -0,0 +1,194 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: civicrm
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Iskander Haziev
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-05-08 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rest-client
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.4'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.4'
30
+ - !ruby/object:Gem::Dependency
31
+ name: activesupport
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: activemodel
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: nokogiri
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: rake
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: rspec
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: mocha
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ description: See http://civicrm.org for details.
127
+ email:
128
+ - gvalmon@gmail.com
129
+ executables:
130
+ - civicrm
131
+ extensions: []
132
+ extra_rdoc_files: []
133
+ files:
134
+ - .gitignore
135
+ - .travis.yml
136
+ - CONTRIBUTORS
137
+ - Gemfile
138
+ - Gemfile.lock
139
+ - History.txt
140
+ - LICENSE
141
+ - README.md
142
+ - Rakefile
143
+ - VERSION
144
+ - bin/civicrm
145
+ - civicrm.gemspec
146
+ - lib/civicrm.rb
147
+ - lib/civicrm/actions/create.rb
148
+ - lib/civicrm/actions/destroy.rb
149
+ - lib/civicrm/actions/find.rb
150
+ - lib/civicrm/actions/list.rb
151
+ - lib/civicrm/actions/update.rb
152
+ - lib/civicrm/client.rb
153
+ - lib/civicrm/errors.rb
154
+ - lib/civicrm/resource.rb
155
+ - lib/civicrm/resources/base.rb
156
+ - lib/civicrm/version.rb
157
+ - lib/civicrm/xml.rb
158
+ - spec/requests/base_spec.rb
159
+ - spec/resources/contact_spec.rb
160
+ - spec/spec_helper.rb
161
+ - spec/support/civicrm.rb
162
+ - spec/support/civicrm_responses.rb
163
+ - spec/support/test_matchers.rb
164
+ homepage: http://civicrm.org
165
+ licenses: []
166
+ post_install_message:
167
+ rdoc_options: []
168
+ require_paths:
169
+ - lib
170
+ required_ruby_version: !ruby/object:Gem::Requirement
171
+ none: false
172
+ requirements:
173
+ - - ! '>='
174
+ - !ruby/object:Gem::Version
175
+ version: '0'
176
+ segments:
177
+ - 0
178
+ hash: -4533903245477044172
179
+ required_rubygems_version: !ruby/object:Gem::Requirement
180
+ none: false
181
+ requirements:
182
+ - - ! '>='
183
+ - !ruby/object:Gem::Version
184
+ version: '0'
185
+ segments:
186
+ - 0
187
+ hash: -4533903245477044172
188
+ requirements: []
189
+ rubyforge_project:
190
+ rubygems_version: 1.8.25
191
+ signing_key:
192
+ specification_version: 3
193
+ summary: Ruby bindings for the CiviCRM API
194
+ test_files: []