Ruby Gem for SugarCRM: the Basics

Posted on April 3, 2011

The Ruby gem for SugarCRM has already been briefly presented in a SugarCRM Developer Zone article (edit: which has since been lost to the sands of time...), but has been drastically improved over the last months. In the following lines, we’ll go over a few of these improvements and show you how leveraging SugarCRM can be greatly improved.

Setting up

The gem is tested with Ruby 1.8.7 and 1.9.2, so you can pick your favorite version. Installing the gem is as easy as

  
  
    
    gem install  sugarcrm
  
    
  

(note: depending on your particular setup, you might need to call sudo gem install instead).

If you don’t have a SugarCRM server setup to use for testing, try one of the SugarCRM stack installers.

Since we’ll be executing simple, nearly-independent commands, feel free to follow along in an irb session.

Connecting

The first thing we need to do is

  
  
    
    require 'sugarcrm'
  
    
  

after which we can connect to the actual SugarCRM server:

  
  
    
    SugarCRM.connect('http://127.0.0.1/sugarcrm','admin','letmein')
  
    
  

(Naturally, this assumes you have your SugarCRM instance running.)

You’ll notice that the URL points to where the SugarCRM server is available on the web server, not the actual index page, nor the API endpoint.

If you’re following along in irb, you’ll see the gem dutifully returns a namespace similar to

  
  
    
    SugarCRM::Namespace0
  
    
  

You can safely ignore this for now (we’ll tell you more about it in the advanced article).

Basic CRUD actions

On to our first subject: Create/Read/Update/Delete actions.

Let’s start by seeing which contact was last entered in CRM:

  
  
    
    puts SugarCRM::Contact.last.last_name
  
    
  

All SugarCRM modules (i.e. “Contacts”, “Opportunities”, etc.) are namespaced within the ‘SugarCRM’ Ruby module. So ‘SugarCRM::Contact’ gives us access to the ‘Contact’ Ruby class that will wrap all Contacts in CRM. (As a side note, any custom modules you may have created in Studio or loaded with the Module loader will be available in the same fashion.) From that class, we call the ‘last’ finder function (more on those below) which will return the record in SugarCRM with the latest ‘date_entered’ value. Finally, we call the ‘last_name’ attribute on the contact instance to get the person’s last name (which is then printed by Ruby).

Let’s create a new contact:

  
  
    
    c = SugarCRM::Contact.create(:first_name => 'John', :last_name => 'Doe')
  
    
  

With respect to creating new class instances and records in CRM, the gem behaves much like ActiveRecord: new will create a new class instance without saving it to CRM, whereas create will actually save  the class instance on CRM (i.e., create a new record). As we called create in this case, our CRM should now have a new record. Let’s check:

  
  
    
    puts SugarCRM::Contact.last.last_name # => Doe
  
    
  

Notice we’re calling the same methods as above: we’re still looking for the last created record. However, the output will be different, since we just created a new record. Let’s change John Doe’s last name:

  
  
    
    c.last_name = 'Dot'
    c.save!
  
    
  

To change the last name, we simply re-assign a value to the attribute. The attribute names always match the SugarCRM field names (which can be looked up in the Studio in the Admin interface), which means that any custom attributes (i.e. that have been added through the Studio tool) will end with ‘_c’. In other words, if we add a “middle name” field to our contact module using the Studio tool, we’ll access that field in the gem by calling my_contact.middle_name_c.

Once again, the gem behaves like ActiveRecord: save will attempt to save the record. If it’s unable to do so (e.g., some required fields are blank), it will simply return false. We’ll call save! instead so that if the record can’t be saved, an exception will be raised and the code execution will stop.

Now that we’ve gone through creating, reading, and updating, it’s time to bid farewell to John Doe:

  
  
    
    c.delete
  
    
  

Associations

Let’s start by getting a SugarCRM account to work with. (We’ll learn more about finding specific module instances later.)

  
  
    
    account = SugarCRM::Account.first
  
    
  

Let’s see how many contacts it has:

  
  
    
    puts account.contacts.size
  
    
  

This account just hired a new receptionist. Let’s start by creating a contact in CRM for her (just like we did above):

  
  
    alice = SugarCRM.create(:last_name => 'Angry',
   :first_name => 'Alice', :title => 'Receptionist')
  
  

Now we can add her to the account’s contacts:

  
  
    
    account.contacts << alice
    account.save!
  
    
  

You can also call account.associate!(alice) which will link the two records and save the relationship in one step. There is a major implementation difference, though: when you call account.contacts , it loads all of the account's contacts before creating the new association, which might take a long time. If you just want to make sure the two records are linked and haven't already loaded the associated accounts, it's usually best to call the associate method.

