infopark-crm-helpers gem version on rubygems.org infopark-crm-helpers build status on Travis CI infopark-crm-helpers on CodeClimate infopark-crm-helpers test coverage on CodeClimate

infopark-crm-helpers

Infopark WebCRM is a cloud-based CRM. This gem infopark-crm-helpers addresses two "annoying" things when you have your own classes and want to use the WebCRM as a data store.

  • Crm::Contact and its friends don't provide setters for attributes and if you build your own classes around them, you will find that collecting all the relevant attributes with which to call #update (or #create) can be a bit tedious.
  • There is no easy way to validate your objects against the type definitions set up in the WebCRM. You however want to validate your entire object before you try to write your data to the WebCRM, because not doing so can cause an awkward user experience: You validate the object to match your expectations of what a valid object is and the user works on fixing their input to match your expectations. Then, you send your data to the WebCRM and... it tells you your data is invalid. The user will have to deal with another round of fixing their input, which can end up in nasty tableflips.

infopark-crm-helpers attempts to address these things in a convenient way.

Installation

Add 'infopark-crm-helpers' into your Gemfile.

gem 'infopark-crm-helpers`

Install the gem with Bundler.

bundle install

Configuration

No direct configuration of infopark-crm-helpers is required. Refer to infopark/webcrm_sdk for instructions on setting up your installation of infopark_webcrm_sdk.

Run specs

Add your CRM credentials to /spec/config/crm.yml.

test:
  tenant: your-tenant-here
  login: [email protected]
  api_key: 1234567890abcdefghijklmnopqrstuvwxyz

Then invoke:

bundle exec rake spec

Examples

Create your own class

Let's build a class Customer which is based on WebCRM contacts.

class Customer
  include Crm::Helpers::Attributes

  represents_crm_type :contact
end

Customer.mandatory_crm_attributes
# => [:language, :last_name]

Customer.crm_attributes[:language]
# => {"attribute_type"=>"enum", "create"=>true, "mandatory"=>true, "read"=>true,
# "title"=>"Language", "update"=>true, "valid_values"=>["de", "en"]}

Customer.crm_attributes[:last_name]
# => {"attribute_type"=>"string", "create"=>true, "mandatory"=>true,
# "read"=>true, "title"=>"Last Name", "update"=>true}

Use attributes from the CRM contact

class Customer
  include Crm::Helpers::Attributes

  represents_crm_type :contact

  crm_attr_accessor :first_name, :last_name, :email
end

customer = Customer.new
# => #<Customer>

customer.first_name = 'Huy'
# => "Huy"

customer.last_name = 'Dinh'
# => "Dinh"

customer.email = '[email protected]'
# => "[email protected]"

customer.crm_attributes
# => {:first_name=>"Huy", :last_name=>"Dinh", :email=>"[email protected]"}

Validate an object

class Customer
  include ActiveModel::Validations
  include Crm::Helpers::Attributes

  represents_crm_type :contact

  crm_attr_accessor :first_name, :last_name, :email
  validates_with Crm::Helpers::Validators::CrmTypeValidator
end

customer = Customer.new
# => #<Customer>

customer.valid?
# => false

customer.errors
# => #<ActiveModel::Errors @base=#<Customer:0x007feb8f3b3d00
# @validation_context=nil, @errors=#<ActiveModel::Errors:0x007feb8f381dc8 ...>,
# @crm_attributes={}>, @messages={:language=>["can't be blank"],
# :last_name=>["can't be blank"]}>```

Fetch and update an object

class Customer
  include ActiveModel::Validations
  include Crm::Helpers::Attributes

  represents_crm_type :contact

  crm_attr_accessor :first_name, :last_name, :email
  validates_with Crm::Helpers::Validators::CrmTypeValidator

  def self.find(crm_id)
    crm_contact = Crm::Contact.find(crm_id)
    return nil if crm_contact.nil?

    new(crm_contact.attributes)
  end

  def initialize(attributes = {})
    @crm_attributes = attributes.dup
  end

  def save
    return false if invalid?

    crm_contact = Crm::Contact.find(crm_id)
    crm_contact.update(crm_attributes)
    @crm_attributes = crm_contact.attributes
    true
  end
end

customer = Customer.find('fc851ba935f8420824498aee739ac897')
# => #<Customer>

customer.last_name
# => "Dinh"

customer.first_name
# => "Huy"

customer.last_name = 'Smith'
# => "Smith"

customer.first_name = 'John'
# => "John"

customer.save
# true

same_customer = Customer.find('fc851ba935f8420824498aee739ac897')
# => #<Customer>

same_customer.last_name
# => "Smith"

same_customer.first_name
# => "John"

Validators

Each CRM attribute type has its own validator:

  • Crm::Helpers::Validators::CrmBooleanValidator
  • Crm::Helpers::Validators::CrmDatetimeValidator
  • Crm::Helpers::Validators::CrmEnumValidator
  • Crm::Helpers::Validators::CrmIntegerValidator
  • Crm::Helpers::Validators::CrmListValidator
  • Crm::Helpers::Validators::CrmMultienumValidator
  • Crm::Helpers::Validators::CrmStringValidator
  • Crm::Helpers::Validators::CrmTextValidator

You can use those to explicitly validate attributes for a certain format.

validates_with Crm::Helpers::Validators::CrmBooleanValidator,
               attributes: [:custom_has_ps4, :custom_has_xbox_one]

Finders

class Customer
  include ActiveModel::Validations
  include Crm::Helpers::Attributes
  include Crm::Helpers::Finders

  represents_crm_type :contact

  crm_attr_accessor :first_name, :last_name, :email
  validates_with Crm::Helpers::Validators::CrmTypeValidator

  def initialize(attributes = {})
    @crm_attributes = attributes.dup
  end
end

customer = Customer.find('fc851ba935f8420824498aee739ac897')
# => #<Customer>

Persistence

class Customer
  include ActiveModel::Validations
  include Crm::Helpers::Attributes
  include Crm::Helpers::Persistence

  represents_crm_type :contact

  crm_attr_accessor :first_name, :last_name, :email
  validates_with Crm::Helpers::Validators::CrmTypeValidator

  def initialize(attributes = {})
    @crm_attributes = attributes.dup
  end
end

customer = Customer.create(language: 'en', last_name: 'Dinh')
# => #<Customer>

customer.language = 'de'
# => 'de'

customer.save
# => true

customer.update(language: 'en')
# => true

customer.destroy
# => #<Customer>