dackdive's blog

新米webエンジニアによる技術ブログ。JavaScript(React), Salesforce, Python など

WEB+DB PRESS Vol.106の「速習 Spring Boot」を読んだ

Spring Framework に入門2日め、という位置づけ。
前回

の最後にもあった通り、最近の WEB+DB PRESS でちょうど Spring Boot 特集があったようなので読んだ。


感想

前回、Spring Framework や Spring Boot について苦労しながら公式ドキュメントを漁った経験からすると
これから Spring Framework に入門する人が最初に目を通しておく本としてとても良いのではないかと思いました。

第1章では Spring Framework とはなにか、から始まり、Spring Boot の特徴や Spring Framework との関係性について順を追って説明されています。

第2章では Spring Boot の基本的な構成や各用語の説明があり、第3, 4章で2つのサンプルアプリを実際に作ります。

最後の第5章では作成したアプリを本番環境で運用するための Tips として、Vagrant による仮想環境を使ったアプリ構築手順や、本番稼働しているアプリのヘルスチェック、バージョン管理方法などが紹介されています。

おすすめの読み方

第3章と第4章で作るアプリの差はそれほど多くない (外部の API を使用する & 定期実行する方法) のと、時間がなければまずは第3章のアプリだけ手を動かしてみるのでも十分だと思います。

また第5章に書かれている内容は IaaS などのサーバーに構築する際の知識なので、Heroku などの PaaS で動かしたいと考えている場合はとりあえずスキップでも問題ないかと。

ただし、第3, 4章で構築したアプリの実行手順は第5章の前半に書かれているのでそこだけ注意。


ソースコード

本書のサポートページ からダウンロードできるほか、自分でも写経したものを前回と同じリポジトリに上げておきます。

https://github.com/zaki-yama/spring-framework-study/tree/master/wdpress106-spring-boot


この書籍で学べること/学べないこと

学べること
  • Spring Framework, Spring Boot とはなにか
  • Spring Boot の全体構成
  • 以下の用語に対するざっくりとした理解
    • テンプレートエンジン Thymeleaf
    • Lombok
    • H2 Database
    • JPA (Java Persistance API), Spring Data
  • 基本的なアプリの作り方
  • ビルドしたアプリの起動方法、実際のサーバーで運用するために必要な手順
学べないこと
  • Maven, Gradle の使い方、設定ファイルの書き方

サンプルアプリは Maven で書かれています。
私は Gradle で書いたので、同じように Gradle でやりたいという方は ↑の GitHub のコードが参考になれば。

また、今回は STS (Spring Tool Suite) という IDE を使って開発を進める想定で、新規プロジェクトのひな形に Maven の設定ファイルも含まれるため、中身について詳しい説明はありません。 (なお、Spring Initializr というツールを使えば IDE を使わなくてもひな形を生成できるため、好きなエディタで開発することは可能です)

一部を除いて、各アノテーションの実際の処理までは説明されていません。
「こういう責務のクラスに対してこういうアノテーションをつけるんだー」とおまじない的に覚える感じです。

  • 実際のデータベースを使ってデータを永続化する方法

サンプルでは H2 というデータベースを使い、アプリをシャットダウンするとデータは削除されます。
データベースにデータを保存するために必要な手順については触れられていませんでした。


学習メモ

第1章


第2章

リポジトリ、サービス、コントローラは対応するアノテーションが提供されている。 クラスに対してアノテーションを付与することで、Spring Boot が自動的に Bean として登録する。 (従来の Spring Framework ではXML で行っていた部分)

  • テンプレートエンジン(Thymeleaf)
    • ビュー機能を実現するテンプレートエンジンとして FreeMaker/Groovy/Thymeleaf/Mustache をサポートしている。


第3, 4章

Gradle で依存追加

  • org.webjars とは
    • クライアントサイドのライブラリを依存解決できるようにしたもの
    • ので、バージョンの指定が必須
dependencies {
    implementation('org.webjars:bootstrap:4.1.0')
}

指定しないと以下のエラーが出る。

> Could not find org.webjars:bootstrap:.
  Required by:
      project :

