Blog #23

Ruby on Rails interview questions

# Ruby on Rails interview questions

1. What is difference between calling super and calling super()?

A call to super invokes the parent method with the same arguments that were passed to the child method. An error will therefore occur if the arguments passed to the child method don’t match what the parent is expecting.

A call to super() invokes the parent method without any arguments, as presumably expected. As always, being explicit in your code is a good thing.

supper example:

class Parent
  def say(message)
    p message
  end
end
class Child < Parent
  def say(message)
    super
  end
end
Child.new.say('Hello world!') # => "Hello world!"

supper() example:

class Parent
  def say
    p "I'm the parent"
class Child < Parent
  def say(message)
    super
  end
end
Child.new.say('Hello!') # => ArgumentError (wrong number of arguments (given 1, expected 0))
class Parent
  def say
    p "I'm the parent"
  end
end
class Child < Parent
  def say(message)
    super()
  end
end
Child.new.say('Hi!') # => "I'm the parent"

2. What is the difference between a class and a module?

Modules are collections of methods and constants. They cannot generate instances. Classes may generate instances (objects), and have per-instance state (instance variables).

Modules may be mixed in to classes and other modules. The mixed in module’s constants and methods blend into that class’s own, augmenting the class’s functionality. Classes, however, cannot be mixed in to anything.

A class may inherit from another class, but not from a module.

A module may not inherit from anything.

module Swimmable
  def swim
    puts "I'm swimming!"
  end
  def self.module_swim_method
    puts 'module_swim_method'
  end
end
module Flyable
  include Swimmable
  def fly
    puts "I'm flying!"
  end
end
class Duck
  include Flyable
end
duck = Duck.new
duck.swim  # Output: I'm swimming!
duck.fly   # Output: I'm flying!
duck.module_swim_method # Output: NoMethodError
Swimmable.module_swim_method # Output: module_swim_method
Flyable.module_swim_method # Output: NoMethodError

3. Which of the expressions listed below will result in "false"?

true    ? "true" : "false"
false   ? "true" : "false"
nil     ? "true" : "false"
1       ? "true" : "false"
0       ? "true" : "false"
"false" ? "true" : "false"
""      ? "true" : "false"
[]      ? "true" : "false"

In Ruby, the only values that evaluate to false are false and nil. Everything else – even zero (0) and an empty array ([]) – evaluates to true.

This comes as a real surprise to programmers who have previously been working in other languages like JavaScript.

(Thanks to Ruby Gotchas for this question.)

4. Is the line of code below valid Ruby code? If so, what does it do? Explain your answer.

-> (a) {p a}["Hello world"]

Yes, it’s valid. Here’s how to understand what it does:

The -> is often called the “stabby proc”. It’s also called the “stabby lambda”, as it creates a new Proc instance that is a lambda. All lambdas are Procs, but not all Procs are lambdas. There are some slight differences between the two.)

This particular Proc takes one parameter (namely, a). When the Proc is called, Ruby executes the block p a, which is the equivalent of puts(a.inspect) (a subtle, but useful, difference which is why p is sometimes better than puts for debugging). So this Proc simply prints out the string that is passed to it.

You can call a Proc by using either the call method on Proc, or by using the square bracket syntax, so this line of code also invokes the Proc and passes it the string “Hello World”.

So putting that all together, this line of code (a) creates a Proc that takes a single parameter a which it prints out and (b) invokes that Proc and passes it the string “Hello world”. So, in short, this line of code prints “Hello World”.

5. What is the difference between the Object methods clone and dup?

Object#dup creates a shallow copy of an object. For example, it will not copy any mixed-in module methods, whereas Object#clone will. This can be shown with the following code example:

class Klass
  attr_accessor :str
end
module Foo
  def foo; 'foo'; end
end
s1 = Klass.new #=> #
s1.extend(Foo) #=> #
s1.foo #=> "foo"
s2 = s1.clone #=> #
s2.foo #=> "foo"
s3 = s1.dup #=> #
s3.foo #=> NoMethodError: undefined method `foo' for #

6. What is the difference between extend and include?

Given the following class definitions:

