class Databasedotcom::Sobject::Sobject

Parent class of dynamically created sobject types. Interacts with Force.com through a Client object that is passed in during materialization.

Constants

Id

Public Class Methods

all() click to toggle source

Returns all records of type self as instances.

client.materialize("Car")
Car.all    #=>   [#<Car @Id="1", ...>, #<Car @Id="2", ...>, #<Car @Id="3", ...>, ...]
# File lib/databasedotcom/sobject/sobject.rb, line 225
def self.all
  self.client.query("SELECT #{self.field_list} FROM #{self.sobject_name}")
end
attributes() click to toggle source

Returns an Array of attribute names that this Sobject has.

client.materialize("Car")
Car.attributes               #=> ["Id", "Name", "Color", "Year"]
# File lib/databasedotcom/sobject/sobject.rb, line 157
def self.attributes
  self.description["fields"].collect { |f| [f["name"], f["relationshipName"]] }.flatten.compact
end
coerce_params(params) click to toggle source

Coerce values submitted from a Rails form to the values expected by the database returns a new hash with updated values

# File lib/databasedotcom/sobject/sobject.rb, line 317
def self.coerce_params(params)
  params.each do |attr, value|
    case self.field_type(attr)
      when "boolean"
        params[attr] = value.is_a?(String) ? value.to_i != 0 : value
      when "currency", "percent", "double"
        value = value.gsub(/[^-0-9.0-9]/, '').to_f if value.respond_to?(:gsub)
        params[attr] = value.to_f
      when "date"
        params[attr] = Date.parse(value) rescue Date.today
      when "datetime"
        params[attr] = DateTime.parse(value) rescue DateTime.now
    end
  end
end
count() click to toggle source

Get the total number of records

# File lib/databasedotcom/sobject/sobject.rb, line 265
def self.count
  self.client.query("SELECT COUNT() FROM #{self.sobject_name}").total_size
end
create(object_attributes) click to toggle source

Delegates to Client.create with arguments object_attributes and self

# File lib/databasedotcom/sobject/sobject.rb, line 311
def self.create(object_attributes)
  self.client.create(self, object_attributes)
end
createable?(attr_name) click to toggle source

Returns true if the attribute attr_name can be created. Raises ArgumentError if attribute does not exist.

# File lib/databasedotcom/sobject/sobject.rb, line 209
def self.createable?(attr_name)
  self.type_map_attr(attr_name, :createable?)
end
delete(record_id) click to toggle source

Delegates to Client.delete with arguments record_id and self

# File lib/databasedotcom/sobject/sobject.rb, line 260
def self.delete(record_id)
  self.client.delete(self.sobject_name, record_id)
end
field_type(attr_name) click to toggle source

Returns the Force.com type of the attribute attr_name. Raises ArgumentError if attribute does not exist.

client.materialize("Car")
Car.field_type("Color")    #=> "string"
# File lib/databasedotcom/sobject/sobject.rb, line 189
def self.field_type(attr_name)
  self.type_map_attr(attr_name, :type)
end
find(record_id) click to toggle source

Delegates to Client.find with arguments record_id and self

client.materialize("Car")
Car.find("rid")    #=>   #<Car @Id="rid", ...>
# File lib/databasedotcom/sobject/sobject.rb, line 217
def self.find(record_id)
  self.client.find(self, record_id)
end
first(where_expr=nil) click to toggle source

Find the first record. If the where_expr argument is present, it must be the WHERE part of a SOQL query

# File lib/databasedotcom/sobject/sobject.rb, line 243
def self.first(where_expr=nil)
  where = where_expr ? "WHERE #{where_expr} " : ""
  self.client.query("SELECT #{self.field_list} FROM #{self.sobject_name} #{where}ORDER BY Id ASC LIMIT 1").first
end
label_for(attr_name) click to toggle source

Returns the label for the attribute attr_name. Raises ArgumentError if attribute does not exist.

# File lib/databasedotcom/sobject/sobject.rb, line 194
def self.label_for(attr_name)
  self.type_map_attr(attr_name, :label)
