Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for About Ruby: A tale of searching for the main
Lucian Ghinda
Lucian Ghinda

Posted on • Originally published atallaboutcoding.ghinda.com

About Ruby: A tale of searching for the main

Here is a fun way to play with Ruby while trying to explore some basic concepts.

If you are learning Ruby, I hope this article demonstrates that Ruby enables excellent self-exploration. Most questions about its functionality can be answered through hands-on experimentation and just a little documentation.

Context

Someone asked me the following question:

When I write in IRB or in a file the following

defmy_methodend
Enter fullscreen modeExit fullscreen mode

Where is themy_method created? What object will contain this method? And what is the access modifier for this method?

Detective Tools

I will use some simple but powerful tools:

  1. Ruby interpreter installed on the local machine

  2. Ruby documentation athttps://docs.ruby-lang.org/en/3.2

Solution

Instead of giving a direct answer, let's put on the hat of an investigator and try to find some clues about this while having fun with Ruby.

First question: When I run a script who is there already?

First, let's create a file calledexploration.rb and ask it aboutself:

# exploring.rbputs"Who is here?#{self}"# => `Who is here? main`
Enter fullscreen modeExit fullscreen mode

We notice this returnmain. But what is thismain?

Maybe we can ask Ruby about it. I will add a couple of methods to find out more aboutmain while trying to dig around:

puts"Who is here:#{self}"puts"What is your class:#{self.class}"puts"What are your ancestors:#{self.class.ancestors}"puts"What is the current exection stack:#{caller.inspect}"# =>Whoishere:mainWhatisyourclass:ObjectWhatareyourancestors:[Object,Kernel,BasicObject]Whatisthecurrentcallerstack:[]
Enter fullscreen modeExit fullscreen mode

We found out thatmain exists insideObject. Good, let's poke around then:

puts"Is `main` a public method?#{self.public_methods.include?(:main)}"puts"Is `main` a public method?#{self.protected_methods.include?(:main)}"puts"Is `main` a private method?#{self.private_methods.include?(:main)}"puts"Is `main` a singleton method?#{self.singleton_methods.include?(:main)}"# =>Is`main`apublicmethod?falseIs`main`apublicmethod?falseIs`main`aprivatemethod?falseIs`main`asingletonmethod?false
Enter fullscreen modeExit fullscreen mode

It appears thatmain is not a method. That seems strange, right? Ifmain is not a method then what could it be?

I think we should now explore how we are asking the question: we are usingputs to ask the question. Here is whatthe documentation says

Documentation for puts

Then maybe something is deturning theputs self and that something could be a methodto_s onObject. Here let's check a bit the documentation forObject#to_s

Documentation for to_s

CLUE A - FOUND ->main is just a string printed by Ruby in the initial execution context. Nothing special here.

Second: Where is a method created when I don't define an object?

For this, we will create a second file calledexploration_method.rb and again ask it about itself:

defmy_methodputs"Who is here:#{self}"puts"What is your class:#{self.class}"puts"What are your ancestors:#{self.class.ancestors}"puts"What is the current exection stack:#{caller.inspect}"puts"What is your current file:#{__FILE__}"endmy_method# =>Whoishere:mainWhatisyourclass:ObjectWhatareyourancestors:[Object,Kernel,BasicObject]Whatisthecurrentexectionstack:["exploration_method.rb:10:in `<main>'"]Whatisyourcurrentfile:exploration_method.rb
Enter fullscreen modeExit fullscreen mode

CLUE B - FOUND ->my_method is created insideObject

Third: What kind of method ismy_method?

Let's askObject about this method:

defmy_methodendputs"Is `my_method` a public method?#{Object.public_methods.include?(:my_method)}"puts"Is `my_method` a private method?#{Object.private_methods.include?(:my_method)}"puts"Is `my_method` a private method?#{Object.protected_methods.include?(:my_method)}"puts"Is `my_method` a singleton method?#{Object.singleton_methods.include?(:my_method)}"# =>Is`my_method`apublicmethod?falseIs`my_method`aprivatemethod?trueIs`my_method`aprivatemethod?falseIs`my_method`asingletonmethod?false
Enter fullscreen modeExit fullscreen mode

CLUE C - FOUND -> Seems likemy_method is a private method defined onObject

I will try to test this. I know thatpublic_send can only call public methods on an object. And I know thatsend will call any method.

I will create a simple method that will printself andobject_id and try out various things:

defmy_methodputs"Who is here:#{self} with object_id#{self.object_id}"endmy_method# =>Whoishere:mainwithobject_id60
Enter fullscreen modeExit fullscreen mode

What happens if I try the following things:

defmy_methodputs"Who is here:#{self} with object_id#{self.object_id}"endObject.new.my_method# =>testing.rb:5:in`<main>': private method `my_method'calledfor#<Object:0x00000001054229c0> (NoMethodError)
Enter fullscreen modeExit fullscreen mode

What about usingpublic_send -> the same result

defmy_methodputs"Who is here:#{self} with object_id#{self.object_id}"endObject.new.public_send(:my_method)# =>testing.rb:5:in`public_send': private method `my_method'calledfor#<Object:0x0000000105952968> (NoMethodError)
Enter fullscreen modeExit fullscreen mode

Thensend should work:

defmy_methodputs"Who is here:#{self} with object_id#{self.object_id}"endObject.new.send(:my_method)# =>Whoishere:#<Object:0x0000000107412960> with object_id 60
Enter fullscreen modeExit fullscreen mode