module ReusableModule
  def module_method
    puts "Module Method: Hi there! I'm a module method"
  end
end
class ClassThatIncludes
  include ReusableModule
end
class ClassThatExtends
  extend ReusableModule
end

Here’s how ClassThatIncludes behaves:

# A class method does not exist
>> ClassThatIncludes.module_method
NoMethodError: undefined method `module_method' for ClassThatIncludes:Class
# A valid instance method exists
>> ClassThatIncludes.new.module_method
Module Method: Hi there! I'm a module method
=> nil

Here’s how ClassThatExtends behaves:

# A valid class method exists
>> ClassThatExtends.module_method
Module Method: Hi there! I'm a module method
=> nil
# An instance method does not exist
ClassThatExtends.new.module_method
NoMethodError: undefined method `module_method' for #<ClassThatExtends:0x007ffa1e0317e8>

We should mention that object.extend ExampleModule makes ExampleModule methods available as singleton methods in the object.

7. What is the difference between private and protected?

class A
  def pub
    1
  end
  
  def use_pri
    pri
  end
  
  def use_pro
    pro
  end
  private
  def pri
    2
  end
  protected
  def pro
    3
  end
end
class B < A
  def call_a_pub
    A.new.pub
  end
  def call_a_pri
    A.new.pri
  end
  def call_a_pro
    A.new.pro
  end
end
A.new.pub # => 1
A.new.use_pri # => 2
A.new.use_pro # => 3
A.new.pri # => NoMethodError: private method `pri' called for #<A:0x00000001085f1f50>
A.new.pro # => NoMethodError: protected method `pro' called for #<A:0x00000001085b8cf0>
B.new.call_a_pub # => 1
B.new.use_pri # => 2
B.new.use_pro # => 3
B.new.call_a_pri # => NoMethodError: private method `pri' called for #<A:0x00000001083bff70>
B.new.call_a_pro # => 3

8. What is the difference between * and ** arguments?

*r captures positional arguments as an array.

def a(*r)
  p r
end
a(1, 2, 3) # Output: [1, 2, 3]

**r captures keyword arguments as a hash.

def b(**r)
  p r
end
b(name: "Alice", age: 30) # Output: {:name=>"Alice", :age=>30}

9. Ruby proc, block, lambda

Nature:

Return Behavior:

Argument Checking:

Example:

Blocks

[1, 2, 3].each do |num|
  puts num * 2
end
# or using {}
[1, 2, 3].each { |num| puts num * 2 }

Procs

my_proc = Proc.new { |x| x * 2 }
puts my_proc.call(5)  # Output: 10

Lambdas

my_lambda = ->(x) { x * 2 }
puts my_lambda.call(5)  # Output: 10
# Parameter check example
my_lambda_with_check = lambda { |x| x * 2 }
my_lambda_with_check.call(5)  # Works
# my_lambda_with_check.call      # Raises an ArgumentError

10. Object ID

String

a = "hello"
b = "hello"

# them object ID not equal
a.object_id # => 73760
b.object_id # => 73780

# but I can use freeze
a = "hello".freeze
b = "hello".freeze
a.object_id # => 73800
b.object_id # => 73800

Integer

5.object_id = 11 # 5 * 2 + 1
22.object_id = 45 # 22 * 2 + 1

Float

a = 1.2
b = 1.2
a.object_id == b.object_id # true

Array

a = [1, 2, 3]
b = [1, 2, 3]
a.object_id == b.object_id # false

Symbol

a = :hello
b = :hello
a.object_id == b.object_id # true

Hash

a = {x: 123}
b = {x: 123}
a.object_id == b.object_id # false

Nil

nil.object_id = 4 # always
nil.object_id = 8 # always with ruby_64

# References

https://stackoverflow.com/questions/31816149/difference-between-calling-super-and-calling-super

https://www.toptal.com/ruby/interview-questions

https://www.ruby-lang.org/en/documentation/faq/8/#:~:text=What%20is%20the%20difference%20between%20a%20class%20and%20a%20module,instance%20state%20(instance%20variables).

https://www.reddit.com/r/ruby/comments/ojsq9z/when_to_use_protected_and_private/