end
last(where_expr=nil) click to toggle source

Find the last record. If the where_expr argument is present, it must be the WHERE part of a SOQL query

# File lib/databasedotcom/sobject/sobject.rb, line 249
def self.last(where_expr=nil)
  where = where_expr ? "WHERE #{where_expr} " : ""
  self.client.query("SELECT #{self.field_list} FROM #{self.sobject_name} #{where}ORDER BY Id DESC LIMIT 1").first
end
materialize(sobject_name) click to toggle source

Materializes the dynamically created Sobject class by adding all attribute accessors for each field as described in the description of the object on Force.com

# File lib/databasedotcom/sobject/sobject.rb, line 162
def self.materialize(sobject_name)
  self.cattr_accessor :description
  self.cattr_accessor :type_map
  self.cattr_accessor :sobject_name

  self.sobject_name = sobject_name
  self.description = self.client.describe_sobject(self.sobject_name)
  self.type_map = {}

  self.description["fields"].each do |field|

    # Register normal fields
    name = field["name"]
    register_field( field["name"], field )

    # Register relationship fields.
    if( field["type"] == "reference" and field["relationshipName"] )
      register_field( field["relationshipName"], field )
    end

  end
end
method_missing(method_name, *args, &block) click to toggle source

Sobject objects support dynamic finders similar to ActiveRecord.

client.materialize("Car")
Car.find_by_Color("Blue")
Car.find_all_by_Year("2011")
Car.find_by_Color_and_Year("Blue", "2011")
Car.find_or_create_by_Year("2011")
Car.find_or_initialize_by_Name("Foo")
Calls superclass method
# File lib/databasedotcom/sobject/sobject.rb, line 277
def self.method_missing(method_name, *args, &block)
  if method_name.to_s =~ /^find_(or_create_|or_initialize_)?by_(.+)$/ || method_name.to_s =~ /^find_(all_)by_(.+)$/
    named_attrs = $2.split('_and_')
    attrs_and_values_for_find = {}
    hash_args = args.length == 1 && args[0].is_a?(Hash)
    attrs_and_values_for_write = hash_args ? args[0] : {}

    named_attrs.each_with_index do |attr, index|
      value = hash_args ? args[0][attr] : args[index]
      attrs_and_values_for_find[attr] = value
      attrs_and_values_for_write[attr] = value unless hash_args
    end

    limit_clause = method_name.to_s.include?('_all_by_') ? "" : " LIMIT 1"

    results = self.client.query("SELECT #{self.field_list} FROM #{self.sobject_name} WHERE #{soql_conditions_for(attrs_and_values_for_find)}#{limit_clause}")
    results = limit_clause == "" ? results : results.first rescue nil

    if results.nil?
      if method_name.to_s =~ /^find_or_create_by_(.+)$/
        results = self.client.create(self, attrs_and_values_for_write)
      elsif method_name.to_s =~ /^find_or_initialize_by_(.+)$/
        results = self.new
        attrs_and_values_for_write.each { |attr, val| results.send("#{attr}=", val) }
      end
    end

    results
  else
    super
  end
end
new(attrs = {}) click to toggle source

Returns a new Sobject. The default values for all attributes are set based on its description.

Calls superclass method
# File lib/databasedotcom/sobject/sobject.rb, line 14
def initialize(attrs = {})
  super()
  self.class.description["fields"].each do |field|
    if field['type'] =~ /(picklist|multipicklist)/ && picklist_option = field['picklistValues'].find { |p| p['defaultValue'] }
      self.send("#{field["name"]}=", picklist_option["value"])
    elsif field['type'] =~ /boolean/
      self.send("#{field["name"]}=", field["defaultValue"])
    else
      self.send("#{field["name"]}=", field["defaultValueFormula"])
    end
  end
  self.attributes=(attrs)
end
picklist_values(attr_name) click to toggle source

Returns the possible picklist options for the attribute attr_name. If attr_name is not of type picklist or multipicklist, [] is returned. Raises ArgumentError if attribute does not exist.

