简体   繁体   中英

rails scope cannot generate correct sql

I added a variable in config/application.rb:

config.available_account_types = %w(SystemAccount CashAccount DemandAccount LiabilityAccount CreditCardAccount)

And generated some scopes in model account.rb:

for t in Mypurse::Application.config.available_account_types
  scope t.underscore.pluralize.to_sym, -> {where(type: t)}
end

But when I try all of them, Account.system_accounts, Account.cash_accounts, etc, I got this sql for every account type:

where type = 'CreditCardAccount'

That is, all of the generated scope are pointed to the {where(type: 'CreditCardACcount')}

I don't know why.

here is the source file: https://github.com/chylli/mypurse/blob/add_cash_demand_liability_credit_card/config/application.rb

https://github.com/chylli/mypurse/blob/add_cash_demand_liability_credit_card/app/models/account.rb

I think this is caused because a scope is given a Proc which is only executed when called, and so t will always be the last element of the loop.

A solution is to define methods instead of scopes (which work exactly the same) :

MyPurs::Application.config.available_account_types.each do |account_type| 
  define_singleton_method(account_type.underscore.pluralize.to_sym) do 
    where(type: "#{account_type}")
  end
end

But since this does not declare a proc, this should work as expected. Also the for .. in is rarely used in ruby, I personally prefer to use the more idiomatic .each (but of course you are free to use whatever you want, programmer happiness is key in ruby :) :)

Now as an aside, while meta-programming is really cool, you should really ask yourself if just listing the scopes is not way more readable. I understand: meta-programming is more DRY, but personally, in most cases where I did this, I reverted to the explicit definitions because of readability.

I am not sure why you have defined 'config.available_account_types', as this is business logic. this should belong to Account modal. so I would do something like this

class Account < ActiveRecord::Base
  ACCOUNT_TYPES = %w(SystemAccount CashAccount DemandAccount LiabilityAccount CreditCardAccount)
  ACCOUNT_TYPES.each do |acccount_type|
    define_singleton_method(account_type.underscore.pluralize.to_sym) do 
    where(type: "#{account_type}")
  end
end
end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM