Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby: Does defining a method inside another method have any real uses?

I was reading an article on meta-programming and it showed that you can define a method within another method. This is something that I had known for a while, but it made me ask myself the question: does this have any practical application? Is there any real life uses of defining a method within a method?

Ex:

def outer_method
  def inner_method
     # ...
  end
  # ...
 end
like image 248
ab217 Avatar asked Nov 04 '10 01:11

ab217


People also ask

Can you define a method within a method in Ruby?

In short: no, Ruby does not support nested methods.

Is it possible to write method inside method?

Java does not support “directly” nested methods. Many functional programming languages support method within method. But you can achieve nested method functionality in Java 7 or older version by define local classes, class within method so this does compile.

What's the point of defining methods in Ruby?

Methods are time savers and help the user to reuse the code without retyping the code. Defining & Calling the method: In Ruby, the method defines with the help of def keyword followed by method_name and end with end keyword. A method must be defined before calling and the name of the method should be in lowercase.

What happens when you call a method in Ruby?

The Method class in Ruby has a source_location function that returns the location of the method's source code - file and line number where the method starts. Then method_source essentially opens that file, finds the respective line, looks for end that will end the method and returns the code in between.


2 Answers

My favorite metaprogramming example like this is dynamically building a method that you're then going to use in a loop. For example, I have a query-engine I wrote in Ruby, and one of its operations is filtering. There are a bunch of different forms of filters (substring, equals, <=, >=, intersections, etc.). The naive approach is like this:

def process_filter(working_set,filter_type,filter_value)
  working_set.select do |item|
    case filter_spec
      when "substring"
        item.include?(filter_value)
      when "equals"
        item == filter_value
      when "<="
        item <= filter_value
      ...
    end
  end
end

But if your working sets can get large, you're doing this big case statement 1000s or 1000000s of times for each operation even though it's going to take the same branch on every iteration. In my case the logic is much more involved than just a case statement, so the overhead is even worse. Instead, you can do it like this:

def process_filter(working_set,filter_type,filter_value)
  case filter_spec
    when "substring"
      def do_filter(item,filter_value)
        item.include?(filter_value)
      end
    when "equals"
      def do_filter(item,filter_value)
        item == filter_value
      end
    when "<="
      def do_filter(item,filter_value)
        item <= filter_value
      end
    ...
  end
  working_set.select {|item| do_filter(item,filter_value)}
end

Now the one-time branching is done once, up front, and the resulting single-purpose function is the one used in the inner loop.

In fact, my real example does three levels of this, as there are variations in the interpretation of both the working set and the filter value, not just the form of the actual test. So I build an item-prep function and a filter-value-prep function, and then build a do_filter function that uses those.

(And I actually use lambdas, not defs.)

like image 100
glenn mcdonald Avatar answered Sep 27 '22 23:09

glenn mcdonald


Yes, there are. In fact, I'll bet you use at least one method that defines another method every day: attr_accessor. If you use Rails, there are a ton more in constant use, such as belongs_to and has_many. It's also generally useful for AOP-style constructs.

like image 21
Chuck Avatar answered Sep 27 '22 23:09

Chuck