简体   繁体   中英

Where do I store the values passed to a macro method in a Rails model?

In my app, I can calculate Result s for a measurement. The result model uses Single Table Inheritance to define different result types, each of which implements a calculate method. Now, I need to calculate some results before others. Example:

class Result < ActiveRecord::Base

  belongs_to :measurement

  def calculate
    raise "#calculate is abstract. Implement this method in one of the subclasses."
  end

  def self.depends_on(args)
    # what to do here?
  end

end

One subclass could look like:

# The total duration in milliseconds
class TotalDuration < Result

  depends_on :EventList # calculate the EventList result first

  def calculate
    # how do I find out what I depend on?
    # only then do the actual calculation
  end

end

As you can see, I've already included a depends_on class method, with which I'd like to express that the TotalDuration result needs the EventList result calculated before.

But how do I access the information at runtime, eg when I'm inside calculate ? There for example I could calculate the dependency first, then continue.

I would assume that using class level attributes (eg, @@depends_on ) are not considered good style?

I would create a join model which allows any particular instance of Result to have_many dependent results.

Then in your calculate method for a result with dependencies you first ask for the calculations from the dependent results before continuing with your own calculation.

Through natural recursion, if any of those dependent results had further dependencies ... this would naturally filter up the chain.

I solved it like this:

def self.depends_on(*args)
  define_method("calculate_dependencies") do
    args.each do |arg|
      measurement.results.where(type: arg.to_s).each { |result| result.calculate unless result.value }
    end
  end
end

This way, I only have to call

def calculate
  calculate_dependencies
  # other stuff
end

in my child models. I could even go as far as defining a new method with the name of the dependent result:

define_method("#{arg.to_s.underscore}") do
  results = measurement.results.where(type: arg.to_s).limit(1)
  result = results.first if results.any?
  result.calculate if result.value.nil?
  result
end

That way, I could do:

depends_on :EventList

def calculate
  # no need for calculate_dependencies
  event_list  # <= this is the result I depend on, already calculated
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