Topic: How to set an objects attribute based on two of its other attributes

I have a Lead object.  It has 4 attributes Name, Date, Value and Cost.  I am trying to set the cost attribute.

I am able to set the cost based on the value. 

before_create :set_cost
  
  def set_cost
    if self.value > 30000
      self.cost = 10
    elsif self.value >= 20000
      self.cost = 9
    elsif self.value >= 15000
      self.cost = 8
    elsif  self.value <= 14999
      self.cost = 7
    end
  end 

So thats pretty straight forward.  The values are pretty straight forward.  So I have a matrix of values and dates that determine cost.  How do I cross check the values and dates to set cost...

any ideas?

thanks so much

Re: How to set an objects attribute based on two of its other attributes

Can you please post more information to your question? 

There are so many ways to begin to approach this but I'll bet you have a very specific structure that you need to maintain. 

Anyway, on a different note, using a case statement might clean up your model and at the same time help... Here you can assign two vales to the case.  For example:

case cost
when "A", "B"
  cost = "something"
when "C", "D"
  cost = "something_else"
else
  cost = "something else_yet"
end

Last edited by imacobrabuilder (2012-05-11 07:47:57)

Re: How to set an objects attribute based on two of its other attributes

http://ravennainteractive.com/table.jpg here is the table I am working with. 

The more I thought about it I want to set a price based on the year and value. Value is the vertical column.  So if  this year is 2012 then 2008 leads that are 30+ would be $10.  Would it be better to do some kind of helper that set the price in the view when the user called the data?  Or would that be a huge load on the db and server?

Basically I need to set the price based on that table.  I have leads that range in value and which are from different years.  So based on the current_year I have to set the cost.  Example: a lead from 2008 which values at 30+ is $10.  A lead that is from 2010 and is <10 would be valued at $3.

Thoughts?

thanks so much

Re: How to set an objects attribute based on two of its other attributes

Anyone?  I am completely stuck on this...

Re: How to set an objects attribute based on two of its other attributes

Try something along these lines:

class PricePredicate 
  
  def initialize(&block)
    @predicate = block
  end
  
  def evaluate(subject)
    @predicate.call(subject)
  end
  
end

prices = {
  "2008" => [
    PricePredicate.new { |subject| if subject >= 30 then 10 end },
    PricePredicate.new { |subject| if subject >= 20 && subject <= 29 then 9 end },
    PricePredicate.new { |subject| if subject >= 15 && subject <= 19 then 8 end },
    PricePredicate.new { |subject| if subject >= 10 && subject <= 14 then 7 end },
    PricePredicate.new { |subject| if subject < 10 then 6 end },
  ],
  "2007" => [
      ...
    ],
    ...
}

year = "2008"

value = 9

price = nil

catch (:done) do
  prices[year].each do |predicate|  
    price = predicate.evaluate(value)
    throw :done unless price.nil?
  end
end

puts price

Re: How to set an objects attribute based on two of its other attributes

Ok that makes sense.  I am a novice so my only other question is basic implementation.  How would I implement this with a existing leads model.

Would I simply put this in a new model, and somehow include it in the Leads model?

I created a gist: https://gist.github.com/2726296

Also would I call whatever method this creates from the Leads_controller#index to output correct pricing?

sorry to need it dumbed down...

Last edited by tjsherrill (2012-05-18 12:42:13)

Re: How to set an objects attribute based on two of its other attributes

Something like this should work:

class PricePredicate 
  
  def initialize(&block)
    @predicate = block
  end
  
  def evaluate(subject)
    @predicate.call(subject)
  end
  
end

class Lead < ActiveRecord::Base
  
  before_create :set_cost

  def set_cost
    
    prices = {
      "2008" => [
        PricePredicate.new { |subject| if subject >= 30 then 10 end },
        PricePredicate.new { |subject| if subject >= 20 && subject <= 29 then 9 end },
        PricePredicate.new { |subject| if subject >= 15 && subject <= 19 then 8 end },
        PricePredicate.new { |subject| if subject >= 10 && subject <= 14 then 7 end },
        PricePredicate.new { |subject| if subject < 10 then 6 end },
      ],
      "2007" => [
          ...
        ],
        ...
    }
    
    catch (:done) do
      Array(prices[self.year]).each do |predicate|  
        self.cost = predicate.evaluate(self.value)
        throw :done unless self.cost.nil?
      end
    end
  
  end
  
end

You can leave the PricePredicate class in the Lead model file or move it to lib and just require it at the top of the Lead model file.

Re: How to set an objects attribute based on two of its other attributes

So that make tons of sense.  Thanks for the info...

The one note/question I have is that I am trying to keep from using magic numbers as much as possible.  I am ok with the cost values being that way but I am trying to make the app responsive enough to handle years changing.  So a 2008 Lead is valuable now.  A 2009 lead is a little less val and 2010 is lower than that. But when the new year comes in 8 months the 2009 leads will be worth what the 2008 leads are today.  Does that make sense?

I am working on the MVP right now so I think your solution answers my question.  But I want to dig deeper and set up a dynamic cost  setting feature... thanks so much