1. How would you implement a thread-safe Singleton in Ruby?
Answer:
- The Singleton pattern ensures that a class has only one instance and provides a global point of access to it. In Ruby, the
Singleton
module can be used, but it may not be thread-safe. Here’s a custom implementation usingmutex
for thread safety:
class MySingleton
@instance = nil
@mutex = Mutex.new
def self.instance
return @instance if @instance
@mutex.synchronize do
@instance ||= new
end
@instance
end
private_class_method :new
end
2. Explain how Ruby handles method dispatch and how you can modify it.
Answer:
- Ruby handles method dispatch through the method lookup path: first checking the object's singleton class, then its class, then included modules, and finally parent classes.
- You can modify method dispatch using techniques like
alias_method
,prepend
,method_missing
, or even overridingrespond_to?
. -
Example using
prepend
:
module LoggerModule def greet puts "Logging: Calling greet" super end end class MyClass prepend LoggerModule def greet "Hello!" end end puts MyClass.new.greet # => Logs the message and then returns "Hello!"
3. How does garbage collection work in Ruby, and how can you optimize it?
Answer:
- Ruby uses a mark-and-sweep garbage collector, which tracks objects in memory and cleans up those no longer in use.
- The generational garbage collection (introduced in Ruby 2.1) improves performance by categorizing objects into generations and focusing on cleaning up younger objects more frequently.
- Optimization techniques include avoiding the creation of unnecessary objects, using symbols instead of strings when appropriate, and utilizing tools like
GC.start
judiciously to control when garbage collection occurs. -
Example:
# Force garbage collection (usually not recommended in production) GC.start # Tune the garbage collector (for advanced scenarios) GC::Profiler.enable
4. What is the difference between Marshal
and YAML
for object serialization in Ruby?
Answer:
-
Marshal:
- Binary serialization format.
- Faster and more compact than YAML.
- Limited portability (Ruby-specific, not human-readable).
- Example:
data = { name: "Ruby", version: 3.0 } marshaled_data = Marshal.dump(data) restored_data = Marshal.load(marshaled_data)
-
YAML:
- Text-based serialization format.
- Slower and more verbose, but human-readable and language-agnostic.
- More suitable for configuration files and data exchange between different programming languages.
- Example:
require 'yaml' data = { name: "Ruby", version: 3.0 } yaml_data = data.to_yaml restored_data = YAML.load(yaml_data)
5. How would you implement method chaining in Ruby?
Answer:
- Method chaining is a technique where multiple methods are called on an object sequentially in a single statement. Each method returns the object itself (or a modified version of it), allowing the next method to be called.
-
Example:
class Calculator def initialize(value = 0) @value = value end def add(n) @value += n self end def subtract(n) @value -= n self end def result @value end end calc = Calculator.new(10) result = calc.add(5).subtract(3).result # => 12
6. How would you handle large numbers and precision in Ruby?
Answer:
- Ruby’s
Integer
type automatically handles large numbers by switching fromFixnum
toBignum
. However, for floating-point numbers,BigDecimal
is preferred to maintain precision in financial calculations or when dealing with very large/small numbers. -
Example:
require 'bigdecimal' require 'bigdecimal/util' number = BigDecimal("1.234567890123456789") result = number + BigDecimal("0.000000000000000001") puts result.to_s('F') # => "1.234567890123456790"
7. Explain the difference between public
, private
, and protected
methods in Ruby.
Answer:
-
Public:
- Accessible from anywhere. Methods are public by default unless specified otherwise.
-
Private:
- Can only be called within the context of the current object. Cannot be called with an explicit receiver, even
self
.
- Can only be called within the context of the current object. Cannot be called with an explicit receiver, even
-
Protected:
- Can be called by any instance of the defining class or its subclasses. Unlike private methods, protected methods can be called with an explicit receiver if the receiver is of the same class or subclass.
-
Example:
class MyClass public def public_method "Public" end protected def protected_method "Protected" end private def private_method "Private" end end obj = MyClass.new obj.public_method # => "Public" # obj.protected_method # => NoMethodError # obj.private_method # => NoMethodError
8. How would you implement a custom enumerable in Ruby?
Answer:
- To create a custom enumerable, a class must include the
Enumerable
module and define aneach
method that yields items to a block. -
Example:
class MyCollection include Enumerable def initialize(items) @items = items end def each @items.each { |item| yield(item) } end end collection = MyCollection.new([1, 2, 3, 4]) collection.map { |x| x * 2 } # => [2, 4, 6, 8] collection.select(&:even?) # => [2, 4]
9. What are fibers in Ruby, and how do they differ from threads?
Answer:
-
Fibers:
- Fibers are a form of lightweight concurrency in Ruby, allowing you to pause and resume code execution manually. They provide finer control over the execution flow but are non-preemptive (the programmer must explicitly yield control).
-
Threads:
- Threads are system-level concurrency primitives. Ruby uses green threads or native threads (depending on the Ruby implementation). Threads can run in parallel (in JRuby or Rubinius) or time-slice (in MRI) but require careful handling of synchronization and resource sharing.
-
Example of a Fiber:
fiber = Fiber.new do puts "Hello" Fiber.yield puts "World" end fiber.resume # => "Hello" fiber.resume # => "World"
I hope that Helps!!
Check out more About me