0

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

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for your answer. Is there no way to create a new local variable for every running of the loop ? I'm a PERL programmer so I always think the problem with the PERL's behavior.
So the way I showed you should work. Defining scopes would not, because it defines a Proc (the ->) which delays execution until it is called.
I changed my version to this and worked: proc {|t2| scope t2.underscore.pluralize.to_sym,-> {where(type:t2)}}.call(t)
If that is your preferred solution, you should enter it as an answer and accept it instead. Personally I find my solution much more readable, and it behaves exactly the same.
I agree with you. I just want to study ruby deeply. I think your answer is better than mine.
0

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

2 Comments

Looks strangely familiar. But good remark about the account types. You are missing an end though.
the reason I define this variable is that I will use it in another module. I tried define it as a function types in the Account module but the User module will say 'missing method'. OK I will remove the meta-program. thanks very much.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.