Force CLI + EmacsでのSalesforce開発メモ


# 概要
emacsでApexの開発をしたくて以下の環境で2日くらい試していた時の構成記録。
CUIのテキストエディタ+ターミナルに閉じて無理に開発してみた風になっており、Emacs成分はほとんどなし。

使うもの
- Emacs
- Apex Mode https://github.com/orangatame/apex-mode
- Force CLI https://force-cli.heroku.com/


# 環境
- Mac OS X Yosemite 10.10.3
- Salesforce Summer'15
- Emacs 24.3.1


## Emacs
apex-mode( https://github.com/orangatame/apex-mode )を導入

site-lisp/あたりのパスの通るところに配置して
init.elあたりで `(require 'apex-mode) `する。

java-mode風のインデントとApex特有のシンタックスハイライトがなされる。

## Force.CLI セットアップ
https://force-cli.heroku.com/


* Download for Max OS X を押下すると、約9MBのforceファイルが落ちてくる
* 必要に応じてパスを通す

# 使い方

## Force CLIを組織にログイン
```bash
force login
```

を実行するとブラウザが立ち上がりSalesforceのログイン画面が開くのでログイン（すでにログイン済みであればこの工程はスキップ）。


![image](https://qiita-image-store.s3.amazonaws.com/0/18719/cefcea87-b044-5ae5-a3c9-533639398f5d.png)


ログイン後、Force CLIのアクセス許可を求められるので、「許可」する。

![Kobito.fllBus.png](https://qiita-image-store.s3.amazonaws.com/0/18719/e41257d5-6832-596c-0a95-49449df61503.png "Kobito.fllBus.png")

ブラウザ画面がログイン成功を示す画面に変われば、ブラウザは用済み。

ターミナルは`force login`コマンドの実行が終了し、以下の出力がされる。

```bash
# force login
Logged in as 'your.user@name.com'
#
```

以上でログインは完了。
## Force CLIで組織上のメタデータをダウンロード
```bash
force export metadata
```

metadataはダウンロード先ディレクトリ（実行時点で存在しなくてもよい）
上記実行により、metadataディレクトリ以下にメタデータが落ちてくる

```export実行結果
# force export metadata
Not done yet.  Will check again in five seconds.
Not done yet.  Will check again in five seconds.
Not done yet.  Will check again in five seconds.
Not done yet.  Will check again in five seconds.
Exported to /path/to/metadata

# tree metadata -d 2
metadata
├── applications
├── approvalProcesses
├── assignmentRules
├── autoResponseRules
├── classes
├── communities
├── flows
├── homePageLayouts
├── labels
├── layouts
├── objectTranslations
├── objects
├── pages
├── permissionsets
├── profiles
├── quickActions
├── remoteSiteSettings
├── reportTypes
├── roles
├── sharingRules
├── tabs
└── workflows
```
## Apexクラスの作成
HogeController.clsを作成するために必要なファイルは以下の2つ。
- Apexクラスファイル: HogeController.cls
- メタデータファイル:HogeController.cls-meta.xml

apex-modeによるApexクラスファイルのインデント、ハイライト具合はこんな感じ。
おおよそはJavaモードのインデントを引き継いでおり、Apexのプリミティブについてシンタックスハイライトが登録されている。
![Kobito.rYmhbj.png](https://qiita-image-store.s3.amazonaws.com/0/18719/e5a0d44f-08f8-c339-26e3-9cb3c11d3c95.png "Kobito.rYmhbj.png")
なお、2010年に1発コミットされて以来、開発に動きはない模様。

メタデータファイルの生成やテストメソッドの記述イディオムをちまちまyasnippetに登録し、
auto-complete-modeを併用して幾分つらみを和らげている。

## メタデータを組織にデプロイ
`force deploy`コマンドで作成したクラスのデプロイ実行。
単ファイルではなく、まとめてデプロイする場合は`force import`も用意されている。

```bash:単一ファイルのデプロイ
# force push -f metadata/classes/HogeController.cls
Validating and deploying push...
Deploying now...

Successes - 1
        HogeController: changed
The deployment took 3.316292192s to run.
```

なお、コンパイルエラーなどでデプロイに失敗した場合は Failuresに 対象クラスとエラー理由が出力される。

```bash:コンパイルエラー時の出力
# force push -f metadata/classes/HogeController.cls
Validating and deploying push...
Deploying now...

Failures - 1
"metadata/classes/HogeController.cls", line 14: Error Invalid field total_inventory__c for SObject Merchandise__c

Successes - 0
The deployment took 3.287677949s to run.
```

上ではデプロイ対象の指定にファイルパス指定オプションを使っているが、他にもいくつか指定方法あり。

## Apexテストの実行

`force test テストクラス名` により、テストが実行され、テストの成否やカバレッジが出力される。

```bash:テストの実行
# force test HogeControllerTest                    
Coverage:

  0%   HogeController


Results:

  [PASS]  HogeControllerTest::throughTest
  [FAIL]  HogeControllerTest::failTest: System.AssertException: Assertion Failed: テスト未実装
    Class.HogeControllerTest.failTest: line 7, column 1
```

verboseオプションを付与 `force test -v` することで、詳細な実行ログが出力され、デバッグ出力やガバナ制限項目の消費値が確認できる。

```bash:verbose
# force test -v  HogeControllerTest
{34.0 APEX_CODE,DEBUG;APEX_PROFILING,INFO
01:20:00.068 (68518045)|EXECUTION_STARTED
01:20:00.068 (68568478)|CODE_UNIT_STARTED|[EXTERNAL]|01p28000001HfrB|HogeControllerTest.failTest
01:20:00.069 (69227698)|METHOD_ENTRY|[2]|01p28000001HfrB|HogeControllerTest.HogeControllerTest()
01:20:00.069 (69244379)|METHOD_EXIT|[2]|HogeControllerTest
01:20:00.115 (115722142)|USER_DEBUG|[6]|DEBUG|*******SYSTEM DEBUG**********
01:20:00.115 (115858396)|EXCEPTION_THROWN|[7]|System.AssertException: Assertion Failed: テスト未実装
01:20:00.116 (116098970)|FATAL_ERROR|System.AssertException: Assertion Failed: テスト未実装

Class.HogeControllerTest.failTest: line 7, column 1
01:20:00.116 (116118234)|FATAL_ERROR|System.AssertException: Assertion Failed: テスト未実装

 (中略)

01:20:00.181 (181497661)|TESTING_LIMITS
01:20:00.181 (181497661)|LIMIT_USAGE_FOR_NS|(default)|
  Number of SOQL queries: 0 out of 100
  Number of query rows: 0 out of 50000
  Number of SOSL queries: 0 out of 20
  Number of DML statements: 1 out of 150
  Number of DML rows: 1 out of 10000
  Maximum CPU time: 0 out of 10000
  Maximum heap size: 0 out of 6000000
  Number of callouts: 0 out of 100
  Number of Email Invocations: 0 out of 10
  Number of future calls: 0 out of 50
  Number of queueable jobs added to the queue: 0 out of 50
  Number of Mobile Apex push calls: 0 out of 10

01:20:00.181 (181497661)|CUMULATIVE_LIMIT_USAGE_END
```

ログ出力は結構な量になるので、埋めたSystem.debugの確認だけがしたいときはgrepにパイプしている。

```bash
# force test -v HogeControllerTest | grep -F "USER_DEBUG"
01:23:38.058 (58869426)|USER_DEBUG|[6]|DEBUG|*******SYSTEM DEBUG**********
```
   
 
## executeAnonymousコードの実行
適当なファイル(ex. snippet.cls)に実行したいコードを書いて、
`M-x`からコマンド `force apex snippet.cls` を叩いている。

![Kobito.ReWi3r.png](https://qiita-image-store.s3.amazonaws.com/0/18719/70a0fd44-7626-51cf-0b7f-6c41da0cd443.png "Kobito.ReWi3r.png")


バッファ内のコードを実行するのではなく、ただ保存ファイル指定を実行しているだけなので、ファイルバッファの保存が漏れていたりして悲しくなることがある。
使い捨てバッファの使い方とか、elispをちゃんと使えるとスマートに行けるのかもしれない。
## SOQLの実行
termから`force query`するだけ。

```
# force query "
select id, name, (select name from contacts) from account
order by name
"
 Id                 | Name                                | Contacts          
--------------------+-------------------------------------+-------------------
 00128000003D3jjAAC | Burlington Textiles Corp of America | Name              
                    |                                     |-------------------
                    |                                     | Rogers Jack       
--------------------+-------------------------------------+-------------------
 00128000003D3jlAAC | Dickenson plc                       | Name              
                    |                                     |-------------------
                    |                                     | Young Andy        
--------------------+-------------------------------------+-------------------
 00128000003D3jiAAC | Edge Communications                 | Name              
                    |                                     |-------------------
                    |                                     | Forbes Sean       
                    |                                     | Gonzalez Rose     
--------------------+-------------------------------------+-------------------

(中略)

(13 records)
```

出力フォーマットとしてcsvやjsonも指定できる。


親子クエリの結果で子が複数存在する場合のCSV出力  `[map[a] map[b]]` が見慣れなくて3度見ぐらいした。

```bash:SOQLクエリ結果csv出力
# force query "   
select id, name, (select name from contacts) from account
order by name
" --format:csv
"Name","Contacts","Id"
"Burlington Textiles Corp of America","[map[Name:Rogers Jack]]","00128000003D3jjAAC"
"Dickenson plc","[map[Name:Young Andy]]","00128000003D3jlAAC"
"Edge Communications","[map[Name:Forbes Sean] map[Name:Gonzalez Rose]]","00128000003D3jiAAC"
"Express Logistics and Transport","[map[Name:Davis Josh] map[Name:Levy Babara]]","00128000003D3jnAAC"
"GenePoint","[map[Name:Frank Edna]]","00128000003D3jfAAC"
"Grand Hotels & Resorts Ltd","[map[Name:Bond John] map[Name:Barr Tim]]","00128000003D3jmAAC"
"Pyramid Construction Inc.","[map[Name:Stumuller Pat]]","00128000003D3jkAAC"
"sForce","[map[Name:Nedaerk Siddartha] map[Name:Llorrac Jake]]","00128000003D3jqAAC"
"United Oil & Gas Corp.","[map[Name:Song Arthur] map[Name:Green Avi] map[Name:Boyle Lauren] map[Name:Pavlova Stella]]","00128000003D3jpAAC"
"United Oil & Gas, Singapore","[map[Name:D'Cruz Liz] map[Name:Ripley Tom]]","00128000003D3jhAAC"
"United Oil & Gas, UK","[map[Name:James Ashley]]","00128000003D3jgAAC"
"University of Arizona","[map[Name:Grey Jane]]","00128000003D3joAAC"
"テスト取引先01","<nil>   ","00128000003HpKWAA0"

```



sql-modeみたいに、カーソルがフォーカスしているクエリを`C-c C-c`で送り込んで結果をバッファに出せる、となれば嬉しい...

# tmux内で通知機能を使用する場合のハングアップ回避について
Salesforce組織とAPIでやりとりする都合か、単発のテストクラス実行でも数秒待たされる。
`force notify true` としておくことで、テストやデプロイの終了時にデスクトップ通知を出すことができる。

事前にterminal-notifierを`brew install terminal-notifier`で入れておくこと。

ただし、tmux環境で使用していると、tmux-notifierがハングする現象が発生した。
下記に記載させていただいているページの対応を行い正常に通知が表示されることを確認。

ref.http://qiita.com/shrkw/items/33ba17224235b14a1649


# 願い
Apexクラス作成すると*-meta.xmlを勝手に作ってくれたり、テストクラス生成したり、テストクラスと実装クラスを行き来したり、カレントバッファのファイルをデプロイしたり、regionのApexコードとかSOQLを投げて結果をバッファに流すようなapex-modeの出現への期待が高まる。