ActiveSalesforce を使ってみたメモ

http://activesfdc.rubyforge.org/ を少し使う機会があったので、そのメモを。
ちなみに、Salesforce の知識はほぼ無い状態で触ってますのであしからず・・・。
なお、rails のバージョンは 2.3.5 です。

Setup と HelloWorld みたいなもの

1. install
公式の通りに

$ gem install activerecord-activesalesforce-adapter

とすると、activerecord-activesalesforce-adapter-2.0.0 がインストールされます*1が、
これだとrails 2.3.5ではパッチ(http://timothynjones.wordpress.com/2008/12/03/patching-activesalesforce-for-rails-222/)を当てないと正しく動きません*2


git(GitHub - oldfartdeveloper/activerecord-activesalesforce-adapter: An ActiveRecord adapter for the Salesforce.com API)には、
gem install で入るものより新しい activerecord-activesalesforce-adapter-2.2.2 があり、
こちらの場合はパッチを当てる必要はありませんでした。
インストール方法は、上記ページでも確認できますが、

$ git clone git://github.com/oldfartdeveloper/activerecord-activesalesforce-adapter.git
$ cd activerecord-activesalesforce-adapter/
$ gem build activerecord-activesalesforce-adapter.gemspec
$ gem install activerecord-activesalesforce-adapter-2.2.2.gem

これでインストールできます。


2. config/database.yml

development:
  adapter:  activesalesforce
  username: <salesforce-username>
  password: <salesforce-password><salesforce-security_token>

password は、「salesforceにログインするときのパスワード」と「セキュリティトークン」を隙間無く並べた文字列です。
また、gitのページでは上記の他に「url:」の項目が説明されていますが、これは記述しなくても動きます。
記述の有無による違いはわかりませんorz


3. モデルを作ります

$ ruby script/generate model Account ―skip-migration

公式でもgitでも書いてない気がしますが、作らないと動きませんでした。
(作らなくても良い方法があるなら教えて下さいorz)


4. 適当なコントローラで Account.all とかやるとデータが取れます

リレーションの無いfind

SalesforceAPIで叩く際には、カラム名が「__c」付きになるっぽいので、そこは気をつける必要があります。

# column_a と column_b で引っかける
Account.find(:first, :conditions => ["column_a__c = ? AND column_b__c = ?", param_a, param_b])

# こっちの書き方でも動きます
Account.find_by_column_a__c_and_column_b__c(param_a, param_b)


で、ここがちょっと微妙なところですが、単に Account.all としても、最大200件しか取ってきません。
でも、limit を大きく設定してあげると200件以上でも取ってきてくれます。
(内部的には、200件ずつループを回して取ってきているようですが。)

# Account に500件のデータが入っていた場合

account = Account.all
p account.length  # --> 200

account = Account.find(:all, :limit => 1000)
p account.length  # --> 500

リレーション(belongs_to, has_many)

belongs_to, has_many は普通に使えます。

しかも、自分でモデルに書かなくても、勝手に作ってくれます。

# log/development.log

Processing AccountsController#index (for ***.***.***.*** at 2010-06-01 13:24:35) [GET]
   Created one-to-one relationship 'master_record' from Account to Account using master_record_id
   Created one-to-one relationship 'parent' from Account to Account using parent_id
   Created one-to-one relationship 'owner' from Account to User using owner_id
   Created one-to-one relationship 'created_by' from Account to User using created_by_id
   Created one-to-one relationship 'last_modified_by' from Account to User using last_modified_by_id
   Created one-to-many relationship 'account_contact_roles' from Account to AccountContactRole using account_id
   Created one-to-many relationship 'histories' from Account to AccountHistory using account_id
   ...

見方としては、

Created one-to-<has_manyならmany / belongs_toならone> relationship '<リレーション名>'
        from <リレーション元モデル名> to <リレーション先モデル名> using <foreign key>

なので、

例えば、上から3つ目の「Created one-to-one relationship 'owner' from Account to User using owner_id」は、

class Account < ActiveRecord::Base
  belongs_to :owner, :class_name => "User", :foreign_key => "owner_id"
end

と同じで、Account.first.owner とかで使えます。


また、一番下の「Created one-to-many relationship 'histories' from Account to AccountHistory using account_id」は、

class Account < ActiveRecord::Base
  has_many :histories, :class_name => "AccountHistory", :foreign_key => "account_id", :dependent => :nullify
end

と同じで、やはり Account.first.histories で使えます。


上のような勝手に作ってくれるリレーションも使えますが、
自分でモデルに書いても正しく動きます。
(foreign_key などにはやはり API名を指定する必要があります)


ただし、SOQLでは、「モデル名__r.カラム名」とかでリレーション先のカラムを条件式に入れたりできるようですが、
これはActiceSalesforceでは使えないようです?*3


例えば、Account と MyCustomObj に 1 対 多 のリレーションが張られているとします。
Account の column1 が 指定した値の MyCustomObj を取得したい場合、
SOQL的にconditions を書くとたぶん下のようになります。
Salesforce全然わかってないので、間違ってたらごめんなさい。。。)

MyCustomObj.find(:all, :conditions => ["MyCustomObj__r.column1 = ?", column1_param])

でも、これは動きません。


ライブラリの中身を少し見ましたが、別テーブルへのエイリアスは削除しているっぽいです。

# /usr/local/lib/ruby/gems/1.8/gems/activerecord-activesalesforce-adapter-2.2.2/lib/active_record/connection_adapters/activesalesforce_adapter.rb の 308, 309行目

# strip away any table alias
column_name.sub!(/\w+\./, '')

この column_name に上でいうと「MyCustomObj__r.column1」が入ってくるので、
column_name はただの「column1」になり、MyCustomObj には column1 というカラムはないので、
そんなカラム無いよということでエラーになってしまいます。

Column not found for #{column_name}!

これが上手いこと動くととても使いやすい気がするんですがね。。。

insert

これも普通に動きます。
newして値をセットして、saveするだけです。
(値をセットする際に、各カラム名API名にしないといけない点だけ注意が必要です)

account = Account.new
account.column1__c = value1
account.column2__c = value2
...

account.save!


とりあえず、実際に使ってみたのはこれくらいですが、
「update」とか「delete」も定義されてるので、同様に使えるのではないかと思います。


*1:2010/05/31 時点

*2:2.2.2のパッチとなっていますが、2.3.5でも動きました

*3:使い方があるなら教えて欲しいですorz