Is There a "Main" Method in Ruby Like in C

Is there a main method in Ruby like in C?

@Hauleth's answer is correct: there is no main method or structure in Ruby. I just want to provide a slightly different view here along with some explanation.

When you execute ruby somefile.rb, Ruby executes all of the code in somefile.rb. So if you have a very small project and want it to be self-contained in a single file, there's absolutely nothing wrong with doing something like this:

# somefile.rb

class MyClass
def say_hello
puts "Hello World"
end
end

def another_hello
puts "Hello World (from a method)"
end

c = MyClass.new
c.say_hello
another_hello

It's not that the first two blocks aren't executed, it's just that you don't see the effects until you actually use the corresponding class/method.

The if __FILE__ == $0 bit is just a way to block off code that you only want to run if this file is being run directly from the command line. __FILE__
is the name of the current file, $0 is the command that was executed by the shell (though it's smart enough to drop the ruby), so comparing the two tells you precisely that: is this the file that was executed from the command line? This is sometimes done by coders who want to define a class/module in a file and also provide a command-line utility that uses it. IMHO that's not very good project structure, but just like anything there are use cases where doing it makes perfect sense.

If you want to be able to execute your code directly, you can add a shebang line

#!/usr/bin/env ruby

# rest of somefile.rb

and make it executable with chmod +x somefile.rb (optionally rename it without the .rb extension). This doesn't really change your situation. The if __FILE__ == $0 still works and still probably isn't necessary.

Edit

As @steenslag correctly points out, the top-level scope in Ruby is an Object called main. It has slightly funky behavior, though:

irb
>> self
=> main
>> self.class
=> Object
>> main
NameError: undefined local variable or method `main' for main:Object
from (irb):8

Don't worry about this until you start to dig much deeper into the language. If you do want to learn lots more about this kind of stuff, Metaprogramming Ruby is a great read :)

Where do I write the main function in Ruby?

There is no such thing in Ruby. The interpreter executes code from top to bottom so your main script is implicitly the body of "main". For example, suppose you have two files script_a.rb and script_b.rb. And suppose the contents of script_a.rb is as follows:

require_relative './script_b'

puts 1 + 1

Now if you run ruby script_a.rb what you will get in terms of actual code execution will be as follows: Find script_b.rb, execute the contents of script_b.rb, execute puts 1 + 1.

Should I define a main method in my ruby scripts?

I usually use

if __FILE__ == $0
x = SweetClass.new(ARGV)
x.run # or go, or whatever
end

So yes, you can. It just depends on what you are doing.

What is main in Ruby?

Everything in Ruby occurs in the context of some object. The object at the top level is called "main". It's basically an instance of Object with the special property that any methods defined there are added as instance methods of Object (so they're available everywhere).

So we can make a script consisting entirely of:

puts object_id
@a = 'Look, I have instance variables!'
puts @a

and it will print "105640" and "Look, I have instance variables!".

It's not something you generally need to concern yourself with, but it is there.

Can Ruby call methods or procs like c calls functions?

You can't pass a pointer to a variable in Ruby, but I don't think you need to do that to accomplish what you're trying to do. Try this:

def get_problem
puts "Select the form that you would like to convert to slope-intercept form: "
puts "1) Two-Point form (you know two points on the line)"
puts "2) Point-slope form (you know the lines slope and one point)"

loop do
choice = gets.chomp.to_i
return choice if [1, 2].include? choice
STDERR.puts "Incorrect choice: choose either 1 or 2"
end
end

choice = get_problem
puts "The user chose #{choice}"

This defines a method get_problem which loops until the user chooses either 1 or 2, and returns their chosen number, which you can store in a top-level variable choice.

Is it possible to have Methods inside Methods?

UPDATE: Since this answer seems to have gotten some interest lately, I wanted to point out that there is discussion on the Ruby issue tracker to remove the feature discussed here, namely to forbid having method definitions inside a method body.


No, Ruby doesn't have nested methods.

You can do something like this:

class Test1
def meth1
def meth2
puts "Yay"
end
meth2
end
end

Test1.new.meth1

But that is not a nested method. I repeat: Ruby does not have nested methods.

What this is, is a dynamic method definition. When you run meth1, the body of meth1 will be executed. The body just happens to define a method named meth2, which is why after running meth1 once, you can call meth2.

But where is meth2 defined? Well, it's obviously not defined as a nested method, since there are no nested methods in Ruby. It's defined as an instance method of Test1:

Test1.new.meth2
# Yay

Also, it will obviously be redefined every time you run meth1:

Test1.new.meth1
# Yay

Test1.new.meth1
# test1.rb:3: warning: method redefined; discarding old meth2
# test1.rb:3: warning: previous definition of meth2 was here
# Yay

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

Note also that in Ruby, method bodies cannot be closures, only block bodies can. This pretty much eliminates the major use case for nested methods, since even if Ruby supported nested methods, you couldn't use the outer method's variables in the nested method.


UPDATE CONTINUED: at a later stage, then, this syntax might be re-used for adding nested methods to Ruby, which would behave the way I described: they would be scoped to their containing method, i.e. invisible and inaccessible outside of their containing method body. And possibly, they would have access to their containing method's lexical scope. However, if you read the discussion I linked above, you can observe that matz is heavily against nested methods (but still for removing nested method definitions).

Can I over-ride Ruby methods written in C?

I can only speak for MRI/YARV, but I'll give it a try. You can only override functions that originate in C in Ruby if the C function has been explicitly defined as a method on a Ruby object. For example, Kernel#extend is explicitly defined in C as

rb_define_method(rb_mKernel, "extend", rb_obj_extend, -1);

So because the C function rb_obj_extend has been "linked" (in quotes because I'm figuratively speaking, I don't mean C linkage here) with the method Kernel#extend in the Ruby world, in theory you could override rb_obj_extend's behaviour if you override Kernel#extend.

I would say given the following two conditions you could claim that you actually "overrode" a rb_* C function:

  • the rb_* C function has been "linked" with some Ruby object, so we have a handle in Ruby world that serves as a hook that we can override
  • the given rb_* method is only used in this one spot for exactly this purpose, it's reused nowhere else

Now if you look at rb_error_frozen it fulfills neither of these two conditions. It's a helper in the C implementation, meaning it's called from several places. And it has not been explicitly "linked" with any Ruby object, so you have no hook where you could override it.

Not all is lost, though. You can't directly override rb_error_frozen, but what you could still try is to override all the Ruby methods where rb_error_frozen bubbles up to the "Ruby surface". What I mean by that is that you could check all the places in the C sources where rb_error_frozen is used and from these places try to find each and every Ruby method that could trigger these bits of code. If this is a closed set, you could simply override all of these methods in order to "de-facto-override" rb_error_frozen's behaviour.

This is only a patchwork solution, however. All your hard work is lost should somebody decide to write another C extension where they again call rb_error_frozen directly.

So long story short: You can only override a C function if it has been explicitly defined as the implementation of some method of a Ruby object, e.g. as in

rb_define_method(rb_cString, "gsub", rb_str_gsub, -1);

where you can assume that it will only ever be used for that purpose only. But even then you're not a 100% safe, somebody could still decide to reuse that function in some other part of C code.


Edit: You said you'd like Ruby only to warn instead of raise if a frozen object was modified. I just went through the sources to see if you could override all of the places where rb_error_frozen is called. The problem is rb_check_frozen - it's called anywhere where an object is modified (as it ought to be) and again itself calls out to rb_error_frozen. This mechanism is deeply rooted in the C internals and not published at the Ruby surface everywhere, so there's no way to override the "raising behaviour" or at least none that would not require significant effort. If you think about it for a minute that is actually a good thing. If it were possible to simply override the behaviour then this could actually be seen as a security flaw in the Ruby implementation. Freezing an object should guarantee you that it stays unmodifiable no matter what.



Related Topics



Leave a reply



Submit