@SpringBootApplication の内部は以下の3つのアノテーションを持っている

@JsonPropertyJava のフィールド名と(API のレスポンスにおける) JSON のキー名を変換するために用いる


次やろうと思ってること

初回から変わらず、公式チュートリアルのこれをやろうと思っています。

[VSCode]htmlファイル編集中のみタグ移動ショートカットキーを有効にする(主にVSCodeVimユーザ向け)

メモ。
Vim には対応する括弧にカーソルを移動する % キーがあるが、
matchit.vim というプラグインを使うと、html の場合に < から > までではなく
対応するタグ ( <body> ~ </body> ) にジャンプできる。

f:id:dackdive:20181106212254g:plain

VSCode にも Vim エクステンション を入れて使っているが、これと同じ操作ができないか調べてみた。

まさにこれでした。

どうやら、 editor.emmet.action.matchTag というコマンドが標準でサポートされており、これにショートカットキーを割り当てればよさそう。

f:id:dackdive:20181106212729p:plain
editor.emmet.action.matchTag

ただ、問題点が1つ。
これをこのまま設定してしまうと、html 以外のファイルを開いているときに % で対応する括弧を移動するショートカットが効かなくなってしまう。

stack overflow だとバッティングを避けるために Cmd+% (Cmd+Shift+5) に割り当ててるみたいだけど、できれば元の Vim と同じように操作したい。

VSCode でショートカットキーを設定する際、 when というオプションがあるが、これで特定の拡張子のときだけ有効になるように制御できないものか。
と思い調べてみるとちゃんとあった。

How can I add a key binding for only certain file types?
Use the editorLangId context key in your when clause:

{ "key": "shift+alt+a",           "command": "editor.action.blockComment",
                                     "when": "editorTextFocus && editorLangId == csharp" },

指定可能な editorLangId のリストは
https://code.visualstudio.com/docs/languages/identifiers
にある。

というわけで、html ファイルを編集中のみ % を対応するタグの移動に割り当てるには、 keybindings.json に以下のように書けばいい。

{
    "key": "shift+5",
    "command": "editor.emmet.action.matchTag",
    "when": "editorLangId == html"
}


残念なところ

1個だけどうにもならなかった点があって。
Vim だと、 % と Visual Mode を組み合わせて

  • Shift+v で開始タグがある行を選択
  • そのまま % を押し、閉じタグまでを選択
  • d でまるっと削除

とかができたんだけど、VSCode ではできなかった。諦める。

Spring Frameworkに入門した

諸事情で Spring Framework を学ぼうと思い始めてみた。1日目。
これまでのプログラミング経験が

みたいな感じだったので、Java については基本的な構文は知ってるもののビルドツールなどのエコシステムすらよくわからない状態からスタート。


やったこと

Spring Framework の全体像を知る

まずは公式ドキュメントを読んでみる。
https://spring.io/

Projects のページを見るとわかるが、Framework といいつつ Spring ○○ と名のつくプロジェクトが大量にあり、関係性やどれから学ぶべきなのかさっぱりわからない。

またこういうのはたいてい Getting Started なるチュートリアルが提供されているものだが、
Guides を見ても一番最初に読むべきものがわからない。困った。

というわけで日本語の解説サイトを検索し、このあたりに目を通す。

どちらも最初の方しか読んでないが、テックスコアの

およびクラスメソッドさんの連載記事の 第1回第2回 あたりを読むと、おおよそ以下のようなことがわかる。

  • Spring Framework は多数のモジュール(プロジェクト)からなるフレームワークであり、コアとなる機能は DI コンテナ
  • DI コンテナ機能のイメージ
  • Spring を用いた DI の実現方法にはいくつかの種類がある。XML の設定ファイルを使う/アノテーションを使う など


Spring Boot の概要を知る

上に挙げたサイトではまだ、Spring Boot というプロジェクトが何を指しているのか見えてこない。
その割に公式サイトでは一番に出てくるので気になる。
というわけで今度は Spring Boot について調べ、以下のスライドにたどり着く。

これ見て Spring Boot が何たるかをなんとなく理解する。
特に

