How to Add a Method to the Global Scope in Ruby

How can I add a method to the global scope in Ruby?

TL;DR extend-ing a module at the top level adds its methods to the top level without adding them to every object.

There are three ways to do this:

Option 1: Write a top-level method in your gem

#my_gem.rb

def add_blog(blog_name)
puts "Adding blog #{blog_name}..."
end
#some_app.rb

require 'my_gem' #Assume your gem is in $LOAD_PATH
add_blog 'Super simple blog!'

This will work, but it's not the neatest way of doing it: it's impossible to require your gem without adding the method to the top level. Some users may want to use your gem without this.

Ideally we'd have some way of making it available in a scoped way OR at the top level, according to the user's preference. To do that, we'll define your method(s) within a module:

#my_gem.rb
module MyGem

#We will add methods in this module to the top-level scope
module TopLevel
def self.add_blog(blog_name)
puts "Adding blog #{blog_name}..."
end
end

#We could also add other, non-top-level methods or classes here
end

Now our code is nicely scoped. The question is how do we make it accessible from the top level, so we don't always need to call MyGem::TopLevel.add_blog ?

Let's look at what the top level in Ruby actually means. Ruby is a highly object oriented language. That means, amongst other things, that all methods are bound to an object. When you call an apparently global method like puts or require, you're actually calling a method on a "default" object called main.

Therefore if we want to add a method to the top-level scope we need to add it to main. There are a couple of ways we could do this.

Option 2: Adding methods to Object

main is an instance of the class Object. If we add the methods from our module to Object (the monkey patch the OP referred to), we'll be able to use them from main and therefore the top level. We can do that by using our module as a mixin:

#my_gem.rb

module MyGem
module TopLevel
def self.add_blog
#...
end
end
end

class Object
include MyGem::TopLevel
end

We can now call add_blog from the top level. However, this also isn't a perfect solution (as the OP points out), because we haven't just added our new methods to main, we've added them to every instance of Object! Unfortunately almost everything in Ruby is a descendant of Object, so we've just made it possible to call add_blog on anything! For example, 1.add_blog("what does it mean to add a blog to a number?!") .

This is clearly undesirable, but conceptually pretty close to what we want. Let's refine it so we can add methods to main only.

Option 3: Adding methods to main only

So if include adds the methods from a module into a class, could we call it directly on main? Remember if a method is called without an explicit receiver ("owning object"), it will be called on main.

#app.rb

require 'my_gem'
include MyGem::TopLevel

add_blog "it works!"

Looks promising, but still not perfect - it turns out that include adds methods to the receiver's class, not just the receiver, so we'd still be able to do strange things like 1.add_blog("still not a good thing!") .

So, to fix that, we need a method that will add methods to the receiving object only, not its class. extend is that method.

This version will add our gem's methods to the top-level scope without messing with other objects:

#app.rb

require 'my_gem'
extend MyGem::TopLevel

add_blog "it works!"
1.add_blog "this will throw an exception!"

Excellent! The last stage is to set up our Gem so that a user can have our top-level methods added to main without having to call extend themselves. We should also provide a way for users to use our methods in a fully scoped way.

#my_gem/core.rb

module MyGem
module TopLevel
def self.add_blog...

end
end

#my_gem.rb

require './my_gem/core.rb'
extend MyGem::TopLevel

That way users can have your methods automatically added to the top level:

require 'my_gem'
add_blog "Super simple!"

Or can choose to access the methods in a scoped way:

require 'my_gem/core'
MyGem::TopLevel.add_blog "More typing, but more structure"

Ruby acheives this through a bit of magic called the eigenclass. Each Ruby object, as well as being an instance of a class, has a special class of its own - its eigenclass. We're actually using extend to add the MyGem::TopLevel methods to main's eigenclass.


This is the solution I would use. It's also the pattern that Sinatra uses. Sinatra applies it in a slightly more complex way, but it's essentially the same:

When you call

require 'sinatra'

sinatra.rb is effectively prepended to your script. That file calls

require sinatra/main

sinatra/main.rb calls

extend Sinatra::Delegator

Sinatra::Delegator is the equivalent of the MyGem::TopLevel module I described above - it causes main to know about Sinatra-specific methods.

Here's where Delgator differs slightly - instead of adding its methods to main directly, Delegator causes main to pass specific methods on to a designated target class - in this case, Sinatra::Application.

You can see this for yourself in the Sinatra code. A search through sinatra/base.rb shows that, when the Delegator module is extended or included, the calling scope (the "main" object in this case) will delegate the following methods to Sinatra::Application:

:get, :patch, :put, :post, :delete, :head, :options, :link, :unlink,
:template, :layout, :before, :after, :error, :not_found, :configure,
:set, :mime_type, :enable, :disable, :use, :development?, :test?,
:production?, :helpers, :settings, :register

Ruby gem won't add method to global scope using extend

Sergio solved this for me, though I don't quite understand how!

It was considered good practice to encapsulate the methods in a module, so that users of the gem can use them directly (hello) or scoped (Example::hello) as they pleased.

