Ruby metaprogramming I
What do we mean by metaprogramming?
Term metaprogramming refers to way of coding which a program has knowledge about itself and can manipulate itself. This technic give programmer to write more dynamic code — “code which write code”.
The code which changes a class or an instance at runtime.
How Ruby does it?
In Ruby, everything is executable, even the class definitions. But it doesn’t end there. In ruby, you can open a class and modify it. Good example of this modification is monkey-patching.
class String
def up_letters
upcase
end
end
slogan = 'Hello'
puts slogan.up_letters# HELLO
class Person
attr_accessor :name, :email attr_accessor :role def initialize(role:, age:) @age = age @role = role end
private def age @age end
end
Sadly with above Person class we are not able to get access to age attribute since the age is private. To handle this kind case we can use send to get access to it.
admin = Person.new(role: 'admin', age: 35)admin.send(:age)#35
Dynamic methods using define_method
We are going to extend our class with method to check which role person has. Let’s image that our Person class will have 3 roles: super_admin, admin, client. Our plan is to have 3 methods ie. is_admin?, is_super_admin? and is_client?.
class Person
.
.
.['admin', 'super_admin', 'client'].each do |role| define_method("is_#{role}?") do @role == role end
.
.
.end
Above we used define_method (from Module) to implement three methods is_admin?, is_super_admin? and is_client?. This approach allow reduce amount of code in Person class.
Extend instance by instance_eval
The method instance_eval allow us to define method only for one particular instance.
admin = Person.new(role: 'admin', age: 35)super_admin = Person.new(role: 'super_admin', age: 35)
super_admin.instance_eval do def access_to_all?
true endendputs super_admin.access_o_all?
puts admin.access_o_all?trueundefined method `access_to_all?' for #<Person:0x00007fa04f8a93b8> (NoMethodError)
The above NoMethodError we got on admin instance — since we have called instance_eval only on super_admin instance.
Person.class_eval do def access_to_all? is_admin? || is_super_admin? endend
Above code solves problem for each instance admin and super_admin.
Summary
Ruby allows write code which change class or instance during runtime.
Advantages
This sort of coding is very powerful and You can achieve the same result with less code.
- flexibility code
- less code
- delegate calls method to other object by using method_missing
Disadvantages
Over use metaprogramming may that the code is hard to:
- read
- debug
- search