Dynamic method in ruby
Software Craftsmanship GraphQLIn Ruby, the dynamic nature of method dispatch is one of its key features. Method calls are resolved at runtime based on the actual type of the object. This flexibility allows for polymorphic and dynamic behavior
Dynamic Dispatch
class Number
def square(argument)
argument ** 2
end
def cube(argument)
argument ** 3
end
end
number = Number.new
number.square(3) #=> 9
Method can be called using dot notation but this restricts making the method call being dynamic.
class.method("method_name").call
is way to invoke a method but is not a common pattern because it involves a more indirect and less flexible approach compared to other dynamic method invocation techniques.
There is another way to call a method that is using send
. It accepts method name and the method’s arguments as it own arguments.
number.send(:square, 3) #=> 9
Since the method name is an argument it can be replaced by any method name during runtime.
puts "Enter a number:"
user_input = gets.chomp
puts "Enter the method name:"
method_name = gets.chomp
number.send(method_name.to_sym, 4) #=> 16
to_sym is used to convert string to symbol but method name can be passed as string.
Using send to dispatch a method has a downside when it comes to privacy concerns as it invokes even a private method of the class without throwing an error.
This can be prevented using by using public_send
.
We can not only call methods during runtime but also define methods during runtime.
Dynamic Methods
A method can be defined using define_method
at the run time.
class Number
define_component(method_name, argument, power)
define_method(method_name) do |argument, power|
argument ** power
end
end
end
number = Number.new
number.define_component(square,4, 2) #=> 16
number.define_component(cube,4, 3) #=> 64
There is another way of defining methods dynamically using method_missing.
Ghost Method
What happens when you call a method on an object that is not yet defined. A method missing error is displayed. What happens internally is ruby goes up the object hierarchy and checks up till BasicObject
and when the method is not found it transfers the call to method_missing
in BasicObject
class, which returns NoMethodError
.
class Book
end
book = Book.new
book.author
#=>undefined method `author' for #<Book:0x00005585e38e9720> (NoMethodError)
We can override this method and return what we want to.
class Book
def method_missing(method, args)
"All missing methods return this string"
end
end
book = Book.new
book.author
#=>"All missing methods return this string"
method_missing
comes with huge advantage when there are many methods with similar definition. It saves the time of writing them and testing them independently.
Cons
- Readability and maintainability of code.
- Security issues arise when method name is taken from input.
- When the method which is supposed to be missing is found in ancestors chain.
- Debugging would be complex.
Conclusion
It is worth noting that while these dynamic features of ruby are powerful they should be used with caution, considering trade-offs.