By removing self. the method can be accessed directly only. By including self. it doesn't work at all. However, by doing:

module Example
extend self
def hello
puts "Hello"
end
end
extend Example

...it does work in both ways.

Ruby - create a global variable from within a method

You can create a global variable from everywhere if you prefix it with a "$" sign, like: $var

If you declare it in a method, make sure you run that method before calling your variable.

For example:

def global_test
$name = "Josh"
end

$name # => nil

global_test() # Your method should run at least once to initialize the variable

$name # => "Josh"

So, in your example, if you would like to use s_name from outside of your method, you can do it by adding a '$' sign before it like this: $s_name

Here is a good description about ruby variable types:

http://www.techotopia.com/index.php/Ruby_Variable_Scope

But as you can read in it (and mostly everywhere in "ruby best practices" and styleguides) it is better to avoid global variables.

Create a global Rails method inside a module?

TLDR

You need to "extend" the Kernel module by including your methods into it with include.

Global methods

Generally a "global" method in Ruby means creating a method in Kernel or Object. The method is then available to all (most) classes because they inherit from Object (which includes Kernel). Your method can be called without a prefix from most places because the implicit self in any given context will be an object that inherits from Object and therefore contains the method you defined.

One way to define such a method is just to place its definition in a file outside of any class or module:

def my_method
# do fancy stuff
end

Of course, you have to make sure that file gets loaded (i.e. required) somewhere.

Global methods from a module

If you want your method to be organized within a module (which is a good idea), you need to include that module into Kernel like this:

module MySpecialMethods
def my_method
# do fancy stuff
end
end

module Kernel
private
include MySpecialMethods # This is where the method gets added to Kernel
end