# File lib/databasedotcom/sobject/sobject.rb, line 199
def self.picklist_values(attr_name)
  self.type_map_attr(attr_name, :picklist_values)
end
query(where_expr) click to toggle source

Returns a collection of instances of self that match the conditional where_expr, which is the WHERE part of a SOQL query.

client.materialize("Car")
Car.query("Color = 'Blue'")    #=>   [#<Car @Id="1", @Color="Blue", ...>, #<Car @Id="5", @Color="Blue", ...>, ...]
# File lib/databasedotcom/sobject/sobject.rb, line 233
def self.query(where_expr)
  self.client.query("SELECT #{self.field_list} FROM #{self.sobject_name} WHERE #{where_expr}")
end
updateable?(attr_name) click to toggle source

Returns true if the attribute attr_name can be updated. Raises ArgumentError if attribute does not exist.

# File lib/databasedotcom/sobject/sobject.rb, line 204
def self.updateable?(attr_name)
  self.type_map_attr(attr_name, :updateable?)
end
upsert(field, value, attrs) click to toggle source

Delegates to Client.upsert with arguments self, field, values, and attrs

# File lib/databasedotcom/sobject/sobject.rb, line 255
def self.upsert(field, value, attrs)
  self.client.upsert(self.sobject_name, field, value, attrs)
end

Private Class Methods

field_list() click to toggle source
# File lib/databasedotcom/sobject/sobject.rb, line 348
def self.field_list
  self.description['fields'].collect { |f| f['name'] }.join(',')
end
register_field( name, field ) click to toggle source
# File lib/databasedotcom/sobject/sobject.rb, line 335
def self.register_field( name, field )
  public
  attr_accessor name.to_sym
  private
  self.type_map[name] = {
    :type => field["type"],
    :label => field["label"],
    :picklist_values => field["picklistValues"],
    :updateable? => field["updateable"],
    :createable? => field["createable"]
  }
end
soql_conditions_for(params) click to toggle source
# File lib/databasedotcom/sobject/sobject.rb, line 357
def self.soql_conditions_for(params)
  params.inject([]) do |arr, av|
    case av[1]
      when String
        value_str = "'#{av[1].gsub("'", "\\\\'")}'"
      when DateTime, Time
        value_str = av[1].strftime(RUBY_VERSION.match(/^1.8/) ? "%Y-%m-%dT%H:%M:%S.000%z" : "%Y-%m-%dT%H:%M:%S.%L%z").insert(-3, ":")
      when Date
        value_str = av[1].strftime("%Y-%m-%d")
      else
        value_str = av[1].to_s
    end

    arr << "#{av[0]} = #{value_str}"
    arr
  end.join(" AND ")
end
type_map_attr(attr_name, key) click to toggle source
# File lib/databasedotcom/sobject/sobject.rb, line 352
def self.type_map_attr(attr_name, key)
  raise ArgumentError.new("No attribute named #{attr_name}") unless self.type_map.has_key?(attr_name)
  self.type_map[attr_name][key]
end

Public Instance Methods

==(other) click to toggle source
# File lib/databasedotcom/sobject/sobject.rb, line 8
def ==(other)
  return false unless other.is_a?(self.class)
  self.Id == other.Id
end
[](attr_name) click to toggle source

Get a named attribute on this object

# File lib/databasedotcom/sobject/sobject.rb, line 143
def [](attr_name)
  self.send(attr_name) rescue nil
end
[]=(attr_name, value) click to toggle source

Set a named attribute on this object

# File lib/databasedotcom/sobject/sobject.rb, line 148
def []=(attr_name, value)
  raise ArgumentError.new("No attribute named #{attr_name}") unless self.class.attributes.include?(attr_name)
  self.send("#{attr_name}=", value)
end
attributes() click to toggle source

Returns a hash representing the state of this object

# File lib/databasedotcom/sobject/sobject.rb, line 29
def attributes
  self.class.attributes.inject({}) do |hash, attr|
    hash[attr] = self.send(attr.to_sym) if self.respond_to?(attr.to_sym)
    hash
  end