f:id:dackdive:20181102174621p:plain

この図を見て Spring Framework 本体との関係を理解。
後半の Spring Boot の構成要素については一旦スキップ。


Gradle を学ぶ

次は Spring 公式サイトの Guides にある一番基本っぽいチュートリアル
Getting Started · Building an Application with Spring Boot
をやろうと思ったんだけど、冒頭で Gradle に詳しくなければこのモジュールを参考にするよう促されたのでまずはこっち。

Gradle や Ant, Maven などの名前は聞いたことがあって、ビルドツールということは知っていた。
のでフロントエンドで言う webpack とか gulp 的な位置づけかなと思ってたんだけど、どうやら依存パッケージの管理もやるみたいなので npm/yarn ぽい役割も兼ねているのかな、ぐらいの理解。


公式ガイドの Getting Started · Building an Application with Spring Boot をやる

ようやく Spring Framework のサンプルコードを書いてみる。
このモジュールも別に「まずこれからやるべき」と明記されてるわけではなく、 https://spring.io/guides に並んでいる膨大なモジュールの中から一番基本っぽいものを選んだ。

結果、たしかに Spring を使って簡単なアプリケーションを作ることはできたが、おまじない的に出てくる各種アノテーションが何をやっているのかはさっぱりわからなかった。

一度 Spring Boot を使わずに素の Spring Framework を使ったコードも書いてみないと感覚がつかめないのかな...と感じる。


今回の学び

  • Spring Framework の概要を学んだ
  • Gradle の基礎的な内容を学んだ
    • Gradle はビルドツール。似たようなものに Ant, Maven がある
    • 依存パッケージの管理もやってくれる
  • Spring Boot を使ったアプリケーション構築チュートリアルを試した


次回やろうと思っているもの

Spring Guides の中ではタイトル的に一番とっつきやすそう&面白そうなので。ただし

  • 冒頭の Spring Data REST が Spring Framework 全体の中でどういう位置づけなのかわかっていない
  • Spring Boot を使うぽいので、結局またコアの仕組みの部分はブラックボックス化された状態から変わらなさそう

という懸念はある。


今回は見送った参考サイト

https://github.com/zaki-yama/spring-framework-study

の README にリンクをまとめている。


参考になりそうな書籍

Spring Framework の日本語書籍、調べるといくつか出てくるがどれも Amazon のレビューがイマイチで買うの躊躇している。。。
おすすめがあれば教えてください。

なるべく出版年が新しめのもので探した。
この3冊の中だと、最初に挙げたものがやや難解だけど内容としては良さげ。

また最近の WEB+DB PRESS で Spring Boot 特集があったらしい。

会社にあるはずだから読んでみよう。

[Salesforce]Winter'19: Apex Replay Debuggerの使い方

昨日の Meetup の内容をブログにもまとめておきます。本当はこっちを事前に公開したかった。

なお Meetup で話したときの LT 資料はこちら。


Apex Replay Debugger とは

Winter'19 リリースノート:Apex Replay Debugger を使用してすべての組織を無料でデバッグ (正式リリース)

Apex Replay Debugger は VSCode拡張機能として使える Apex のデバッグ機能です。
Apex を実行した際に出力されるデバッグログファイルを元に、ログファイル出力時の状況を VSCode 上で再現(Replay)できます。

そのため、Apex をステップ実行したり、任意の行にブレークポイントを置いてその時点での変数の状態を確認することができます。


事前に必要なもの Prerequisites

Apex Replay Debugger for Visual Studio Code を参考に必要なものを事前にインストールしておきます。


セットアップ手順

プロジェクトフォルダを作成する

VSCode のコマンドパレット (Mac の場合 Cmd + Shift + P で開く) から

SFDX: Create Project
# または
SFDX: Create Project with Manifest

を実行してプロジェクトを作成します。

または、従来のディレクトリ構成 (src/ の下に package.xml を置き、Force.com Migration Tool などを使ってデプロイ) でも
sfdx-project.json を置きさえすればこの後の VSCode のコマンドを利用することは可能です。

`--src
     `--classes
     `--pages
     `--...
     `--package.xml
`--sfdx-project.json