In Rails, I would put the MySpecialMethods module in lib/, add put the Kernel part inside an initializer file like config/initializers/extend_kernel.rb. You also need to help Rails "find" your module in lib/ by adding config.autoload_paths += %W(#{config.root}/lib) to application.rb

Consider avoiding global methods

It's worth nothing that there are very few situations where it's a good idea to extend Kernel in this way. It's possible (likely?) that for your situation it is better to put your methods in a module and explicitly call them with the module name like MyModule.my_method.

module MyModule
def self.my_method
# do fancy stuff here
end
end

There are many places you could place this code. In Rails, I'd recommend putting it in the lib/ folder as above and then making sure Rails can find it by adding config.autoload_paths += %W(#{config.root}/lib) to application.rb

From a Gem

Creating a gem is beyond the scope of this question, but supposing you created a gem, typically the modules within that gem are available in your Rails app as long as you specify the gem in your Gemfile. This is because Rails apps by default call Bundler.require. As long as the gem follows proper naming conventions, the main gem file is required automatically, and the main gem file requires any other necessary files.

If you want to extend Kernel from a gem, just put the Kernel extension code (from above) inside a file that is required (directly or indirectly) when your gem is required.

Notes

  1. Rails initializers only run once when you boot the Rails server, so changes to them are not autoloaded like many other pieces of Rails in development.

  2. If you're tweaking Rails' autoload_paths, you might also want to tweak eager_load_paths (see this blog post)

  3. There are some objects in Ruby that do not inherit from Object (e.g. BasicObject), so technically these "global" methods will not be available everywhere. That's not likely to cause problems for you.

  4. Using Bundler.require in a Rails app is perhaps controversial (see this blog post for more information on that)

How to call a method from the global scope with same name as an instance method in ruby?

This should work

class String
def hello
Kernel.send(:hello,self)
end
end

How to access global method from object?

There maybe a better way if you can explain what exactly you're trying to do. But, if you must to do it anyway then:

def login(user, pass)
puts 'global login'
puts "user: #{user}, pass: #{pass}"
end

class Bob
def login(pass)
self.class.send(:login, 'bob',pass) #ERROR#
end
end

login('hello','world')
bob = Bob.new
bob.login('world')

#=> global login
#=> user: hello, pass: world
#=> global login
#=> user: bob, pass: world

Ruby method lookup in regards to globals/scopes

Ruby's method lookup algorithm is actually really simple:

  • retrieve the class pointer of the receiver
  • if the method is there, invoke it
  • otherwise retrieve the superclass pointer, and repeat

That's it.

If the algorithm comes to a point where there is no more superclass, but it still hasn't found the method yet, it will restart the whole process again, with method_missing as the message and the name of the original message prepended to the arguments. But that's it. That's the whole algorithm. It is very small and very simple, and it has to be very small and very simple because method lookup is the single most often executed operation in an object-oriented language.

Note: I am completely ignoring Module#prepend / Module#prepend_features since I just don't know enough about how it works. I only know what it does, and that's good enough for me.

Also note: I am ignoring performance optimizations such as caching the result of a method lookup in something like a Polymorphic Inline Cache.

Okay, but here's the trick: where exactly do those class and superclass pointers point to? Well, they do not point to what the Object#class and Class#superclass methods return. So, let's step back a little.

Every object has a class pointer that points to the class of the object. And every class has a superclass pointer that points to its superclass.

Let's start a running example:

class Foo; end

Now, we have class Foo, and its superclass pointer points to Object.

foo = Foo.new

And our object foo's class pointer points to Foo.

def foo.bar; end

Now things start to get interesting. We have created a singleton method. Well, actually, there is no such thing as a singleton method, it's really just a normal method in the singleton class. So, how does this work? Well, now the class pointer points to foo's singleton class and foo's singleton class's superclass pointer points to Foo! In other words, the singleton class was inserted in between foo and its "real" class Foo.

However, when we ask foo about its class, it still responds Foo:

foo.class #=> Foo

The Object#class method knows about singleton classes, and simply skips over them, following the superclass pointer until it finds a "normal" class, and returns that.

Next complication:

module Bar; end

class Foo
include Bar
end

What happens here? Ruby creates a new class (let's call it Barʹ), called an include class. This class's method table pointer, class variable table pointer, and constant table pointer point to Bar's method table, class variable table, and constant table. Then, Ruby makes Barʹ's superclass pointer point to Foo's current superclass, and then makes Foo's superclass pointer point to Barʹ. In other words, including a module creates a new class that gets inserted as the superclass of the class the module is included into.

There's a slight complication here: you can also include modules into modules. How does that work? Well, Ruby simply keeps track of the modules that were included into a module. And then, when the module is included into a class, it will recursively repeat the steps above for every included module.

And that's all you need to know about the Ruby method lookup:

  • find the class
  • follow the superclass
  • singleton classes insert above objects
  • include classes insert above classes

Now let's look at some of your questions:

When calling methods explicitly on a class, there are lots of illustrations on the order in which the classes, and modules included by them are searched (and thus exactly what super calls in each case). But when not explicitly calling a method, e.g. a plain func args rather than self.func args what is the search order?

The same. self is the implicit receiver, if you don't specify a receiver, the receiver is self. And parentheses are optional. In other words:

func args

is exactly the same as

self.func(args)

Why does in my example below, the member method calling func find the member method before the global, but func2 finds the global without method_missing being called?

There is no such thing as a "global method" in Ruby. There is also no such thing as a "member method". Every method is an instance method. Period. There are no global, static, class, singleton, member methods, procedures, functions, or subroutines.

A method defined at the top-level becomes a private instance method of class Object. Test inherits from Object. Run the steps I outlined above, and you will find exactly what is going on:

  • Retrieve x's class pointer: Test
  • Does Test have a method called func: Yes, so invoke it.

Now again:

  • Retrieve x's class pointer: Test
  • Does Test have a method called func2: No!
  • Retrieve Test's superclass pointer: Object
  • Does Object have a method called func2: Yes, so invoke it.

And when the global is instead an module/class/type, why is the member with the same name not found at all?

Again, there is no global here, there are no members here. This also doesn't have anything to do with modules or classes. And Ruby doesn't have (static) types.

Math

is a reference to a constant. If you want to call a method with the same name, you have to ensure that Ruby can tell that it's a method. There are two things that only methods can have: a receiver and arguments. So, you can either add a receiver:

self.Math

or arguments:

Math()

and now Ruby knows that you mean the method Math and not the constant Math.

The same applies to local variables, by the way. And to setters. If you want to call a setter instead of assigning a local variable, you need to say

self.func = 'setter method'

How to access a (shadowed) global function in ruby

At the top-level a def adds a private method to Object.

I can think of three ways to get the top-level function:

(1) Use send to invoke the private method on Object itself (only works if the method is not a mutator since Object will be the receiver)

Object.send(:fn)

(2) Get a Method instance of the top-level method and bind it to the instance you want to invoke it on:

class Bar
def fn
Object.instance_method(:fn).bind(self).call
end
end

(3) Use super (assumes no super classes of Bar below Object redefine the function)

class Bar
def fn
super
end
end

UPDATE:

Since solution (2) is the preferable one (in my opinion) we can try to improve the syntax by defining a utility method on Object called super_method:

class Object
def super_method(base, meth, *args, &block)
if !self.kind_of?(base)
raise ArgumentError, "#{base} is not a superclass of #{self}"
end

base.instance_method(meth).bind(self).call(*args, &block)
end
end

Use like the following:

class Bar
def fn
super_method Object, :fn
end
end

Where the first argument to super_method must be a valid superclass of Bar, the second argument the method you want to invoke, and all remaining arguments (if any) are passed along as parameters to the selected method.

Hacking ActiveRecord: add global named scope

Rails 5, ApplicationRecord (Hope it helps others)

# app/models/concerns/not_older_than.rb

module NotOlderThan
extend ActiveSupport::Concern

included do
scope :not_older_than, -> (time, table = self.table_name){
where("#{table}.created_at >= ?", time.ago)
}
end
end

# app/models/application_record.rb

class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
include NotOlderThan
end

# app/models/user.rb
class User < ApplicationRecord
# Code
end

# Usage
User.not_older_than(1.week)

In Rails 5, all models are inherited from ApplicationRecord by default. If you wan to apply this scope for only particular set of models, add include statements only to those model classes. This works for join queries and chained scopes as well.



Related Topics



Leave a reply



Submit