Ertu reiður í hvert skipti sem þú sérð breytilegar dæmisbreytur í Rails-stýringunni til að sía gögn? Þessi grein er fyrir þig. 🙂
Hér er tómt.
Síur
Þú hefur líklega séð þetta áður:
# app/controllers/api/v1/things_controller.rb
module forritaskil
module V1
class ThingsController < BaseController
def index
@things = Thing.all
@things = @things.where(size: params[:size]) if params[:size]
@things = @things.where('name ILIKE ?', "%#{params[:name_contains]}%") if params[:name_contains]
render json: @things
end
end
end
end
Af hverju tel ég það vera slæmt kóði? Vegna þess að það gerir einfaldlega stjórnandann okkar feitan. Að mínu mati ættum við að draga út eins mikla rökfræði og við getum úr stýritækjunum og nota tilgangstengda hjálparforrit eða þjónustur. Í þessu tilfelli munum við innleiða almennan síu sem við munum geta til notkunar í mörgum stýritækjum.
En bíddu, fyrst skulum við greina núverandi kóðann. Hann getur verið slæmur en virkar þó. Við höfum nokkurn upphaflegan umfang (hlutur.allt) og síðan eru takmörkun ef notandinn hefur staðist tengdur breytur. Fyrir hvert síupróf athugum við í raun hvort breytan var send og ef svo er, Við beitum síu. Annað er að við þurfum ekki að nota ivar, við getum notað venjulegar staðbundnar breytur.
Allt í lagi, þá. Gætum við ekki notað einhvern þjónustuhlut til að breyta upphaflegu gildissviði? Við framkvæmdina getur þetta litið svona út:
# app/controllers/api/v1/things_controller.rb
module API
module V1
class ThingsController < BaseController
def index
scope = Thing.all
things = Things::IndexFilter.new.call(scope, params)
render json: things
end
end
end
end
Það lítur mun betur út núna, en auðvitað þurfum við enn að innleiða síuna. Athugaðu að undirskrift köllsins verður sú sama fyrir allar auðlindir, svo við getum haft einhver almenn flokkur fyrir þetta verkefni.
# app/services/generic/index_filter.rb
module Generic
class IndexFilter
EMPTY_HASH = {}.freeze
def self.filters
EMPTY_HASH
end
def call(scope, params)
apply_filters!(self.class.filters.keys, scope, params)
end
private
def apply_filters!(filter_keys, scope, params)
filter_keys.inject(scope.dup) do |current_scope, filter_key|
apply_filter!(filter_key, current_scope, params)
end
end
def apply_filter!(filter_key, scope, params)
filter = fetch_filter(filter_key)
return scope unless apply_filter?(filter, params)
filter[:apply].call(scope, params)
end
def apply_filter?(filter, params)
filter[:apply?].call(params)
end
def fetch_filter(filter_key)
self.class.filters.fetch(filter_key) { raise ArgumentError, 'óþekktur síu' }
end
end
end
Virðist flókið? Ekki raunverulega – allt galdurinn gerist í #apply_síur!. Við tökum afrit af upphaflegu umfanginu og beitum hverjum síu á það.
Þegar við beitum umfanginu þýðir það að við breytum afriti upphaflega umfangsins okkar. Og við búumst við að síur verði innleiddar sem skrá í sjálf.síur aðferð af barnastétt. Skulum gera það.
# app/services/things/index_filter.rb
module Things
class IndexFilter (params) {
params[:size].is_a?(String)
},
apply: ->(scope, params) {
scope.where(size: params[:size])
}
}.freeze,
name_contains_filter: {
apply?: ->(params) {
params[:name_contains].is_a?(String)
},
apply: ->(scope, params) {
scope.where('name ILIKE ?', "%#{params[:name_contains]}%")
}
}.freeze
}.freeze
def self.filters
FILTERS
end
end
end
Þetta er það! Við höfum skrifað meira kóða, en einföldu síurnar munu líta eins út. Leið fyrir allar auðlindir. Við höfum hreinsað stýritækið úr kóðanum sem ber ábyrgð. af síun og veitti sérhæfða bekk fyrir þetta tilgangi sem fylgir mjög skýr venja.