SObjectModel
Salesforce sObject class library
install
- Rubygem
-
the easiest way
$ gem install sobjectmodel
- Bundler
-
in Gemfile:
gem 'sobjectmodel'
then,
$ bundle install
Generate sObject classes
require 'sobject_model'
# connect with Salesforce using REST API
SObjectModel.connect(:rest, instance_url: url, access_token: token, api_version: 62.0)
# generate Account, Contact and User sObject class
SObjectModel.generate :Account, :Contact, :User
# identify all generated classes
SObjectModel.generated_classes #=> [Account, Contact, User]
What is instance url?
In short, it’s Salesforce host url. You can check it by Salesforce CLI:
$ sf org display
KEY VALUE
--------------------------------------------------------------
Access Token <the org's access token>
Api Version 62.0
Connected Status Connected
Instance Url https://hoge-abc-ed.example.my.salesforce.com
For details, see the command reference.
Main features
Initialize and save a record
c = Contact.new(:Name => "John Smith")
c.Name # "John Smith"
c.save
Find and update a record
c2 = Contact.find(c.id) # find by record ID
c2 = Contact.find_by(Name: "John Smith") # find by Name
c2.Name = "Johnny Smith"
c2.save # update
Delete a record
c2 = Contact.find(c.id)
c2.delete
Query and Get a record
contact = Contact.select(:Id, :Name).where(Name: 'Akin Kristen').take
contact # => #<Contact: @Id="0035j00001RW3xbAAD", @Name="Akin Kristen">
contact.Name # Akin Kristen
Contact.select(:Name).where(Name: 'John Smith', LastModifiedDate: :Yesterday).take
Contact.select(:Name).where(Name: 'John Smith').where(LastModifiedDate: :Yesterday).take # same as above
Query and Get records
contacts = Contact.where(LastModifiedDate: :Yesterday).all # get all contacts who are modified yesterday
accounts = Account.where.not(LastModifiedDate: :Yesterday).all # get all accounts that are *not* modified yesterday
Aggregate functions
Account.where(BillingCountry: 'Japan').count # count accounts whose billing country is Japan
User.where(country: 'USA').max(:LastModifiedDate) # get the latest LastModifiedDate of users in USA
Case.min(:LastCreatedDate) # get the date when the oldest case was created
Child-Parent Relationship
contact = Contact.select(:Id, :Name, "Account.Name").where(Name: 'Akin Kristen').take
contact # <Contact: @Id="0035j00001RW3xbAAD", @Name="Akin Kristen", @Account= #<Account @Name="Aethna Home Products">>
contact.Account.Name # Aethna Home Products
Parent-Children Relationship
account = Account.select(:Id, :Name, "(SELECT Name FROM Contacts)").take
account # <Account @Contacts=[#<Contact @Name="Akin Kristen">], @Id="0015j00001dsDuhAAE", @Name="Aethna Home Products">
account.Name # Aethna Home Products
account.Contacts # [#<Contact @Name="Akin Kristen">]
account.Contacts.first.Name # Akin Kristen
Time keywords such as ‘yesterday’ and ‘LAST_N_DAYS:N’ with symbol style
Contact.select(:Name).where(LastModifiedDate: :Yesterday).take # "SELECT Id, Name FROM Contact WHERE LastModifiedDate = Yesterday" LIMIT 1
Contact.select(:Name).where(LastModifiedDate: :"LAST_N_DAYS:5").take # "SELECT Id, Name FROM Contact WHERE LastModifiedDate = LAST_N_DAYS:5" LIMIT 1
Array for ‘IN’ keyword
Contact.select(:Name).where(Name: ['Jonny Good', 'John Smith']).all # same as "SELECT Name FROM Contact WHERE Name IN ('Jonny Good', 'John Smith')"
Using partial soql directly
Contact.select("Id, Name").where("LastModifiedDate = LAST_N_DAYS:5").all
Ternary style
Contact.select(:Id, :Name).where(:LastModifiedDate, :>=, :"LAST_N_DAYS:5").all # SELECT Id, Name FROM Contact WHERE LastModifiedDate >= LAST_N_DAYS:5
Account.select(:Id, :Name).where(:Name, :LIKE, "%OIL%").all # SELECT Id, Name FROM Account WHERE Name LIKE '%OIL%'
Get schema
schema = Account.describe
schema.name # Account
schema.field_names # [Id, Name, ....]
schema.fields.name_and_labels # returns all field name and label pairs
Sometimes it’s better to use raw SOQL when it gets complecated.
accounts =
SObjectModel.connection.exec_query(
"SELECT Id, Name FROM Account WHERE Name like '%Hoge' OR (Age <= 30 AND BillingCity IN ['Tokyo', 'NewYork'])",
model_class: Account
)
Known Issues
exec_query
SELECT clause missing Id causes model class to force insert a record, not update
irb(main):006> rows = SObjectModel.connection.exec_query "SELECT Name FROM Account LIMIT 1", model_class: Account
=>
[#<Account:0x0000724bada70970
...
irb(main):007> rows.first
=>
#<Account:0x0000724bada70970
@Name="Express Logistics and Transport2",
@current_attributes={:Name=>"Express Logistics and Transport2"},
@original_attributes={:Name=>"Express Logistics and Transport2"},
@updated_attributes={:Name=>nil}>
=> rows.first.save #=> this does NEW RECORD INSERT, not Update because its Id is nil
Child relationship in SELECT clause
child relationsip that doesn’t specify Id in SELECT clause causes model class to force insert a record, not update
irb(main):016> acc = Account.select(:Name, '(SELECT Name FROM Contacts)').where(Id: Contact.where.not(AccountId: nil).pluck(:AccountId)).take
=>
#<Account:0x0000724bae18f4b8
...
irb(main):017> acc
=>
#<Account:0x0000724bae18f4b8
@Contacts=
[#<Contact:0x0000724bacf411a8 @Name="Gonzalez Rose", @current_attributes={:Name=>"Gonzalez Rose"}, @original_attributes={:Name=>"Gonzalez Rose"}, @updated_attributes={:Name=>nil}>,
#<Contact:0x0000724bae18f468 @Name="Forbes Sean", @current_attributes={:Name=>"Forbes Sean"}, @original_attributes={:Name=>"Forbes Sean"}, @updated_attributes={:Name=>nil}>],
@Id="0015j00001U2XvEAAV",
@Name="Edge Communications",
@current_attributes={:Name=>"Edge Communications"},
@original_attributes={:Name=>"Edge Communications", :Id=>"0015j00001U2XvEAAV"},
@updated_attributes={:Name=>nil}>
irb(main):018> acc.Contacts.first.save #=> this does NEW RECORD INSERT, not Update because its Id is nil
Development Note
1. To develop, install libraries at first.
$ bundle install
2. This project adopts TDD (Test Driven Development)
# rspec
$ bundle install rspec
# cucumber
$ SF_TARGET_ORG=your_target_org bundle exec cucumber
3. For debug, use IRB
$ bundle exec rake irb[your_target_org]
> gen :Account, :Contact #=> shortcut to generate classes
The word “target org” is a technical term of Salesforce CLI. It means the alias of Salesforce org. You can check it like bellow:
$ sf org list
Type Alias Username Org ID Status Expires
------------------------------------------------------------------------------------------
DevHub dev hoge@example.sandbox 00D5j00000DiuxmEAB Connected
trailhead hoge@resourceful-hoge-bar-baz.com 00DIS000002BinI2AS Connected
See also the command reference.