v1.0以降、すべてのSailsアプリには、複数の場所でNode.jsコードを共有できるシンプルなユーティリティであるヘルパーの組み込みサポートが付属しています。これにより、コードの重複を避け、バグを減らし、書き直しを最小限に抑えることで、開発効率が向上します。actions2と同様に、アプリのドキュメント作成も非常に簡単になります。
Sailsでは、ヘルパーは、繰り返し使用するコードを別のファイルにまとめ、そのコードをさまざまなアクション、カスタムレスポンス、コマンドラインスクリプト、ユニットテスト、さらには他のヘルパーでも再利用するための推奨されるアプローチです。ヘルパーを使用する必要はありません。実際、すぐに必要にならないかもしれません。しかし、コードベースが拡大するにつれて、ヘルパーはアプリの保守性にとってますます重要になります(それに、本当に便利です)。
たとえば、クライアントリクエストに応答するためにNode.js/Sailsアプリが使用するアクションを作成する過程で、コードが複数の場所で繰り返されていることに気づくことがあります。これは非常にバグが発生しやすく、迷惑なのは言うまでもありません。幸い、優れた解決策があります。重複するコードをカスタムヘルパーの呼び出しに置き換えます。
const greeting = await sails.helpers.formatWelcomeMessage('Bubba');
sails.log(greeting);
// => "Hello, Bubba!"
ヘルパーは、
sails
アプリインスタンスにアクセスできる限り、コードのほぼどこからでも呼び出すことができます。
以下に、シンプルで適切に定義されたヘルパーの例を示します。
// api/helpers/format-welcome-message.js
module.exports = {
friendlyName: 'Format welcome message',
description: 'Return a personalized greeting based on the provided name.',
inputs: {
name: {
type: 'string',
example: 'Ami',
description: 'The name of the person to greet.',
required: true
}
},
fn: async function (inputs, exits) {
const result = `Hello, ${inputs.name}!`;
return exits.success(result);
}
};
シンプルですが、このファイルは優れたヘルパーのいくつかの特徴を示しています。ユーティリティが何をするのかをすぐに理解できるフレンドリーな名前と説明で始まり、ユーティリティの使用方法を簡単に確認できるように入力が記述されており、可能な限り最も単純な方法で離散的なタスクを完了します。
fn
関数ヘルパーのコアは、ヘルパーが実行する実際のコードを含むfn
関数です。この関数は、2つの引数、inputs
(入力値の辞書、または「argin」)とexits
(コールバック関数の辞書)を取ります。fn
の役割は、arginを利用して処理し、提供された出口の1つをトリガーして、ヘルパーを呼び出したコードに制御を戻すことです。return
を使用して呼び出し元に出力を提供する一般的なJavaScript関数とは対照的に、ヘルパーはexits.success()
に渡すことでその結果値を提供することに注意してください。
ヘルパーの宣言された入力は、一般的なJavaScript関数のパラメータに似ています。コードが処理する必要のある値を定義します。ただし、標準のJavaScript関数のパラメータとは異なり、入力は自動的に検証されます。ヘルパーが、対応する入力に対して間違った型のarginを使用して呼び出された場合、または必須の入力の値が欠落している場合は、エラーが発生します。したがって、ヘルパーは自己検証されます。
ヘルパーの入力は、inputs
ディクショナリで定義されます。各入力定義は、少なくともtype
プロパティで構成されます。ヘルパー入力は、次のような型をサポートしています。
string
- 文字列値number
- 数値(整数と浮動小数点数の両方が有効)boolean
- 値true
またはfalse
ref
- JavaScript変数参照(辞書、配列、関数、ストリームなど、任意の値を指定できます)これらは、モデル属性の定義ですでに慣れ親しんでいる可能性のあるデータ型(および関連するセマンティクス)と同じです。したがって、予想どおり、defaultsTo
プロパティを設定することで、入力のデフォルト値を指定できます。または、required: true
を設定することで必須にすることができます。allowNull
、およびisEmail
のようなほぼすべての高レベルの検証ルールも使用できます。
ヘルパーを呼び出すときに渡す引数は、そのヘルパーの宣言されたinputs
のキーの順序に対応します。または、名前でarginを渡す場合は、.with()
を使用します。
const greeting = await sails.helpers.formatWelcomeMessage.with({ name: 'Bubba' });
出口は、ヘルパーが持つ可能性のあるすべての異なる結果(良いものも悪いものも)を記述します。すべてのヘルパーは、自動的にerror
およびsuccess
出口をサポートします。ヘルパーを呼び出すときに、そのfn
がsuccess
をトリガーした場合、正常に返されます。しかし、そのfn
がsuccess
以外の出口をトリガーした場合、エラーがスローされます(.tolerate()
が使用されていた場合を除く)。
必要に応じて、他のカスタム出口(「例外」と呼ばれる)を公開して、ヘルパーを呼び出すユーザーランドコードが特定の例外的なケースを処理できるようにすることもできます。これにより、エラーを宣言および交渉することが簡単になり、コードの透明性と保守性が保証されます。
ヘルパーの例外(カスタム出口)は、
exits
ディクショナリで定義されます。すべてのカスタム例外に明示的なdescription
プロパティを指定することが推奨されます。
「inviteNewUser」というヘルパーが、カスタムのemailAddressInUse
出口を公開しているとします。提供されたメールがすでに存在する場合、ヘルパーのfn
はこのカスタム出口をトリガーして、ユーザーランドコードがこの特定のシナリオを処理できるようにします。結果値を混乱させたり、余分なtry/catch
ブロックに頼ったりする必要はありません。
たとえば、このヘルパーが、独自の「badRequest」出口を持つアクション内から呼び出された場合
const newUserId = sails.helpers.inviteNewUser('[email protected]')
.intercept('emailAddressInUse', 'badRequest');
上記の凝った略語は、次のように記述するより迅速な方法にすぎません。
.intercept('emailAddressInUse', (err)=>{ return 'badRequest'; });
.intercept()に関しては、常にこれらのエラーを手動で交渉するためにカスタムtry/catchブロックを記述する必要がないようにするためのもう1つのショートカットにすぎません。
内部的には、ヘルパーのfn
は、特別な出口信号をスローするか、出口コールバック(例:exits.success('foo')
)を呼び出すことによって、その出口の1つをトリガーする必要があります。ヘルパーがsuccess出口を介して結果(例:'foo'
)を返した場合、それがヘルパーの戻り値になります。
注:成功以外の出口の場合、Sailsは、必要に応じて、出口の事前定義された説明を使用して、適切なJavaScriptエラーインスタンスを自動的に作成します。
デフォルトでは、すべてのヘルパーは非同期と見なされます。これは安全なデフォルトの仮定ですが、常に当てはまるわけではありません。ヘルパーが同期であることを確信している場合は、sync: true
プロパティを使用してSailsに指示することで、パフォーマンスを最適化できます。これにより、ユーザーランドコードはawait
なしでヘルパーを呼び出すことができます。ただし、sync
をtrue
に設定する場合は、fn: async function
をfn: function
に変更することを忘れないでください。
注:
await
なしで非同期ヘルパーを呼び出すことは機能しません。
req
へのアクセス特にアクション内から使用するためにリクエストヘッダーを解析するヘルパーを設計している場合は、リクエストオブジェクトの既存のメソッドやプロパティを利用する必要があります。アクションのコードがreq
をヘルパーに渡せるようにする最も簡単な方法は、type: 'ref'
入力を定義することです。
inputs: {
req: {
type: 'ref',
description: 'The current incoming request (req).',
required: true
}
}
次に、アクションでヘルパーを使用するには、次のようなコードを記述します。
const headers = await sails.helpers.parseMyHeaders(req);
Sailsには、新しいヘルパーを自動的に作成するために使用できる組み込みジェネレーターが用意されています。
sails generate helper foo-bar
これにより、api/helpers/foo-bar.js
ファイルが作成され、コードでsails.helpers.fooBar
としてアクセスできるようになります。最初に作成されるファイルは、入力がなく、デフォルトの出口(success
とerror
)のみを持つ汎用ヘルパーで、実行時にすぐにsuccess
出口をトリガーします。
Sailsアプリがロードされるたびに、api/helpers/
内のすべてのファイルが見つかり、関数にコンパイルされ、ファイル名のキャメルケースバージョンを使用してsails.helpers
ディクショナリに保存されます。次に、任意のヘルパーをawait
で呼び出して、argin値をいくつか指定するだけで、コードから呼び出すことができます。
const result = await sails.helpers.formatWelcomeMessage('Dolly');
sails.log('Ok it worked! The result is:', result);
これは、モデルメソッドである
.create()
からすでに慣れている可能性のある使い方とほぼ同じです。
ヘルパーがsync
プロパティを宣言している場合は、await
なしで呼び出すこともできます。
const greeting = sails.helpers.formatWelcomeMessage('Timothy');
ただし、await
を削除する前に、ヘルパーが実際に同期していることを確認してください。await
がない場合、非同期ヘルパーは実行されません。
アプリケーションで多数のヘルパーを使用する場合は、関連するヘルパーをサブディレクトリにグループ化すると便利です。たとえば、次のディレクトリ構造で整理された、多数のuser
ヘルパーといくつかのitem
ヘルパーがあるとします。
api/
helpers/
user/
find-by-username.js
toggle-admin-role.js
validate-username.js
item/
set-price.js
apply-coupon.js
これらのヘルパーを呼び出す場合、各サブフォルダー名(例:user
とitem
)は、sails.helpers
オブジェクトの追加のプロパティレイヤーになるため、sails.helpers.user.findByUsername()
を使用してfind-by-username.js
を呼び出し、sails.helpers.item.setPrice()
を使用してset-price.js
を呼び出すことができます。
詳細については、カスタムヘルパーとオーガニックの操作に関する一般的なヒントとコツを含む、このユースケースの詳細を説明している、Ryan EmberlingとMike McNeilの間の会話を読むことができます。
より詳細なエラー処理のために(そして、エラーではない例外的なケースでも)、何らかのエラーコードを設定してから、エラーを検出することに慣れているかもしれません。このアプローチはうまく機能しますが、時間がかかり、追跡が困難になる可能性があります。
幸い、Sailsヘルパーでエラーを便利に処理する方法がいくつかあります。詳細については、.tolerate()、.intercept()、および特別な出口信号に関するページを参照してください。
この例での使用法は過剰ですが、notUnique
のようなカスタム出口に依存することが役立つシナリオを想像するのは簡単です。それでも、毎回すべてのカスタム出口を処理する必要はありません。理想的には、何らかの機能を実装するため、あるいはユーザーエクスペリエンスを向上させるため、またはより良い内部エラーメッセージを提供するために必要な場合にのみ、ユーザーランドコードでカスタム出口を処理する必要があります。
幸い、Sailsヘルパーは「自動出口転送」をサポートしています。これは、ユーザーランドコードが、ケースバイケースで、必要な数のカスタム出口と統合できることを意味します。言い換えれば、ヘルパーを呼び出すときに、必要ない場合はカスタムのnotUnique
出口を完全に無視しても問題ありません。そうすることで、コードは可能な限り簡潔で直感的なままになります。また、状況が変化した場合は、後でカスタム出口を処理するようにコードを修正することもできます。
sails-hook-organics
には、多くの一般的なユースケースに対応する、いくつかの無料のオープンソースでMITライセンスのヘルパーが付属しています。見てみましょう!