end
attributes=(attrs) click to toggle source

Set attributes of this object, from a hash, in bulk

# File lib/databasedotcom/sobject/sobject.rb, line 37
def attributes=(attrs)
  attrs.each do |key, value|
    self.send("#{key}=", value)
  end
end
delete() click to toggle source

Deletes the corresponding record from the Force.com database. Returns self.

client.materialize("Car")
c = Car.find_by_Color("Yellow")
c.delete
# File lib/databasedotcom/sobject/sobject.rb, line 126
def delete
  if self.client.delete(self.class, self.Id)
    self
  end
end
new_record?() click to toggle source

Returns true if this record has not been persisted in the Force.com database.

# File lib/databasedotcom/sobject/sobject.rb, line 49
def new_record?
  !self.persisted?
end
persisted?() click to toggle source

Returns true if the object has been persisted in the Force.com database.

# File lib/databasedotcom/sobject/sobject.rb, line 44
def persisted?
  !self.Id.nil?
end
reload() click to toggle source

Reloads the record from the Force.com database. Returns self.

client.materialize("Car")
c = Car.find_by_Color("Yellow")
c.reload
# File lib/databasedotcom/sobject/sobject.rb, line 137
def reload
  self.attributes = self.class.find(self.Id).attributes
  self
end
save(options={}) click to toggle source

Updates the corresponding record on Force.com with the attributes of self.

client.materialize("Car")
c = Car.find_by_Color("Yellow")
c.Color = "Green"
c.save

options can contain the following keys:

exclusions # an array of field names (case sensitive) to exclude from save
# File lib/databasedotcom/sobject/sobject.rb, line 102
def save(options={})
  attr_hash = {}
  selection_attr = self.Id.nil? ? "createable" : "updateable"
  self.class.description["fields"].select { |f| f[selection_attr] }.collect { |f| f["name"] }.each { |attr| attr_hash[attr] = self.send(attr) }

  # allow fields to be removed on a case by case basis as some data is not allowed to be saved
  # (e.g. Name field on Account with record type of Person Account) despite the API listing
  # some fields as editable
  if options[:exclusions] and options[:exclusions].respond_to?(:include?) then
    attr_hash.delete_if { |key, value| options[:exclusions].include?(key.to_s) }
  end

  if self.Id.nil?
    self.Id = self.client.create(self.class, attr_hash).Id
  else
    self.client.update(self.class, self.Id, attr_hash)
  end
end
to_key() click to toggle source

Returns a unique object id for self.

# File lib/databasedotcom/sobject/sobject.rb, line 59
def to_key
  [object_id]
end
to_model() click to toggle source

Returns self.

# File lib/databasedotcom/sobject/sobject.rb, line 54
def to_model
  self
end
to_param() click to toggle source

Returns the Force.com Id for this instance.

# File lib/databasedotcom/sobject/sobject.rb, line 64
def to_param
  self.Id
end
update_attribute(attr_name, attr_value) click to toggle source

Updates the corresponding record on Force.com by setting the attribute attr_name to attr_value.

client.materialize("Car")
c = Car.new
c.update_attribute("Color", "Blue")
# File lib/databasedotcom/sobject/sobject.rb, line 73
def update_attribute(attr_name, attr_value)
  update_attributes(attr_name => attr_value)
end
update_attributes(new_attrs) click to toggle source

Updates the corresponding record on Force.com with the attributes specified by the new_attrs hash.

client.materialize("Car")
c = Car.new
c.update_attributes {"Color" => "Blue", "Year" => "2012"}
# File lib/databasedotcom/sobject/sobject.rb, line 82
def update_attributes(new_attrs)
  if self.client.update(self.class, self.Id, new_attrs)
    new_attrs = new_attrs.is_a?(Hash) ? new_attrs : JSON.parse(new_attrs)
    new_attrs.each do |attr, value|
      self.send("#{attr}=", value)
    end
  end
  self
end