Let's check Alice is now associated with the account, and see how many contacts this account now has:

  
  
    
    puts account.contacts.include? alice
    puts account.contacts.size
  
    
  

It turns out Alice wasn't a great fit for the company (she didn't really have people skills). Let's remove her from that account:

  
  
    
    account.disassociate!(alice)
  
    
  

You could, of course, simply call alice.delete which would delete Alice's record in CRM and all associated relationships, whereas what we did above simply remove the link between the two records, without deleting Alice's record. (In other words, Alice is still in CRM.)

Let's print the full names of all contacts linked to this account, so we can make sure Alice isn't among them:

  
  
    account.contacts.each{|c|
  puts "#{c.first_name} #{c.last_name}"
}
  
  
(Let's quickly remove Alice from CRM: alice.delete )

Dynamic finders

Just like ActiveRecord, the gem provides dynamic finders with attribute names. Here's an easy way to find the "Family Stationary" account:

  
  
    
    SugarCRM::Account.find_by_name("Family Stationary")
  
    
  

As you'll see below, this is simply a convenience method that calls a finder with a condition matching the attribute name ('name', in this case) and value ("Family Stationary"). Such methods are available for all attributes defined on your CRM modules.

There is also an "all-in-one" method to ensure a record exists:

  
  
    
    SugarCRM::Account.find_or_create_by_name("Family Stationary")
  
    
  

If the record exsits, it will be returned. If no such record exists, it will first be created in CRM, and then returned.

Finders

We've briefly seen how to use the default behavior of the 'first' and 'last' finders. As their name suggests, these finders will return, respectively, the first or last record matching the criteria (if no criteria is given, the default is to sort by date entered). But we can also give conditions to the query: let's search for the first account in Los Angeles:

  
  
    
    SugarCRM::Account.first(:conditions =>
      {:billing_address_city => "Los Angeles"})
  
    
  

Notice that conditions are specified within a :conditions array. By default, the query will search for records where the attribute is equal to the specified value ( "Los Angeles" in this case). But there are other ways to use conditons, as you will see below.

Let's find the first account with a zip code between 50000 and 800000. Notice how, instead of a single value, we've used an array to specify several conditions (including operators). These conditions will be joined with an SQL AND (if you want to use a query with complex boolean algebra, at the time of this writing you'll need to use the direct API method the gem provides, as explained in the "advanced" article).

  
  
    
    SugarCRM::Account.first(:conditions =>
      {:billing_address_postalcode => ["> 50000", "<= 80000"]})
  
    
  

The last account in California that was created by the administrator (note how conditions are specified on multiple attributes):

  
  
    
    SugarCRM::Account.last(:conditions =>
      {:billing_address_state => "CA", :created_by_name => "Administrator"})
  
    
  

The gem also has (very limited) SQL operator support, although they must be all uppercase to be recognized. Let's print all the accounts with "Inc" in their name:

  
  
    
    SugarCRM::Account.all(:conditions =>
      {:name => "LIKE '%Inc%'"}).each{|a| puts a.name }
  
    
  

Notice how this time, instead of calling 'first' or 'last', we called 'all'. The finder syntax is the same for 'all', the difference lies in the return value: 'first' and 'last' will return either an instance matching the criteria or nil , whereas 'all' will return an array of matching object instances (or an empty array if there are none).

This shows you how you can iterate across result sets as arrays. The better way to pass each record into a block is this way:

  
  
    
    SugarCRM::Account.all(:conditions =>
      {:name => "LIKE '%Inc%'"}){|a| puts a.name }
  
    
  

(Notice the absence of each .) The reason this way is superior is that when each is used, we must must retrieve all matching records before iterating, which can be an issue if there are many. By passing the block directly to the finder as in the second example, records are yielded to the block as each record slice is retrieved.

Finders also accept some SQL arguments: let's print the list of the last 10 accounts (except the last one, due to the 'offset' value) sorted descending by name:

  
  
    
    SugarCRM::Account.all(:order_by => "name DESC", :limit => 10,
      :offset => 1){|a| puts a.name }
  
    
  

Of course, you can also add a :conditions hash in there. Also to be noted: as of this writing the SugarCRM REST API seems to have a bug where the limit and offset options don't work correctly (the value for limit is always considered to be the smaller of the 2 values). This bug is fixed when using the gem: your query will return the expected results, even if the SugarCRM version you're using has this bug. That's how we roll...

Want to learn more? Check out other articles on this gem: how to create a basic Rails portal for SugarCRM using this gem, and advanced gem use (covering thins like using configuration files for automatic login, extending the gem, etc.).

Gem documentation can be found at https://github.com/chicks/sugarcrm, and questions/problems regarding the gem should be reported there also for quicker resolution.


Would you like to see more Elixir content like this? Sign up to my mailing list so I can gauge how much interest there is in this type of content.