(その場合、sfdx-project.jsonpath パラメータは force-app -> src にする必要あり)


起動構成ファイル (launch.json) を作成する

.vscode/launch.json ファイルを作成し、以下をコピペします。

{
  // Use IntelliSense to learn about possible attributes.
  // Hover to view descriptions of existing attributes.
  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Launch Apex Replay Debugger",
      "type": "apex-replay",
      "request": "launch",
      "logFile": "${command:AskForLogFileName}",
      "stopOnEntry": true,
      "trace": true
    }
  ]
}

もし、プロジェクトごとに設定するのが面倒な場合、ユーザー設定に "launch": ... という設定を追加することでも可能のようです。

f:id:dackdive:20181031003338p:plain

このあたりは VSCode のドキュメントが参考になります。
https://vscode-doc-jp.github.io/docs/userguide/debugging.html#起動構成


デバッグログレベルを FINEST にする

Replay Debugger を実行するためには、デバッグログレベルを

  • VISUALFORCE: FINER or FINEST
  • APEX_CODE: FINEST

に設定する必要があります。

これは、VSCode 上で

SFDX: Turn On Apex Debug Log for Replay Debugger

というコマンドを実行することで変更できます。


Replay Debugger の使い方


ソースコード上に Checkpoint を置く

処理を中断したりその時点での変数の状態を確認したい行にカーソルを置き、

SFDX: Toggle Checkpoint

を実行します。
行番号の左に赤いマークがつくのがわかります。これを Checkpoint と呼びます。

f:id:dackdive:20181031004307g:plain


Checkpoint を組織に反映させる

設置した Checkpoint は組織に反映させる必要があります。
事前に Scratch Org を作成したり、 sfdx force:auth:web:login などで認証を済ませておいてから

SFDX: Update Checkpoints in Org

を実行します。Output パネルに

Starting SFDX: Update Checkpoints in Org
SFDX: Update Checkpoints in Org, Step 1 of 6: Retrieving org information
SFDX: Update Checkpoints in Org, Step 2 of 6: Retrieving source and line information
SFDX: Update Checkpoints in Org, Step 3 of 6: Setting typeRefs for checkpoints
SFDX: Update Checkpoints in Org, Step 4 of 6: Clearing existing checkpoints
SFDX: Update Checkpoints in Org, Step 5 of 6: Uploading checkpoints
SFDX: Update Checkpoints in Org, Step 6 of 6: Confirming successful checkpoint creation
Ending SFDX: Update Checkpoints in Org

のように出力されれば成功です。

ちなみにこれは、開発者コンソールでエディタを開き、Checkpoint を設置したのと同じことをやっています。

f:id:dackdive:20181031004947p:plain


Apex を実行する

Checkpoint を設置した Apex コードを実行します。
実行にはいくつか方法がありますが、好きな方法で OK です。

  1. Visualforce ページなどで使われている Apex の場合、画面を直接操作する
  2. 該当の Apex のテストコードを実行する
  3. Execute Anonymous Window などで実行する

余談ですが特に VSCode を使った開発の場合、2 に関してはテストクラスを開くとメソッドの上に Run Test というリンクが表示されており、そこからメソッドごとにテストを実行させることができたり

f:id:dackdive:20181031005645p:plain

3 に関しては、適当なスクリプトファイルを作成し、開いた状態で

SFDX: Execute Anonymous Apex with Editor Contents

を実行すれば、開発者コンソールでの Open Execute Anonymous Window とほぼ同等のことを行えて便利です。


デバッグログファイルを取得する

Apex を実行したら、その時のログファイルをローカルにダウンロードします。
これについても VSCode 上で

SFDX: Get Apex Debug Logs...

を実行すると、新しい順にデバッグログの一覧が表示されるので、そこから選択するとダウンロードすることができます。

f:id:dackdive:20181031010027p:plain

ログは .sfdx/tools/debug/logs/ の下に保存されます。


デバッグログファイルから Replay Debugger を起動する

該当のログファイルを開いた状態で、コマンドパレットまたは右クリックして表示されるメニューから

