The Ruby gem for SugarCRM has already been briefly presented in a SugarCRM Developer Zone article, 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! [/code] 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: [code language='ruby'] puts account.contacts.include? alice puts account.contacts.size [/code] 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: [code language='ruby'] account.disassociate!(alice) [/code] 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: [code language='ruby'] account.contacts.each{|c| puts "#{c.first_name} #{c.last_name}" } [/code] (Let's quickly remove Alice from CRM: 'alice.delete') <h2>Dynamic finders</h2> 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"]}) [/code] The last account in California that was created by the administrator (note how conditions are specified on multiple attributes): [code language='ruby'] 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 10 last 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).each{|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.
Thanks for the article.
The advanced Gem use page.. seems not to be there.
Very helpful Gem.
Yes, it was set to publish one day later but the link was already in place. I later realized the mistake. (The article is now published.)
Thank you for this fantastic article !
but, actually, after the connection, when I create a contact:
c = SugarCRM::Contact.create(:first_name => 'John', :last_name => 'Doe')
the contact are NOT created, and if I try to save it with c.save!, I get :
SugarCRM::InvalidRecord: Team name cannot be blank, Team count cannot be blank
In your example you don’t pass any other fields in create method ! … any idea ?
thanks,
Alessandro DS
Hi Alex,
In the community edition of SugarCRM, the only required field for a contact is the last name.
The pro and enterprise editions seem to have additional required fields, you can see which ones (and what the requirements are) by doing something like this:
c = SugarCRM::Contact.new
c.valid?
puts c.errors.inspect
You solve your problem, you can either assign attributes to the required fields (that you don’t care about) by expanding the gem as described in the post on advanced use (http://davidsulc.com/blog/2011/04/05/ruby-gem-for-sugarcrm-advanced-use/), or try remove the requirement from the SugarCRM Studio.
Pingback: Today’s Links May 23, 2011 | myErp
First off, thank you! This is an incredibly useful gem, so far it’s working great.
Is it possible to work with targets. I’d like to have my rails app find a target by it’s email address and then convert it to a lead.
Best,
Hillel
Unfortunately, it seems that won’t be possible at this time: `SugarCRM.modules` reveals no Target module class is returned by the SugarCRM API, so I don’t see how to access campaign targets.
I will contact you by email to see if there might be another way to achieve your objectives.