1

So, I'm currently learning about metaprogramming in Ruby and I want to fully understand what is happening behind the scenes.

I followed a tutorial where I included some of the methods in my own small project, an importer for CSV files and I have difficulties to wrap my hand around one of the methods used.

I know that the define_method method in Ruby exists to create methods "on the fly", which is great. Now, in the tutorial the method initialize to instantiate an object from a class is defined with this method, so basically it looks like this:

class Foo
    def self.define_initialize(attributes)
      define_method(:initialize) do |*args| 
       attributes.zip(args) do |attribute, value|
         instance_variable_set("@#{attribute}", value)
       end
      end
    end
end

Next, in an initializer of the other class first this method is called with Foo.define_initialize(attributes), where attributes are the header row from the CSV file like ["attr_1", "attr_2", ...], so the *args are not provided yet.

Then in the next step a loop loops over the the data:

@foos = data[1..-1].map do |d|
  Foo.new(*d)
end

So here the *d get passed as the *args to the initialize method respectively to the block.

So, is it right that when Foo.define_initialize gets called, the method is just "built" for later calls to the class? So I theoretically get a class which now has this method like:

def initialize(*args)
  ... do stuff
end

Because otherwise, it had to throw an exception like "missing arguments" or something - so, in other words, it just defines the method like the name implies.

I hope that I made my question clear enough, cause as a Rails developer coming from the "Rails magic" I would really like to understand what is happening behind the scenes in some cases :).

Thanks for any helpful reply!

2
  • 2
    Yep, that is exactly what happens Commented Aug 22, 2020 at 10:37
  • Typo alert: Class Foo should be class Foo. Note that since (sic) Ruby v2.1, Foo#initialize will be private even thought you have not so-designated it in define_method. Also, consider this alternative conStruct. Commented Aug 22, 2020 at 17:52

1 Answer 1

2

Short answer, yes, long answer:

First, let's start explaining in a really (REALLY) simple way, how metaprogramming works on Ruby. In Ruby, the definition of anything is never close, that means that you can add, update, or delete the behavior of anything (really, almost anything) at any moment. So, if you want to add a method to Object class, you are allowed, same for delete or update.

In your example, you are doing nothing more than update or create the initialize method of a given class. Note that initialize is not mandatory, because ruby builds a default "blank" one for you if you didn't create one. You may think, "what happens if the initialize method already exist?" and the answer is "nothing". I mean, ruby is going to rewrite the initialize method again, and new Foo.new calls are going to call the new initialize.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for your answer, yep that's also the way that I understand metaprogramming and your explanation with update/create nails it I think. It's just sometimes I have to change my type of thinking and going through the code step by step to really comprehend how I build or update a method by creating a method which dynamically updates the base method :D
To add to what Andres said: anything which is initialized in between the first and second method definitions will use the first initialize method, not the second.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.