SFDX: Launch Apex Replay Debugger with Current File

を実行します。
ログパネルが開き、またログファイルの1行目に黄色いカーソルが表示され、デバッグ用のメニューが表示されます。

f:id:dackdive:20181031010756p:plain


デバッグ:Apex をステップ実行したり、変数の中身を覗いてみる

この状態で |▶ ボタン (Continue: F5) を押すと、先ほどの Checkpoint まで処理が進み、該当行でストップしていることがわかります。
左側の VARIABLES ペインを見ると、この時点での変数に格納されている値を確認することができます。

f:id:dackdive:20181031011213p:plain

また、行番号の左のスペースをクリックすることで、 Checkpoint を設置した行以外の行にも Breakpoint を設置することができます。
次にデバッガを実行したときにはこの Breakpoint でも処理が中断されるようになります。
(Checkpoint と Breakpoint の違いは後述)


注意事項

今のところ以下の制約があるようです。

  • Checkpoint は最大5個まで。また有効期限は30分
  • 一度に実行できるのは1個のログファイルまで
    • そのため、非同期処理などのデバッグは注意が必要

詳細は Apex Replay Debugger の「Considerations」の項 を一読されることをおすすめします。


その他

Checkpoint と Breakpoint

SFDX: Toggle Checkpoint を使い、また組織にもデプロイした Checkpoint と、VSCode 上だけで定義した Breakpoint は、できること(得られる情報)が異なります。
「Set Breakpoints and Checkpoints」「Considerations」 あたりを読む限り、以下のような違いがあるようです。

For more information than line breakpoints provide, add checkpoints. You can set up to five checkpoints to get heap dumps when lines of code run. All local variables, static variables, and trigger context variables have better information at checkpoints. Trigger context variables don’t exist in logs and are available only at checkpoint locations.

トリガーのコンテキスト変数をはじめとして、Checkpoint の方が持っている情報が多いみたいです。

  • Long string variable values are truncated at breakpoints. At checkpoints, heap-dump-augmented variables have full strings.
  • When viewing a standard or custom object at a breakpoint, you can drill down only to the object’s immediate child variables (one level deep). At checkpoints, heap-dump-augmented variables have full drill-down to child standard objects, not only to immediate children.

Checkpoint の方が長い文字列も省略されなかったり、ネストしたオブジェクトの情報なども完全に保持しているらしい。


Apex Debugger との違い

名前がややこしいですが、VSCode拡張機能として Apex Debugger というのもあります。

ただ、こちらは

This extension enables VS Code to use the real-time Apex Debugger with your scratch orgs and to use ISV Customer Debugger with your subscribers’ sandbox orgs.

というところから対象組織は Scratch Org (および ISV 向けの機能については Sandbox Org) に限定されたり、

  • One Apex Debugger session is included with Performance Edition and Unlimited Edition orgs.
  • To purchase Apex Debugger sessions for Enterprise Edition orgs, or to purchase more sessions for orgs that already have allocated sessions, contact Salesforce.

から Performance Edition, Unlimited Edition 以外では有償のようです。

[Salesforce]UserRecordAccessでレコードの参照・編集権限をチェックする

こんなことやりたい

Apex で SOQL を実行してレコードを取得するとき、実行ユーザが編集権限のあるレコードだけ返したい。
このとき、レコードに対し参照権限だけあると SOQL では取得できてしまうため適宜フィルターする必要がある。


先にまとめ

UserRecordAccess オブジェクトというのがあるので、それを使いましょう。
UserRecordAccess | SOAP API 開発者ガイド | Salesforce Developers

UserRecordAccess には Has*Access ( *EditDelete など) という項目があるので、チェックしたい権限に応じた項目を使います。

また API バージョン 30.0 以降であれば UserRecordAccess は各 SObject のリレーション項目として取得できます。

例:SELECT Id, Name, Email, UserRecordAccess.HasEditAccess FROM Contact


方法

ほぼほぼこちらのテラスカイさんの記事にある通りです。感謝。
が、少々アップデートがあるみたいなので順を追って記載します。

