Ruby Method Calls Declared in Class Body

Calling method within Class

class TestClass
# a class method
def self.test_method
puts "Hello from TestClass"
end

# an instance method
def test_method
puts "Hello from an instance of TestClass"
end
end

# call the class method
TestClass.test_method

# create and instance object of TestClass
instance_of_TestClass = TestClass.new

# call the instance method of the new object
instance_of_TestClass.test_method

Ruby methods called at top of classes

It'll be easier to understand by looking at it in action.

# Step 1
# Run this in IRB
class MyClass
def self.another_method
puts "Executed when class is loaded to memory"
end
end

# Step 2
# Now when you run the following in IRB after that, its going to execute the
# class method which was defined in its parent class (MyClass)
class MyAwesomeClass < MyClass
another_method # Executed ONCE when the class is loaded to memory for the first
def initialize; end
end

# Now if you instantiate MyAwesomeClass though, it will not print the same as of
# Step 2 as class method :another_method already has been executed when class
# was loaded
awesome1 = MyAwesomeClass.new

The body of a class will be interpreted and executed sequentially & behaves much like you'd expect it inside an instance method. Try putting a puts "Hello World" in the body of your class definition like so:

class MyClass
# Executes when the class is defined in IRB(Loaded to memory)
puts "Hello World"
end

How to test in Ruby/Rails if method has been called in class body?

It's usually recommended to test the behavior, not the implementation. In this case, whatever acts_as_paranoid provides for this class in terms of behavior, is what you want to test.

However, if you trust that calling acts_as_paranoid correctly provides all the behavior you need and just want to test that it is added to the class, you can use:

assert User.included_modules.include? ActsAsParanoid::Core

To figure this out I just briefly looked at the source code for acts_as_paranoid here: https://github.com/ActsAsParanoid/acts_as_paranoid/blob/master/lib/acts_as_paranoid.rb#L8

You can see that on line 50, it extends the ActsAsParanoid module to ActiveRecord::Base, which gives the model classes access to the acts_as_paranoid method. And if you look at the definition of this method, you can see it calls include ActsAsParanoid::Core

Why can't a class method have the same name as a non-class method?

tldr; within the scope of the instance, the puts resolves to self.puts (which then resolves to the locally defined method, and not Kernel#puts). This method overriding is a form of shadowing.

Ruby has an 'implicit self' which is the basis for this behavior and is also how the bare puts is resolved - it comes from Kernel, which is mixed into every object.

The Kernel module is included by class Object, so its methods [like Kernel#puts] are available in every Ruby object. These methods are called without a receiver and thus can be called in functional form [such as puts, except when they are overridden].

To call the original same-named method here, the super keyword can be used. However, this doesn't work in the case where X#another_method calls X#puts with arguments when it expects to be calling Kernel#puts. To address that case, see Calling method in parent class from subclass methods in Ruby (either use an alias or instance_method on the appropriate type).

class X
def puts
super "hello!"
end
end

X.new.puts

P.S. The second example should trivially fail, as my_puts clearly does not take any parameters, without any confusion of there being another "puts". Also, it's not a syntax error as it occurs at run-time after any language parsing.

Method invocation in class definition?

In Ruby, class declarations are just chunks of code, executed in order.

It's important to remember that inside a class definition, self points to the class itself. validates is a class method of ActiveRecord. As the class is being defined, code in the definition is executed. The validates method resolves to a class method of ActiveRecord, so is called during class definition.

In your Person example, it will only print once, because you only define the class once.

Consider the following:

class Foo
def self.validates_nothing(sym)
(@@syms ||= []) << sym
puts "!!! Here there be logic"
end

def validate
@@syms.each { |s| puts s }
end
end

This defines a class with a class method validates_nothing, and an instance method, validate. validates_nothing just gathers whatever arguments are given it, validate just dumps them out.

class Bar < Foo
validates_nothing :anything
validates_nothing :at_all
end

This defines a subclass. Note that when the class method validates_nothing is called, it prints:

Here there be logic
Here there be logic

If we create a new bar and call validate, we get the expected output:

> Bar.new.validate
!!!anything
!!!at_all

Add method to a class which can only be accessed inside specific class

You could use refinements for this:

Due to Ruby's open classes you can redefine or add functionality to existing classes. This is called a “monkey patch”. Unfortunately the scope of such changes is global. All users of the monkey-patched class see the same changes. This can cause unintended side-effects or breakage of programs.

Refinements are designed to reduce the impact of monkey patching on other users of the monkey-patched class. Refinements provide a way to extend a class locally. Refinements can modify both classes and modules.

Something like this:

module HashPatches
refine Hash do
def new_hash_method
# ...
end
end
end

and then:

class YourClass
using HashPatches

def m
{}.new_hash_method
end
end

That would let you call YourClass.new.m (which would use new_hash_method) but it wouldn't pollute Hash globally so outside YourClass, some_hash.new_hash_method would be a NoMethodError.

Reading:

  • Official Refinements docs
  • Refinements spec

What is the 'self' keyword doing exactly in this Class method?

self is the class Restaurant. def self.method is how you implement a method on the class itself rather than an instance of the class. Restaurant.filter_by_city(...) rather than Restaurant.new.filter_by_city(...).

self changes in Ruby depending on context. Within a method, self is the object the method was called on.

Within the class Restaurant block, and outside of any method, self is the Restaurant object which is a Class object. Everything is an object in Ruby. Everything.

You can also do this by declaring a block where the class is the instance.

class << self
def filter_by_city(restaurants, city)
restaurants.select { |restaurant| restaurant.city == city }
end
end

Normally you'd use this syntax if you have a lot of class methods.

See Self in Ruby: A Comprehensive Overview for more.



Related Topics



Leave a reply



Submit