Class: SalesforceAdapter

Inherits:
DataMapper::Adapters::AbstractAdapter
  • Object
show all
Includes:
SQL
Defined in:
lib/dm-salesforce-adapter.rb,
lib/dm-salesforce-adapter/sql.rb,
lib/dm-salesforce-adapter/adapter.rb,
lib/dm-salesforce-adapter/version.rb,
lib/dm-salesforce-adapter/property.rb,
lib/dm-salesforce-adapter/resource.rb,
lib/dm-salesforce-adapter/connection.rb,
lib/dm-salesforce-adapter/soap_wrapper.rb

Defined Under Namespace

Modules: Property, Resource, SQL Classes: Connection, SoapWrapper

Constant Summary collapse

Inflector =
::DataMapper::Inflector
VERSION =
"1.0.2"

Instance Method Summary collapse

Methods included from SQL

#comparison_operator, #comparison_statement, #conditions_statement, #equality_operator, #include_operator, #like_operator, #negate_operation, #order, #property_to_column_name, #quote_value, #storage_name

Constructor Details

#initialize(name, uri_or_options) ⇒ SalesforceAdapter

Returns a new instance of SalesforceAdapter.



5
6
7
8
9
10
11
12
13
14
15
16
17
18
# File 'lib/dm-salesforce-adapter/adapter.rb', line 5

def initialize(name, uri_or_options)
  super
  @resource_naming_convention = proc do |value|
    klass = Inflector.constantize(value)
    if klass.respond_to?(:salesforce_class)
      klass.salesforce_class
    else
      value.split("::").last
    end
  end
  @field_naming_convention = proc do |property|
    connection.field_name_for(property.model.storage_name(name), property.name.to_s)
  end
end

Instance Method Details

#aggregate(query) ⇒ Object

www.salesforce.com/us/developer/docs/api90/Content/sforce_api_calls_soql.htm SOQL doesn’t support anything but count(), so we catch it here and interpret the result. Requires ‘dm-aggregates’ to be loaded.



111
112
113
114
115
116
117
118
119
# File 'lib/dm-salesforce-adapter/adapter.rb', line 111

def aggregate(query)
  query.fields.each do |f|
    unless f.target == :all && f.operator == :count
      raise ArgumentError, %{Aggregate function #{f.operator} not supported in SOQL}
    end
  end

  [ execute_select(query).size ]
end

#connectionObject



20
21
22
# File 'lib/dm-salesforce-adapter/adapter.rb', line 20

def connection
  @connection ||= Connection.new(options["username"], options["password"], options["path"], options["apidir"])
end

#create(resources) ⇒ Object

FIXME: DM Adapters customarily throw exceptions when they experience errors, otherwise failed operations (e.g. Resource#save) still return true and thus confuse the caller.

Someone needs to make a decision about legacy support and the consequences of changing the behaviour from broken-but-typical to correct-but-maybe-unexpected. Maybe a config file option about whether to raise exceptions or for the user to always check Model#valid? + Model#salesforce_errors?

Needs to be applied to all CRUD operations.



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/dm-salesforce-adapter/adapter.rb', line 35

def create(resources)
  arr = resources.map do |resource|
    make_salesforce_obj(resource, resource.dirty_attributes)
  end

  result = connection.create(arr)
  result.each_with_index do |record, i|
    resource = resources[i]
    if id_field = resource.class.key.find {|p| p.serial?}
      normalized_value = normalize_id_value(resource.class, id_field, record.id)
      id_field.set!(resource, normalized_value)
    end
  end

  result.size

rescue Connection::SOAPError => e
  handle_server_outage(e)
end

#delete(collection) ⇒ Object



65
66
67
68
69
70
71
72
73
# File 'lib/dm-salesforce-adapter/adapter.rb', line 65

def delete(collection)
  query = collection.query
  keys  = collection.map { |r| r.key }.flatten.uniq

  connection.delete(keys).size

rescue Connection::SOAPError => e
  handle_server_outage(e)
end

#handle_server_outage(error) ⇒ Object



75
76
77
78
79
80
81
# File 'lib/dm-salesforce-adapter/adapter.rb', line 75

def handle_server_outage(error)
  if error.server_unavailable?
    raise Connection::ServerUnavailable, "The salesforce server is currently unavailable"
  else
    raise error
  end
end

#read(query) ⇒ Object

Reading responses back from SELECTS:

In the typical case, response.size reflects the # of records returned.
In the aggregation case, response.size reflects the count.

Interpretation of this field requires knowledge of whether we are expecting an aggregate result, thus the response from execute_select() is processed differently depending on invocation (read vs. aggregate).



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/dm-salesforce-adapter/adapter.rb', line 90

def read(query)
  properties = query.fields
  repository = query.repository

  response = execute_select(query)
  return [] unless response.records

  rows = response.records.inject([]) do |results, record|
    results << properties.inject({}) do |result, property|
      meth = connection.field_name_for(property.model.storage_name(repository.name), property.field)
      result[property] = normalize_id_value(query.model, property, record.send(meth))
      result
    end
  end

  query.model.load(rows, query)
end

#update(attributes, collection) ⇒ Object



55
56
57
58
59
60
61
62
63
# File 'lib/dm-salesforce-adapter/adapter.rb', line 55

def update(attributes, collection)
  query = collection.query
  arr   = collection.map { |obj| make_salesforce_obj(query, attributes) }

  connection.update(arr).size

rescue Connection::SOAPError => e
  handle_server_outage(e)
end