ブログ記事にあるように、あるユーザが特定のレコードに対して参照・編集・削除などの権限を有しているかを判定するためのオブジェクトとして UserRecordAccess というオブジェクトがあります。

UserRecordAccess | SOAP API 開発者ガイド | Salesforce Developers

上のオブジェクト定義を見るとわかりますが、 UserRecordAccess には以下の項目があります。

  • 各種の権限があるかどうかのフラグ項目
    • HasReadAccess : 参照
    • HasEditAccess : 編集
    • HasDeleteAccess : 削除
    • ほか
  • UserId
  • RecordId

UserIdRecordId を WHERE 句に指定することで、「あるユーザが特定のレコードに対して権限を有しているか」を判断できるわけです。

なので、冒頭に記載したように「編集権限のあるレコードだけ返す」を実現するためには、こういったメソッドを用意すればいいでしょう。

public List<Contact> getEditableContacts() {
    Map<Id, Contact> contactMap = new Map<Id, Contact>([
        SELECT Id, Name, Email FROM Contact
    ]);
                                                         
    List<UserRecordAccess> accesses = [
        SELECT
            RecordId, HasEditAccess
        FROM
            UserRecordAccess
        WHERE
            UserId = :UserInfo.getUserId()
        AND
            RecordId IN :contactMap.keySet()
    ];
                                                         
    List<Contact> result = new List<Contact>();
    for (UserRecordAccess access : accesses) {
        if (access.HasEditAccess) {
            result.add(contactMap.get(access.RecordId));
        }
    }
    return result;
}


制限事項

というわけで便利なオブジェクトですが、制限事項がいくつかあります。
↑のテラスカイさんのブログ記事でも述べられていますが、個人的に気になったものを挙げると

  • RecordId は単一または複数のレコードを WHERE 句に指定できるが、 UserId は単一のみ
    • UserId IN *** とすると Error: Can filter on only UserId = [single ID], either RecordId = [single ID] or RecordId IN [list of IDs], and Has*Access = true というエラーが出る
  • 一度に取得できるレコードは 200 件までで、SOQL の結果が 200 件を超えると QueryException が発生する

特に後者の制限が厳しいですね。。。


より良い方法

制限事項を回避できる何か良い方法はないのかと SOAP API 開発者ガイド を最後まで読むと、

API バージョン 30.0 以降では、UserRecordAccess はレコードの外部キーになります。このオブジェクトをルックアップまたは外部キーとして使用する場合は、UserId または RecordId 項目を検索条件に使用したり、指定したりすることはできません。前記のサンプルクエリは、次のように実行できます。
SELECT Id, Name, UserRecordAccess.HasReadAccess, UserRecordAccess.HasTransferAccess, UserRecordAccess.MaxAccessLevel FROM Account

お! UserRecordAccessAPI バージョン 30.0 以降だと任意の SObject のリレーション項目として一緒に取得できるようです。
なので、先ほどのサンプルは以下のように書き直すことができます。

/** Since API version 30.0 **/
public List<Contact> getEditableContacts() {
    List<Contact> contacts = [
        SELECT
            Id, Name, Email,
            UserRecordAccess.HasEditAccess
        FROM
            Contact
    ];
    List<Contact> result = new List<Contact>();
    for (Contact contact : contacts) {
        System.debug('Contact [' + contact.Name + '] is editable?: ' + contact.UserRecordAccess.HasEditAccess);
        if (contact.UserRecordAccess.HasEditAccess) {
            result.add(contact);
        }
    }
    return result;
}

これにより、制限事項のうち「一度に取得できるレコードは 200 件まで」は回避できました。よかった。

ただし、 UserRecordAccess.Has*Access は WHERE 句に直接は指定できず、以下のエラーになります。

Error: You cannot filter on UserRecordAccess when used in a relationship


注意事項

Task, Event など一部の標準オブジェクトについては、UserRecordAccess を項目として取得する方法は使えないようです。

UserRecordAccess オブジェクトへのクエリが一度に 200 件までという制限があることなど、内部的にパフォーマンスに影響ありそうなことをやってそうな香りがしますね。
実際に使うときはパフォーマンス面での影響を少し意識しなければ。


