views:

1809

answers:

3

My rails model has code that is attempting to define_method(method_name) inside the model.

I keep getting:

NoMethodError: undefined method `define_method'

What am I doing wrong? Am I doing this in the wrong place. I need this method attached to this model. Where else can I define this method?

EDIT: For those asking to see the code:

for field in rdev_fields
  next if self.attributes.include?(field)
  count = count + 1
  rdev_hash[field.to_sym] = self.attributes["attribute#{count}"]
  if !self.respond_to?(field) then
    define_method("#{field}") do
      self.send("attribute#{count}".to_sym)
    end
  end
end
+1  A: 

Hi, was able to cobble this together. Very little understanding of what's actually going on though.

My instance method foo is opening the class and defining bar on it so that I can then call that on my instance. More experienced folks will let us know if this is opening a can of worms at the same time.

Would be useful to know your specific use for this though.

class User < ActiveRecord::Base

  def foo
    (class << self; self; end).class_eval do
      define_method(:bar) {puts "bar"}
    end
  end
end

u = User.first
u.foo
u.bar #=> "bar"
Tim Harding
foo is opening the metaclass. Read _why's article linked from my answer, and prepare to bend your brain
Orion Edwards
A: 

The answer to your question is "yes, you can". As for why it's not working for you - it's impossible to say for sure why, if you don't provide some context for the code.

Avdi
+12  A: 

There's nothing magical or about a rails model, it's just a normal class with a bunch of pre-existing methods,

So, the question is "can I define_method in a class"?

Part 1: Yes you can.

The important distinction is than you can define method in a class not in an instance method

For example:

class Cow
  define_method "speak" do
    "MOOOO"
  end
end

Cow.new.speak
=> "MOOOO"

This should work fine. Note you're defining it on the class Cow, so any other Cows that you already have will automatically get that method added.

Part 2: What do you do if you want to define a method from within an instance method?

You can't define methods from an instance method, so you have to grab the class, and use that to define the method. Like this:

class Cow
  def add_speak
    self.class.send(:define_method, :speak) do
      "MOOOO added"
    end
  end
end

Cow.new.speak
NoMethodError: undefined method 'speak' for #<Cow:0xb7c48530>

Cow.new.add_speak
Cow.new.speak
=> "MOOOO added"

Problem solved. Astute readers will note that in this example I'm using send(:define_method) - this is needed because define_method is private, and private methods are only accessible to the thing they're in. In this case, define_method is in the class, we are in the instance, so we can't directly access it.

As above though, we're adding the method directly to the class, so all other Cows which already exist will automatically also get the speak method added.

Part 3: What do you do if you want to define a method for only 1 object, not all objects of that class?

Example:

class Cow
  def add_speak_just_me
    class << self
      define_method "speak" do
        "MOOOO added for just me"
      end
    end
  end
end

Cow.new.speak
NoMethodError: undefined method 'speak' for #<Cow:0xb7c72b78>

c = Cow.new
c.add_speak_just_me
c.speak
=> "MOOOO added for just me" # it works, hooray

Cow.new.speak # this new cow doesn't have the method, it hasn't been automatically added
NoMethodError: undefined method `speak' for #<Cow:0xb7c65b1c>

How does this work? Down the rabbithole you go!

Read this: http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html and good luck. It helps when you realise that 'adding a method' to an instance isn't actually adding it to the instance at all :-)

Orion Edwards