Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
August 21, 2020 08:57 pm GMT

What Makes Ruby Beautiful: Metaprogramming

Ruby is one of the underrated programming languages among modern developers. It has become popular with the Ruby on Rails framework.

Ruby is making developers happy, productive and enjoying programming.-Yukihiro Matsumoto

You are maybe coming from the JS world with a lot of frameworks or from Java and all its complexity.
If you are enough of wasting your time building software that matter and want concrete result as soon as possible, let me introduce you to Ruby.

I'll introduce you to the concept of metaprogramming.
First time I learned Ruby, my mind literally blew when I was confronted to this.

Think about a program that can generate program itself. A program that can generate executable code without the help of a developer.

No, it's not sci-fi, it's Ruby Metaprogramming!

If I had to explain to a 5 years old, imagine you want to draw a sunny city, you take your pen and write "Sunny City" on the paper. Push the button and magic happens. That's metaprogramming.

Now, I'll explain to real developers.

In Ruby, you can make runtime introspection on method. In another hand, you can ask an object about its capabilities (Do you have this method?), its variables, constants and its class and ancestors.

In this article I'll show you some examples with methods like respond_to? (? is a Ruby convention for method returning a boolean) send and define_method. But there is a lot more like method_missing, remove_method and undef_method.
I'll explain these 3 methods and finally show you the mind-blowing examples.

The respond_to?() method

This method tests your class if it can handle a specific message, for those who don't speak Ruby: it checks if a method can be called in a specific class.

Message example

(In the Ruby vocabulary, message is known as a method).

Here is a Shipment class:

class Shipment  def prepare_for_delivery    @message = 'Shipment is prepared for delivery'  end  def tracking_code(code)    @tracking_code = code  endend

Use the respond_to? method to check if prepare_for_delivery method exists:

s = Shipment.new s.respond_to?(:prepare_for_delivery) ==> true

A more complete example sending a message, if this one exists, to another object.

s = Shipment.newif s.respond_to?(:cancel_shipping)  s.cancel_shippingelse  puts "Oh no ! Shipment cannot be cancel."end

It can be used for any classes.

'hello'.respond_to?(:count)==> true'world'.respond_to?(:include)==> false

Did you catch it?

Well, keep it in mind for the end of this article.

The send()method

You can call any method in a class with the send() method.

s = Shipment.newif s.respond_to?(:tracking_code)  s.send(:tracking_code, '123ABC') # or s.send('cancel_shipping', '123ABC')else  puts "Tracking code is not available."end

Message example 2

(Message is sent in the first parameter of send())

I'm hearing you saying: "Why not calling our method directly?"

Yes, we can and I know that this example is not a real world example on how we use it.

Let's continue.

The define_method() method

Now that you know the logic, you could even find the behavior of this method before I explain it to you.

It defines a method? Well done!

class Shipment  define_method :cancel do |reason|    @cancelled = true    puts reason  endend

You just defined a new method for the Shipment class, setting an instance variable cancelled to true and printing the reason.

Wait, for the final example that will blow your mind.

Metaprogramming in oneexample

You know the basics of Ruby metaprogramming, let's see the final example.

Cargo

Let's embark on the sea trip to metaprogramming and set sail!

# We create a container class. We only store the product's name at instantiationclass Container  attr :product_name  def initialize(name)    @product_name = name  endend# Apples ! Oranges ! All of theses fruits are contained in FruitContainer extending Container# For simplification we only have one scannerclass FruitContainer < Container  def apples_scanner    puts "Scanning apples..."  endend# Potatoes ! Broccoli ! All of vegetables are contained in VegetableContainer extending Container# For simplification we only have one scannerclass VegetableContainer < Container  def potatoes_scanner    puts "Scanning potatoes..."  endend# The Cargo containing all the containersclass Cargo  # The constructor accepting multiple parameters  def initialize(*containers)    containers.each do |container|      # self.class.send is used to define singleton methods for our class      # we could also use define_singleton_method      self.class.send(:define_method, "inspect_#{container.product_name}") do        scanner_name = "#{container.product_name}_scanner"        if container.respond_to?(scanner_name)          container.send(scanner_name)        else          puts "No scanner found."        end      end    end  endendpotatoes_container = VegetableContainer.new "potatoes"apples_container = FruitContainer.new "apples"cargo = Cargo.new(potatoes_container, apples_container)cargo.inspect_applescargo.inspect_potatoes

We used all the methods I explained. For each Container classes, we define new methods which call a method (if exists) based on their product name, a scanner.

The output:

Scanning apples...Scanning potatoes...

Now, you know what metaprogramming is and how it works. Well done.

One of the first uses case of metaprogramming is creating its own DSL (Domain Specific Languages).

There are some well-known tools based on the Ruby DSL, Chef and Puppet for DevOps peoples.

This is what makes Ruby beautiful.

Since I can't offer you a book on this topic, I'm replying to all your questions and feedback. Join me on Twitter!


Original Link: https://dev.to/alexlion/what-makes-ruby-beautiful-metaprogramming-536a

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To