サンプルコード

https://github.com/zaki-yama/salesforce-samples/tree/master/user-record-access

[Salesforce]ワークフローの時間ベースのアクションについて

今さらながら時間ベースのワークフローを使うことがあったのでメモ。

時間ベースのワークフローとは

f:id:dackdive:20180817101627p:plain

ここのこと。

時間ベースのワークフローで何ができる?

時間ベースじゃない方のワークフローアクションだと、ルール条件に一致した場合レコードの作成または編集直後にアクションが実行される。
これに対し、時間ベースのアクションは文字通り、アクションの実行を将来のある時点に予約しておくことができる。

具体的なユースケースとして、たとえば
「金額が1000万円以上の大型商談について、完了予定日の7日前になった時点でクローズされていなければ、商談所有者にリマインドメールを送信する」
といったことができる。


将来のある時点、は何を基準に決定される?

レコードの作成日やルールが適用された日だけでなく、対象のオブジェクトの日付項目の値を基準にアクション実行のタイミングを制御することもできる。

タイミングの制御は基準となる項目の日付から数えて◯日前(後)、といった指定方法になる。

f:id:dackdive:20180817102652p:plain

↑では例として、商談オブジェクトのレコードに対し、「完了予定日」の7日前に実行されるアクションを設定している。


アクションが予約されたことをどうやって確認するの?

LEXであれば 環境>監視>時間ベースのワークフロー から、現在セットされている時間ベースのアクションを確認できる。

f:id:dackdive:20180817103443p:plain


アクションは一度予約されたら必ず実行される?

NO。
一度ルール条件に合致してアクションがセットされても、その後レコードが更新されてルール条件を満たさなくなれば、アクションは実行されない。

冒頭の例でいうと、

  • レコード作成時は金額1000万以上だったが、その後1000万未満になった
  • 完了予定日の7日前より以前に商談がロストした/クローズした

場合はリマインドメールは送信されない。

参考:FAQ - 時間ベースのワークフロー

キューの待機中のアクションは、必ず起動されますか?
いいえ。時間ベースのアクションは、プロセスまたはワークフロールールのルール条件が “false” と評価される限り、ワークフローキューに留まります。ルールが評価されたときに、レコードがルール条件に一致しなくなると、Salesforce はそのレコードについてキューにある時間ベースのアクションを削除します。


評価条件が「作成されたとき」か「作成されたとき、およびその後基準を満たすように編集されたとき」で挙動にどういう違いがあるの?

これは時間ベースじゃないアクションと同じ。
「作成されたとき」の場合はレコード作成時しか評価しないので、たとえば冒頭の例でいうと

  • 一度ロストした商談を再度 Prospecting に戻した(その時点ではまだ完了予定日の7日前より以前だった)

という操作をした場合も、リマインドメールの対象になるわけではない。

参考:FAQ - 時間ベースのワークフロー

レコードの待機中のアクションをキューに戻すことはできますか?
はい。レコードが更新され、評価条件をレコードが [作成されたとき、およびその後基準を満たすように編集されたとき] に設定した場合は、自動的にキューに戻されます (レコードは、入力された条件を以前に満たしていない必要があります)。

※これは余談だが、この直後にある例は日本語記事だとなぜか間違っている(1個前のと同じ例になっている)


プロセスビルダーでも同じことできないの?

できますね。「スケジュール済みアクション」という名前みたい。

f:id:dackdive:20180817105810p:plain

注意点として、プロセスの開始条件を「レコードを作成または編集したとき」にしている場合、スケジュール済みアクションを使えるようにするには

「レコードに指定の変更が行われた場合にのみアクションを実行しますか?」

にチェックを入れる必要がある。


リファレンス

Pipenvで仮想環境をプロジェクトディレクトリの下に作る(PIPENV_VENV_IN_PROJECT)

ちょいメモ。

久しぶりに Python を書くにあたって環境構築する際、 2018年のPythonプロジェクトのはじめかた - Qiita を見て Pipenv を使ってみた。