So ifmy_method will be defined on theObject then I must be able to use it in my custom objects right?

defmy_methodputs"Who is here:#{self} with object_id#{self.object_id}"endclassSimpleObjectdeftestmy_methodendendSimpleObject.new.test
Enter fullscreen modeExit fullscreen mode

Indeed it works! I now confirmed in two ways thatmy_method is indeed created at runtime inObject

Four: Ismy_method defined in Object or added to Object?

Just to push things more, let's try to find out what kind of mechanism is used to add this method to theObject (well from how I formulated this you probably can guess):

defmy_methodputs"Who is here:#{self} with object_id#{self.object_id}"endclassSimpleObjectdeftestmy_methodendendputs"Are you defining `my_method`?#{SimpleObject.private_method_defined?(:my_method)}"uts"Do you have this method?#{Object.private_methods.include?(:my_method)}"# =>Areyoudefining`my_method`?trueDoyouhavethismethod?true
Enter fullscreen modeExit fullscreen mode

This is a method that was added to theObject andObject is defining this method as a private method.

We can test this by usingObject#method_added

classObjectdefself.method_added(method_name)puts"I just added#{method_name.inspect} on#{self}"endenddefmy_methodputs"Who is here:#{self} with object_id#{self.object_id}"end# =>Ijustadded:my_methodonObject
Enter fullscreen modeExit fullscreen mode

CLUE D - FOUND The method isdefined onObject but as a private method. That's why it is accessible everywhere, but that is also why if you add this kind of method, you would be polluting every object with extra private methods if you define top-level methods like that or you might redefine already existing methods.

Five: Ismy_method part of BasicObject

In the beginning, I asked this question inside theexploration.rb file:

puts"What are your ancestors:#{self.class.ancestors}"# => What are your ancestors: [Object, Kernel, BasicObject]
Enter fullscreen modeExit fullscreen mode

I think it is worth investigating a bit what happens withBasicObject andKernel.

Question 1: DoesBasicObjectincludemy_method?

defmy_methodputs"Who is here:#{self} with object_id#{self.object_id}"endclassAVeryBasicObject<BasicObjectdeflook_for_my_methodmy_methodendendbeginAVeryBasicObject.new.look_for_my_methodrescueNameError=>_puts"`my_method` is not here"end# => `my_method` is not here
Enter fullscreen modeExit fullscreen mode

BasicObject does not includemy_method

Question 2: DoesKernelmodule includemy_method?

First I will includeKernel and test to make sure it is included properly by testing that the newly created object hasputs

classAnObjectWithKernel<BasicObjectinclude::Kerneldefa_methodputs"Inside AnObjectWithKernel - it now includes puts"enddeflook_for_my_methodmy_methodendendobj=AnObjectWithKernel.newobj.a_method# => Inside AnObjectWithKernel - it now includes putsbeginobj.look_for_my_methodrescueNameError=>_puts"`my_method` is not here"end# => `my_method` is not here
Enter fullscreen modeExit fullscreen mode

Kernel does not includemy_method

Conclusion

In conclusion, when defining a method without specifying an object in Ruby, the method is created as a private method inside theObject class.

This method can be called on any custom object, as it is added during the execution of the file.

Make sure you are not defining in the main context a method that is already defined by Object, Kernel or any other classes by Ruby cause that will create some not-wanted side effects.

Check out the result of executing the following code that I added inside a file calledside_effects.rb:

classSimpleObjectdefmy_methodputs"Who is here inside?#{self}"puts"Where is `to_s` defined?#{method(:to_s).source_location.inspect}"endendputs"Who is here in the initial execution context?#{self}"SimpleObject.new.my_methoddefto_s"THIS IS NOT MAIN"endclassSecondSimpleObjectdefmy_methodputs"Who is here inside?#{self}"puts"Where is `to_s` defined?#{method(:to_s).source_location.inspect}"endendputs"Who is here in the initial execution context?#{self}"SecondSimpleObject.new.my_method
Enter fullscreen modeExit fullscreen mode

The output will be:

Whoishereintheinitialexecutioncontext?mainWhoishereinside?#<SimpleObject:0x00000001057b21f8>Whereis`to_s`defined?nilWhoishereintheinitialexecutioncontext?mainWhoishereinside?THISISNOTMAINWhereis`to_s`defined?["side_effects.rb",12]
Enter fullscreen modeExit fullscreen mode

Notice thatto_s from SecondSimpleObject has now the valueTHIS IS NOT MAIN and it is reported to be defined inside_effects.rb file?

Updates

  • Ufuk Kayserilioglu gave mevaluable feedback on an earlier version of this article that I incorrectly usedmethod_defined? instead ofprivate_method_defined? in the section about if method was added or defined. Thus I refactored that section and now it states clearly thatmy_method is defined as a private method on theObject

Enjoyed this article?

Join myShort Ruby Newsnewsletter for weekly Ruby updates from the community. For more Rubylearning resources, visitrubyandrails.info. You can also find meonRuby.socialorLinkedinorTwitter

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Find me on Mastodon at @lucian@ruby.social or twitter @lucianghindaI am curating a weekly newsletter about Ruby and Rails at https://newsletter.shortruby.com
  • Work
    Senior Product Engineer | Senior Ruby Developer
  • Joined

More fromLucian Ghinda

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp