Metaprogramming Ruby: Program Like the Ruby Pros

Category: Programming
Author: Paolo Perrotta
4.6
All Stack Overflow 16
This Month Stack Overflow 7

Comments

by anonymous   2019-07-21

Ok. You asked a lot of questions in one. It will be hard to explain all ruby and rails magic in one answer but I'll try to give you some useful resources where you can find some answers.

1) About require statements. If you are new to ruby and rails it will be hard to understand how rails application is initialized. Here is a tutorial were you can some useful information:
http://guides.rubyonrails.org/initialization.html
If you need more information about particular method you can always look into documentation. For example redirect_to method info:
http://apidock.com/rails/ActionController/Base/redirect_to

2) About "Using methods that don't exist". This is one of the most beautiful feature of ruby language called metaprogramming. This is also advanced topic. Here are some useful resources:
http://www.amazon.com/Metaprogramming-Ruby-Program-Like-Pros/dp/1934356476
Ruby metaprogramming online tutorial
http://yehudakatz.com/2009/11/15/metaprogramming-in-ruby-its-all-about-the-self/
http://rubylearning.com/blog/2010/11/23/dont-know-metaprogramming-in-ruby/

by anonymous   2019-07-21

I don't understand your example. Are you generating the source for the method as well?

So I will start with an example from the book Perrotta: Metaprogramming Ruby

class MyClass
  define_method :my_method do |my_arg|
    my_arg * 3
  end
end

obj = MyClass.new
obj.my_method(2) # => 6
by anonymous   2019-07-21

Instead of

instance=self.class.new

just write

instance = new

Why is this?

Well, in first place, you have to understand that you are calling a class method, thus you are at a class level already. The .new method is a class method, so you can call it directly without calling self.class.new.

Why does self.class.new return Class?

Because the class of the class Car is Class (I know, sounds weird ;), because classes in Ruby are instances of Class.

This is actually a pretty deep concept, I recommend you read more about it. One nice reference I have read is the book Metaprogramming Ruby by Paolo Perrotta (ISBN-10: 1934356476) [1].

  1. http://www.amazon.com/Metaprogramming-Ruby-Program-Like-Pros/dp/1934356476
by anonymous   2019-07-21

root is defined on ActionDispatch::Routing::Mapper::Base, which is included by ActionDispatch::Routing::Mapper.

There is a little more going on behind the scenes than at first glance.

If we look at the definition of draw, we can see why:

# action_dispatch/routing/route_set.rb
def draw(&block)
  # ...
  eval_block(block)
  # ...
end

The block is converted to a proc (with &) and passed to eval_block which is defined a bit farther down:

def eval_block(block)
  # ...
  mapper = Mapper.new(self)
  # ...
  mapper.instance_exec(&block)
end

So, as it turns out, yield isn't used at all. The block we pass to Application.routes.draw is just being evaluated in the context of an instance of ActionDispatch::Routing::Mapper.

If we add puts self to our config/routes.rb, we can see this in action:

Application.routes.draw do
  puts self
end

When we run rake routes we get something similar to the following:

#<ActionDispatch::Routing::Mapper:0x007feadf52cbd8>
# ...

For further reading, see:

http://ruby-doc.org/core-1.8.7/Object.html#method-i-instance_exec https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/routing/route_set.rb#LC296 https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/routing/mapper.rb#LC336 http://www.amazon.com/Metaprogramming-Ruby-Program-Like-Pros/dp/1934356476

by anonymous   2019-07-21

"Metaprogramming Ruby" is well worth adding to your bookshelf. http://www.amazon.co.uk/Metaprogramming-Ruby-Program-Like-Facets/dp/1934356476/

by anonymous   2017-08-20
def make_class(method_name, method_body, s_value)
  Class.new {
    define_method method_name do |*args|
      eval(method_body)
    end

    define_method :to_s do
      s_value
    end
  }
end

"Metaprogramming Ruby: Program Like the Ruby Pros" is a very good book to learn about ruby metaprogramming.

by anonymous   2017-08-20

First read the article in Wikipedia about self (even if it does not mention Ruby at all).

To make a long story short:

  • Ruby has borrowed a lot of concepts from other languages, and self comes from Smalltalk.
  • self is called in Smalltalk a pseudo-variable, which means it is variable, but it is set by the runtime environment, not by the program or programmer.
  • self references all the time the receiver of a message. super references the superclass of that message that is implemented by the method the reference super is in. (Glad that you did not ask for super).
  • self in Ruby (as in Smalltalk) references all the time the current object, and that may be an instance of a class or even a class itself. So if you define methods on the class-side (only callable on the class), even there self references the object, which is the class. So it is possible in Ruby to use only self, you never have to write down the name of the class to denote the receiver. That helps a little bit when refactoring.

If you have get all that, take a look at Metaprogramming Ruby which tells you some more tricks how to use self, classes, eigenclasses and some other interesting things.

by anonymous   2017-08-20

In Ruby local variables only accessible in the scope that they are defined. Whenever you enter/leave a Class, a Module or a Method definiton your scope changes in Ruby.

For instance :

v1 = 1

class MyClass # SCOPE GATE: entering class
  v2 = 2
  local_variables # => ["v2"]

  def my_method # SCOPE GATE: entering def
    v3 = 3
    local_variables  # => ["v3"]
  end # SCOPE GATE: leaving def

  local_variables # => ["v2"]
end # SCOPE GATE: leaving class

These entering and leaving points are called Scope Gates. Since you enter through Scope Gate via method definition you cannot access your local_var inside hello method.


You can use Scope Flattening concept the pass your variable through these gates.

For instance instead of using def for defining your method you can use Module#define_method.

local_var = "Hello"

define_method :hello do
  puts local_var
end

In the same way you can define your classes via Class#New so that your scope does not change when you pass through class definition.

local_var = 'test'

MyClass = Class.new do
  puts local_var #valid
end

instead of

class MyClass
  puts local_var #invalid
end

In the same way you should use Module#New if you want to pass your local variables through Module gates.

Example is taken from Metaprogramming Ruby