将来を見据えたウェブ・アプリケーションの構築:The Codestのエキスパート・チームによる洞察
The Codestが、最先端技術を駆使してスケーラブルでインタラクティブなウェブアプリケーションを作成し、あらゆるプラットフォームでシームレスなユーザー体験を提供することにどのように秀でているかをご覧ください。The Codestの専門知識がどのようにデジタルトランスフォーメーションとビジネス...
データをフィルタリングするためにrailsコントローラのインスタンス変数を変異させるのを見るたびに腹を立てていませんか?この記事はそんなあなたのためのものです。
以前にも見たことがあるだろう:
# app/controllers/api/v1/things_controller.rb
モジュール API
モジュール V1
class ThingsController < BaseController
def index
Things = Thing.all
もし params[:size] なら @things = @things.where(size: params[:size])
params[:name_contains] if @things = @things.where('name ILIKE ?', "%#{params[:name_contains]}%")。
jsonをレンダリングする:をレンダリングする。
終了
終了
終了
終了
なぜ私はそれを悪いと考えるのか? コード?単にコントローラーを太らせるだけだからだ。
私は、コントローラーからできる限り多くのロジックを抽出し、目的に関連した
ユーティリティやサービスを使用します。この場合、次のような汎用フィルタを実装します。
多くのコントローラで使用できる。
でも待って、まずは現在のコードを分析してみよう。悪くても動くんだけどね。
最初のスコープ(すべて
)を通過した場合に制限している。
関連するパラメータが渡されたかどうかをチェックします。それぞれのフィルターについて、パラメータが渡されたかどうか、渡された場合はそのパラメータを実際にチェックする、
フィルタを適用する。もうひとつは、ivarを使う必要がないということだ。
単なる古いローカル変数だ。
わかった。初期スコープを変更するために、何かサービス・オブジェクトを使うことはできないだろうか?
実行はこのようになる:
# app/controllers/api/v1/things_controller.rb
モジュール API
モジュール V1
class ThingsController < BaseController
def index
scope = Thing.all
things = Things::IndexFilter.new.call(scope, params)
json をレンダリングする: things
終了
終了
終了
終了
見た目はかなり良くなったが、もちろんまだフィルターを実装しなければならない。
コールのシグネチャはすべてのリソースで同じであることに注意。
このタスクのための汎用クラス。
# app/services/generic/index_filter.rb
モジュール Generic
クラス IndexFilter
EMPTY_HASH = {}.freeze
def self.filters
EMPTY_HASH
終了
def call(scope, params)
apply_filters!(self.class.filters.keys, scope, params)
end
プライベート
def apply_filters!(filter_keys, scope, params)
filter_keys.inject(scope.dup) do |current_scope, filter_key|.
apply_filter!(filter_key, current_scope, params)
終了
終了
def apply_filter!(filter_key, scope, params)
filter = fetch_filter(filter_key)
apply_filter?(filter, params) でない限り、スコープを返す。
filter[:apply].call(scope, params)
終了
def apply_filter?(filter, params)
filter[:apply?].call(params)
end
def fetch_filter(filter_key)
self.class.filters.fetch(filter_key) { raise ArgumentError, 'unknown filter' }.
end
end
終了
複雑に見える?そんなことはありません。 #apply_filters!
.
最初のスコープの複製を取り、各フィルターを適用する。
スコープを適用するということは、最初のスコープの複製を変異させるということだ。
そして、フィルターがハッシュとして実装されることを期待している。 self.filters
方法
子クラスのやってみよう。
# app/services/things/index_filter.rb
モジュール Things
class IndexFilter (scope, params) { { scope.where(size: params[:size])
scope.where(size: params[:size])
}
}.freeze、
name_contains_filter:{
apply?: ->(params) { { params[:name_contains].is_a?
params[:name_contains].is_a?(String)
},
を適用します:->(スコープ, パラメータ) { { スコープ.where('name ILIKE ?', "%X")
scope.where('name ILIKE ?', "%#{params[:name_contains]}%")
}
}.freeze
}.freeze
def self.filters
フィルター
終了
終了
終了
以上だ!より多くのコードを書きましたが、単純なフィルターは同じように見えます。
すべてのリソースのための方法です。私たちは、責任あるコードからコントローラーをクリーンアップしました。
フィルタリングのための「特別な」クラスが提供された。
明確な規約。
続きを読む