普通に pipenv shell で仮想環境を作成すると

 ~/workspace/Python/pipenv-sandbox $ pipenv shell
Creating a virtualenv for this project…
Using /Users/yamazaki/.pyenv/versions/3.6.5/bin/python3.6m (3.6.5) to create virtualenv…
⠋Running virtualenv with interpreter /Users/yamazaki/.pyenv/versions/3.6.5/bin/python3.6m
Using base prefix '/Users/yamazaki/.pyenv/versions/3.6.5'
New python executable in /Users/yamazaki/.local/share/virtualenvs/pipenv-sandbox-9fl74ZVx/bin/python3.6m
Also creating executable in /Users/yamazaki/.local/share/virtualenvs/pipenv-sandbox-9fl74ZVx/bin/python
Installing setuptools, pip, wheel...done.

Virtualenv location: /Users/yamazaki/.local/share/virtualenvs/pipenv-sandbox-9fl74ZVx
Spawning environment shell (/bin/zsh). Use 'exit' to leave.
. /Users/yamazaki/.local/share/virtualenvs/pipenv-sandbox-9fl74ZVx/bin/activate
 ~/workspace/Python/pipenv-sandbox $ . /Users/yamazaki/.local/share/virtualenvs/pipenv-sandbox-9fl74ZVx/bin/activate
(pipenv-sandbox-9fl74ZVx)  ~/workspace/Python/pipenv-sandbox $

というように、プロジェクトディレクトリとは別のグローバルな場所( ~/.local/share/virtualenvs/ )に仮想環境が作られる。

これを、

$ virtualenv venv

したときと同じように、各プロジェクトディレクトリの下に作成したい。

Configuration With Environment Variables を読むと

  • PIPENV_VENV_IN_PROJECT — If set, use .venv in your project directory instead of the global virtualenv manager pew.

とあり、 .zshrc

export PIPENV_VENV_IN_PROJECT=true

を追加したところ、プロジェクトディレクトリの下に仮想環境が作成されるようになった。

 ~/workspace/Python/pipenv-sandbox $ pipenv shell
Creating a virtualenv for this project…
Using /Users/yamazaki/.pyenv/versions/3.6.5/bin/python3.6m (3.6.5) to create virtualenv…
⠋Running virtualenv with interpreter /Users/yamazaki/.pyenv/versions/3.6.5/bin/python3.6m
Using base prefix '/Users/yamazaki/.pyenv/versions/3.6.5'
New python executable in /Users/yamazaki/workspace/Python/pipenv-sandbox/.venv/bin/python3.6m
Also creating executable in /Users/yamazaki/workspace/Python/pipenv-sandbox/.venv/bin/python
Installing setuptools, pip, wheel...done.

Virtualenv location: /Users/yamazaki/workspace/Python/pipenv-sandbox/.venv
Spawning environment shell (/bin/zsh). Use 'exit' to leave.
. /Users/yamazaki/workspace/Python/pipenv-sandbox/.venv/bin/activate
 ~/workspace/Python/pipenv-sandbox $ . /Users/yamazaki/workspace/Python/pipenv-sandbox/.venv/bin/activate
(pipenv-sandbox) ~/workspace/Python/pipenv-sandbox $ 

# .venv ディレクトリが作られている
(pipenv-sandbox) ~/workspace/Python/pipenv-sandbox $ ls -al
total 8
drwxr-xr-x  4 yamazaki  staff  136  5 16 02:33 ./
drwxr-xr-x  8 yamazaki  staff  272  5 16 02:08 ../
drwxr-xr-x  5 yamazaki  staff  170  5 16 02:33 .venv/
-rw-r--r--  1 yamazaki  staff  138  5 16 02:08 Pipfile

.venv というディレクトリ名は変更できないのかな。
個人的にはこちらの方が好みだけど、両者にメリットデメリットあるのかは不明。

ちなみに

仮想環境が作られた場所を確認するには --venv オプションを使う。

(pipenv-sandbox-9fl74ZVx)  ~/workspace/Python/pipenv-sandbox $ pipenv --venv
/Users/yamazaki/.local/share/virtualenvs/pipenv-sandbox-9fl74ZVx