Chloe
Chloe

Reputation: 26264

What is this code doing and how can I write it more simply?

[:initial_amount, :rate_increase_amount].each do |method|
  define_method method do
    self["#{method}_in_cents".to_sym].to_f/100 if self["#{method}_in_cents".to_sym]
  end

  define_method "#{method}=" do |_value|
    self["#{method}_in_cents".to_sym] = _value.to_f * 100
  end
end

It's giving the following error:

NoMethodError: undefined method `initial_amount_in_cents' for #<ViolationType:0x6220a88>

I tried to re-write it as:

def initial_amount_in_cents
  initial_amount_in_cents.to_f/100 if initial_amount_in_cents
end

def rate_increase_amount_in_cents
  rate_increase_amount_in_cents.to_f/100 if rate_increase_amount_in_cents
end

def initial_amount= (value)
  initial_amount_in_cents = value.to_f * 100
end

def rate_increase_amount= (value)
  rate_increase_amount_in_cents = value.to_f * 100
end

But it gave me this error instead:

ERROR SystemStackError: stack level too deep

Upvotes: 2

Views: 99

Answers (1)

Todd A. Jacobs
Todd A. Jacobs

Reputation: 84373

Problems

You have several, including:

  1. Overly "clever" code. Don't do things like this without a good reason.
  2. Recursive calls. You might consider using defined? or something to avoid this.
  3. An example that's missing a lot of context.

Being DRY doesn't mean being obscure. If the code doesn't make sense to you the way it's written, refactor it for clarity.

What It's Probably Trying to Do

The code is apparently trying to dynamically define an _in_cents method for related methods that accept a float. You'd have to ask the author why he (or she) wrote it that way, but that's what it's for, whether or not it currently works for you.

Possible Solution

With all that said, this may help. Assuming you have defined attributes in your model for initial_amount and rate_increase_amount then you should be able to simplify the call to Module#define_method. For example:

class YourRailsModel
  %i[initial_amount rate_increase_amount].each do |method|
    # You don't have a real Rails model here, so we create accessors to simulate
    # model attributes.
    attr_accessor method

    define_method "#{method}_in_cents" do
      Integer(Float send(method) * 100)
    end
  end
end

model = YourRailsModel.new
model.initial_amount = 0.97
# => 0.97

model.initial_amount_in_cents
# => 97

Upvotes: 2

Related Questions