Forum: Ruby on Rails Metaprogramming oddness - works in console but fails in spec

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Max W. (Guest)
on 2009-04-23 18:57
Hi folks

Let's say i have these methods, and i want to abstract them into a bit
of metaprogramming, for dryness' sake:

  def water_score_for(year)
    self.send("ratings.water.#{year}.collect(&:score).sum")
  end

  def possible_water_score_for(year)
    self.ratings.water.#{year}.size * 2
  end

  def sanitation_score_for(year)
    self.send("ratings.sanitation.#{year}.collect(&:score).sum")
  end

  def possible_sanitation_score_for(year)
    self.ratings.sanitation.#{year}.size * 2
  end

I'm trying to do this via 'define_method': here's my code:

  #defined elsewhere
  #PolicyIndicator::SECTORS = ["water", "sanitation"]

  PolicyIndicator::SECTORS.each do |sector|
    self.send(:define_method, "#{sector}_score_for".to_sym) do |year|
      self.send("ratings").send("#{sector}").send("for_#{year}").collect(&:score).sum
    end

    self.send(:define_method, "possible_#{sector}_score_for".to_sym) do
|year|
      self.send("ratings").send("#{sector}").send("for_#{year}").size *
2
    end
  end
Now, this works fine in the console:

>> country = Country.find(:first)
=> #<Country id: 1, region_id: 1, name: "Nigeria", created_at:
"2009-04-21 08:39:09", updated_at: "2009-04-22 16:24:29",
sanitation_coverage: 62.0, water_coverage: 45.0, hdi: 0.405497,
population_millions: 46.0, child_mortality: 52.0, gdp: 13724.8>

>> country.water_score_for(2009)
=> 9

But when i run a spec that makes a country, makes some ratings,
associates them with the country and call the method on it, i get this
error back:

undefined method `for_2009' for #<Array:0xb70cd19c>
(__DELEGATION__):2:in `__send__'
(__DELEGATION__):2:in `with_scope'
(__DELEGATION__):2:in `__send__'
(__DELEGATION__):2:in `with_scope'
/home/max/work/washwatch_container/washwatch/app/models/country.rb:11:in
`send'

the line (country.rb:11) in question is this:
  self.send("ratings").send("#{sector}").send("for_#{year}").collect(&:score).sum

It seems to be falling over on the named scopes on the Rating class, so
i guess maybe i'm not using 'send' int he optimal way.  However, it
seems to work perfectly in the console - checking my log file, the
correct, optimised sql query is being called.

Can anyone help?
thanks
max
This topic is locked and can not be replied to.