Rails APIでの.whereメソッドの連鎖
概要
提出物を収集するために、Rails API を使用して構築された Web アプリがあります。属性 (場所、サイズなど) が一致する特定のストアに電子メールを送信するフォームを送信しています。これらは次のフィールドに基づいて照合されます
また、ストアがすべてのリードを受け取るための属性もあり、true に設定されている場合は、前のオプションのいずれかをオーバーライドします。
Rails API のhard_worker.rb では、どのストアにリードを送信するかをクエリするために、次のフィールドをチェックさせています。条件文の連鎖が正しくないことはわかっていますが、正しく送信するために再フォーマットする方法がわかりません。助けていただければ幸いです。ありがとうございます!
def build_filters_obj
filters = []
filters.push 'location_north' if @lead.location_north
filters.push 'location_east' if @lead.location_east
filters.push 'location_south' if @lead.location_south
filters.push 'location_west' if @lead.location_west
filters.push 'location_other' if @lead.location_other
return filters
end
def perform(lead_id)
@lead = Lead.find(lead_id)
lead_email = ValidEmail2::Address.new(@lead.email)
UserNotifierMailer.send_signup_email(@lead).deliver if lead_email.valid?
@stores = Store.all
@stores = @stores.where.not(email: [nil, ''])
n = @lead.guests_total.to_i
@stores = @stores.where("capacity_min <= ? AND capacity_max >= ?", n, n)
@stores = @stores.where(:type_unique => true) if @lead.store_type_unique
@stores = @stores.where(:type_dining => true) if @lead.store_type_dining
@stores = @stores.where(:type_hotel => true) if @lead.store_type_hotel
filters = build_filters_obj
filters.each do |filter|
@stores = @stores.where(filter.to_sym => true)
end
@stores = @stores.or(Store.where(:receive_all => true))
@stores.each do |store|
store_email = ValidEmail2::Address.new(store.email)
UserNotifierMailer.send_lead_email(store, @lead).deliver if store_email.valid?
end
end
私のコードがひどい場合は申し訳ありません。Ruby は私が普段働いている場所ではありません。初心者向けの間違いを犯していることは間違いありません。
解決策
私が気づいた最大のコード臭は、if チェックがたくさんあり、コードを推論するのが難しくなっているということです。代わりに、ハッシュを作成し、メソッドを呼び出してハッシュをクリーンアップしてみてください。
このコードをrubocop経由で渡すこともお勧めします。これにより、コードがより慣用的になり、間違いが減ります。
filters.push 'location_north' if @lead.location_north
filters.push 'location_east' if @lead.location_east
filters.push 'location_south' if @lead.location_south
filters.push 'location_west' if @lead.location_west
filters.push 'location_other' if @lead.location_other
少なくとも使える状態にすることはできるかもしれません。 Ruby の慣用的な名前の付け方では、これを名詞として直接使用します。文字列を作成したり、後で記号に変換したりしていないことに注意してください。
def location_filters
{
location_north: @lead.location_north,
location_east: @lead.location_east,
location_south: @lead.location_south,
location_west: @lead.location_west,
location_other: @lead.location_other
}.filter { |key, value| value.present? }
end
# later on you would use this like this
@stores = @stores.where(location_filters)
ハッシュを構築してから偽の値を削除できます
type_filters = {
type_unique: @lead.store_type_unique
type_dining: @lead.store_type_dining
type_hotel: @lead.store_type_hotel
}.filter { |key, value| value.present? }
# later on we can directly pass it to where method
@stores = @stores.where(type_filters)
def perform(lead_id)
@lead = Lead.find(lead_id)
lead_email = ValidEmail2::Address.new(@lead.email)
UserNotifierMailer.send_signup_email(@lead).deliver if lead_email.valid?
capacity = @lead.guests_total.to_i
type_filters = { # consider moving to a private method called `type_filters`
type_unique: @lead.store_type_unique
type_dining: @lead.store_type_dining
type_hotel: @lead.store_type_hotel
}.filter { |key, value| value.present? }
@stores = Store.where.not(email: [nil, ''])
.where("capacity_min <= ? AND capacity_max >= ?", capacity, capacity)
.where(type_filters)
.where(location_filters)
.or(Store.where(receive_all: true))
@stores.each do |store|
store_email = ValidEmail2::Address.new(store.email)
UserNotifierMailer.send_lead_email(store, @lead).deliver if store_email.valid?
end
end
https://translate.google.com/translate?hl=ja&sl=en&tl=ja&u=https://rubyapi.org/3.2/o/hash#method-i-filter