Vai jūs dusmojaties ikreiz, kad redzat, kā rails kontrolierī tiek mainīti instance mainīgie, lai filtrētu datus? Šis raksts ir domāts tieši jums. 🙂
Filtri
Iespējams, jūs to jau esat redzējuši:
# app/controllers/api/v1/things_controller.rb
modulis API
V1 modulis
klase 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
beigas
end
Kāpēc es uzskatu, ka tas ir slikti kods? Jo tas vienkārši padara mūsu kontrolieri treknu. IMHO mums vajadzētu iegūt pēc iespējas vairāk loģikas no kontrolieriem un izmantot ar mērķi saistītu komunālie pakalpojumi vai pakalpojumi. Šajā gadījumā mēs ieviesīsim vispārīgu filtru, kas mums ļaus izmantot daudzos kontrolieros.
Bet pagaidiet, vispirms izanalizēsim pašreizējo kodu. Tas var būt slikts, bet tomēr darbojas. Mums ir dažas sākotnējās darbības jomas (Thing.all) un pēc tam to ierobežo, ja lietotājs ir izturējis saistītais parametrs. Katram filtram mēs faktiski pārbaudām, vai param ir nodots, un, ja ir, tad pārbaudām, mēs piemērojam filtru. Otra lieta ir tā, ka mums nav nepieciešams izmantot ivar, mēs varam izmantot vienkārši vecie vietējie mainīgie.
Labi, tad. Vai mēs nevarētu izmantot kādu pakalpojuma objektu, lai mainītu sākotnējo darbības jomu? Izpilde var izskatīties šādi:
# app/controllers/api/v1/things_controller.rb
modulis API
modulis V1
klase ThingsController < BaseController
def index
scope = Thing.all
things = Things::IndexFilter.new.call(scope, params)
render json: things
end
end
end
beigas
Tagad tas izskatās daudz labāk, bet, protams, mums vēl ir jāievieš filtrs. Ņemiet vērā, ka izsaukuma paraksts būs vienāds visiem resursiem, tāpēc mēs varam izmantot. kādu vispārīgu klasi šim uzdevumam.
# app/services/generic/index_filter.rb
modulis Generic
klase IndexFilter
EMPTY_HASH = {}.freeze
def self.filters
EMPTY_HASH
beigas
def call(scope, params)
apply_filters!(self.class.filters.keys, scope, params)
end
privāts
def apply_filters!(filter_keys, scope, params)
filter_keys.inject(scope.dup) do |current_scope, filter_key|
apply_filter!(filter_key, current_scope, params)
beigas
beigas
def apply_filter!(filter_key, scope, params)
filter = fetch_filter(filter_key)
return scope unless apply_filter?(filter, params)
filter[:apply].call(scope, params)
beigas
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
end
Šķiet sarežģīti? Patiesībā nē - visa burvība notiek 1TP78Pielietojiet_filtrus!. Ņemam sākotnējās darbības jomas dublikātu un tam piemērojam katru filtru.
Kad mēs piemērojam darbības jomu, tas nozīmē, ka mēs mutējam mūsu sākotnējās darbības jomas dublikātu. Un mēs sagaidām, ka filtri tiks ieviesti kā hash in the self.filters metode bērnu klases. Darīsim to.
Tas ir viss! Mēs esam uzrakstījuši vairāk koda, bet vienkāršie filtri izskatīsies tāpat. veids, kā izmantot visus resursus. Mēs esam iztīrījuši kontrolieri no atbildīgā koda filtrēšanas un šim nolūkam nodrošināja ‘specializētu’ klasi, kas seko ļoti skaidra konvencija.