Amit Erandole
Amit Erandole

Reputation: 12281

Why is my define_method not working?

I have two methods in my Designer class (in my rails app):

  def add_specialty(specialty)
    specialty_list.add(specialty)
    save
  end

  def add_qualification(qualification)
    qualification_list.add(qualification)
    save
  end

Here are specs I have for them that are passing:

  context 'adding specialties' do
    it "can add a new specialty" do
      expect { designer.add_specialty("interior design") }.to change {designer.specialty_list.count}.by(1)
      expect(designer.specialty_list).to include("interior design")
    end
  end

  context 'adding qualifications' do
    it "can add a new qualification" do
      expect { designer.add_qualification("architect") }.to change {designer.qualification_list.count}.by(1)
      expect(designer.qualification_list).to include("architect")
    end
  end

Now I want to refactor to this implementation:

  ["specialty", "qualification"].each do |attr|
    define_method("add_#{attr}") do |arg|
      "#{attr}_list".add(arg)
      save
    end
  end

This fails. I get failures:

  1) Designer adding qualifications can add a new qualification
     Failure/Error: expect { designer.add_qualification("architect") }.to change {designer.qualification_list.count}.by(1)
     NoMethodError:
       undefined method `add' for "qualification_list":String
     # ./app/models/designer.rb:93:in `block (2 levels) in <class:Designer>'
     # ./spec/models/designer_spec.rb:79:in `block (4 levels) in <top (required)>'
     # ./spec/models/designer_spec.rb:79:in `block (3 levels) in <top (required)>'
     # -e:1:in `<main>'

  2) Designer adding specialties can add a new specialty
     Failure/Error: expect { designer.add_specialty("interior design") }.to change {designer.specialty_list.count}.by(1)
     NoMethodError:
       undefined method `add' for "specialty_list":String
     # ./app/models/designer.rb:93:in `block (2 levels) in <class:Designer>'
     # ./spec/models/designer_spec.rb:72:in `block (4 levels) in <top (required)>'
     # ./spec/models/designer_spec.rb:72:in `block (3 levels) in <top (required)>'
     # -e:1:in `<main>'

What am I doing wrong in my define_method implementation?

Upvotes: 0

Views: 518

Answers (2)

Ben
Ben

Reputation: 2156

"#{attr}_list" by itself is just the string "specialty_list" or "qualification_list", and strings don't have an add method. I think you want to the send the specialty_list method e.g.

%w{ specialty qualification }.each do |attr|
  define_method("add_#{attr}") do |arg|
    send("#{attr}_list").add(arg)
    save
  end
end

Upvotes: 1

Amit Erandole
Amit Erandole

Reputation: 12281

Ok: I got it working like this:

  ["specialty", "qualification"].each do |attr|
    define_method("add_#{attr}") do |arg|
      instance_eval("#{attr}_list").send(:add, arg)
      save
    end
  end

Not sure why this worked though or if its the right way to do it. Would anyone care to contribute towards a better understanding?

Upvotes: 0

Related Questions