11 Ruby on Rails Interview Questions and Answers

Published May 18, 2016Last updated Aug 31, 2017
11 Ruby on Rails Interview Questions and Answers
CodementorX has the experts you need. Check out our top Ruby on Rails developers!

Have an upcoming Ruby on Rails interview for a junior web developer position?

Experienced developers at Codementor have gathered together to collaborate on this list of Ruby on Rails interview questions to help you prepare for your Ruby on Rails technical interview. Can you answer them all?

Ruby on Rails Interview Question #1

The product team has a great new feature they want to add to your Ruby on Rails application: they want every model in the system to be able to retain special user notes. You realize that there will be a collection of forms and model code that will be duplicated in the dozen ActiveRecord models already in your application.

What are some strategies you can employ for reducing duplication and bloated Active Record models? What are the pros/cons of each strategy?
(Question provided by Jon Lebensold)

Because Ruby on Rails is an MVC framework, it can become tempting to try and fit everything into the Model or the Controller. Ruby on Rails is a powerful framework that provides many different mechanisms for describing our application and keeping our models and controllers nice and tidy.

Below are two ways of reducing fat models. They illustrate different levels of shared understanding between the extracted functionality and the model in question.

1. Use ActiveSupport::Concern

If the code really belongs in the model (because it relies on ActiveRecord helpers), but there is a coherent grouping of methods, a concern might be worth implementing. For example, many models in a system could enable a user to create a note on a number of models:

require 'active_support/concern'

module Concerns::Noteable
  extend ActiveSupport::Concern

  included do
    has_many :notes, as: :noteable, dependent: :destroy
  end

  def has_simple_notes?
    notes.not_reminders_or_todos.any?
  end

  def has_to_do_notes?
    notes.to_dos.any?
  end

  def has_reminder_notes?
    notes.reminders.any?
  end
  ...
end

The Concern can then be applied like so:

class Language < ActiveRecord::Base
  include TryFind
  include Concerns::Noteable
end

Pros:
This is a great way of testing a cohesive piece of functionality and making it clear to other developers that these methods belong together. Unit tests can also operate on a test double or a stub, which will keep functionality as decoupled from the remaining model implementation as possible.

Cons:
ActiveSupport::Concerns can be a bit controversial. When they are over-used, the model becomes peppered in multiple files and it’s possible for multiple concerns to have clashing implementations. A concern is still fundamentally coupled to Rails.

See also:
https://signalvnoise.com/posts/3372-put-chubby-models-on-a-diet-with-concerns
http://api.rubyonrails.org/classes/ActiveSupport/Concern.html
http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/

2. Delegate

Depending on the source of the bloat, sometimes it makes better sense to delegate to a service class. 10 lines of validation code can be wrapped up in a custom validator and tucked away in app/validators. Transformation of form parameters can be placed in a custom form under app/forms. If you have custom business logic, it may be prudent to keep it in a lib/ folder until it’s well defined.

The beauty of delegation is that the service classes will have no knowledge of the business domain and can be safely refactored and tested without any knowledge of the models.

Pros:
This approach is elegant and builds a custom library on top of what Ruby on Rails provides out of the box.

Cons:
If the underlying APIs change, your code will likely need to be updated to match. Instead of coupling to your model layer, you’ve now coupled yourself to either Ruby on Rails or a third-party library.

See also:
http://guides.rubyonrails.org/active_record_validations.html#custom-validators
https://github.com/apotonick/reform
https://robots.thoughtbot.com/activemodel-form-objects
https://github.com/timcraft/formeze

Conclusion :
This question helps demonstrate two critical skills every Ruby developer needs to develop: how handle complexity from emerging requirements and how to decide the most appropriate refactoring.

By working through different refactoring strategies, I can explore a candidate’s problem solving skills and their overall familiarity with Ruby on Rails and their knowledge of MVC. It’s important to know what is code that is specific to the application and what can be generalized into a completely decoupled piece of functionality.


Author Bio

Having spent over a decade tapping away at a terminal, Jon has worked on large systems for Open Source projects, Fortune 500s, and non-profit organizations. Hire Jon Now.


Ruby on Rails Interview Question #2

Given an array [1,2,34,5,6,7,8,9], sum it up using a method:
(Question provided by Codementor Nicholas Ng)

def sum(array)
  return array.inject(:+)
end

Summation of an array is one of the most fundamental concepts in programming, and there are a lot of methods to achieve it, such as iterating the array and summing the numbers. In Ruby, it’s neat to know there is method that called inject, because it’s so powerful yet simple.

Ruby on Rails Interview Question #3

What is metaprogramming?
(Question provided by Codementor Nicholas Ng)

Ruby developers should know what’s metaprogramming, because it is widely used, especially in popular frameworks such as Rails, Sinatra, and Padrino. By using metaprogramming, we can reduce duplicate code, but there is a downside where it will increase the complexity of the code in the long run.

Here’s an example of metaprogramming in Ruby:

A user can have a lot of roles, and you want to check the authorization.

Normal scenario:

def admin?
     role ==  'admin'
end

def marketer?
    role == 'marketer'
end

def sales?
   role == 'sales'
end

Metaprogramming:

['admin', 'marketer', 'sales'].each do |user_role|
    define_method "#{user_role}?" do
        role == user_role
    end
end

Ruby on Rails Interview Question #4

Given this Human class implementation
(Question provided by Codementor Nicholas Ng)

class Human

    def talk
        puts "I’m talking"
    end

     private

     def whisper
          puts "I’m whispering"
     end
end

What’s the output of :

  1. Human.new.talk

  2. Human.new.whisper

  3. Human.new.send(:talk)

  4. Human.new.send(:whisper)

  5. I’m talking

  6. NoMethodError: private method ‘whisper’ called for #<Human:0x007fd97b292d48>

  7. I’m talking

  8. I’m whispering

To explain, the class object Human.new.talk is calling an instance method, so it works perfectly. The talk method is a public method and can be called by everyone.

The class object Human.new.whisper is trying to access a private method, which it is not allowed to. Private and Protected methods are not accessible from outside. They are only used internally. This is an object-oriented design and can be used to structure the code, which the implementation is not supposed to expose to consumer object.

Finally, Human.new.send(:talk) sends a bypass control check to the method so it can be called without raising an error. Same goes to Human.new.send(:whisper).


Author Bio

Nicholas Ng is the technical Lead in PropSocial and the Founder of Virtual Spirit, a Ruby on Rails tech firm that provides web & mobile development. Hire Nicholas Now.


Ruby on Rails Interview Question #5

Write code that splits a given array of integers into two arrays; the first containing odd numbers and second containing even numbers
(Question provided by anonymous Codementor)

%i|select reject|.map { |m| array.partition(&odd?)}

or

array.each_with_object({odd:[], even:[]}) do |elem, memo|
  memo[elem.odd? ? :odd : :even] << elem
end # inject for ruby < 2.0 is fine as well

The straightforward approach would be to call array.select to store odds and then array.reject to store evens. There is nothing wrong with this, except it violates DRY principle:

odds = array.select &:odd?
evens = array.reject &:odd?

Future modification of this code might accidentally change the only one line from the above pair, breaking consistency. It is not likely the case for this particular example, but DRY usually works for future use.

One might notice that the latter example does one iteration through an array, while the former is still iterating the array twice, once for each method. In most cases, the performance penalty is not significant, but it should be taken into consideration when dealing with huge arrays.


hire-ruby-on-rails-developers.png

Find top Ruby on Rails developers today.

CodementorX will help you find you the best engineers for your project.


Ruby on Rails Interview Question #6

How would you flatten
(Question provided by anonymous Codementor)

hash = { a: { b: { c: 42, d: 'foo' }, d: 'bar' }, e: 'baz' }

to

{ :a_b_c=>42, :a_b_d=>"foo", :a_d=>"bar", :e=>"baz" }
λ = ->(h, key = nil) do
  h.map do |k, v|
    _k = key ? "#{key}_#{k}" : k.to_s
    v.is_a?(Hash) ? λ.call(v, _k) : [_k.to_sym, v]
  end.flatten #⇒ note flatten 
end
Hash[*λ.call(hash)]
#⇒ {:a_b_c=>42, :a_b_d=>"foo", :a_d=>"bar", :e=>"baz"}

Understanding recursion is important. While all these Fibonacci numbers and factorials are repeated over and over again, the real world tasks are less academic. Walking through the hash in the described manner is often a must.

The exercise might be stated as the exact opposite: given the “flattened” form of the hash, rebuild its nested representation.

Ruby on Rails Interview Question #7

Given the following syntactic sugar:
(Question provided by anonymous Codementor)

(1..42).reduce &:*
#⇒ 1405006117752879898543142606244511569936384000000000

What makes this notation to be an equivalent of

(1..42).reduce { |memo, elem| memo * elem }

Does the Ruby parser handle this particular case, or could this be implemented in plain Ruby?

The candidate can monkeypatch to the Symbol class with their own implementation of the aforementioned syntactic sugar.

E.g.

class Symbol
def to_proc
  # this implementation in incomplete
  # — more sophisticated question: why?
  # — even more hard: re-implement it properly
lambda { |memo, recv| memo.public_send(self, recv)  }
end
end
(1..42).reduce &:*
#⇒ 1405006117752879898543142606244511569936384000000000

There is not much magic in this example. An ampersand converts an argument, that is apparently a Symbol instance here, to Proc object, simply calling to_proc method on it. The implementation is Symbol#to_proc in done in C for performance reasons, but the candidate can write their own implementation in Ruby to make sure everything works as expected.

Answers to additional questions in the code: — the code above fails when the callee expects to receive one parameter only

(e.g. a try to use this implementation with Enumerator#each)

will fail with an arity error:

(1..42).each &:*
ArgumentError: wrong number of arguments (given 1, expected 2)

To fix this, the candidate should use the splat argument to lambda and analyze the amount of actual argument passed:

lambda do |*args|
  case args.size
  when 1 then # each-like
when 2 then # with accumulator
...

Ruby on Rails Interview Question #8

What’s wrong with the code below and why?
(Question provided by anonymous Codementor)

require 'benchmark'
puts Benchmark.measure do  
    break if Random.rand(100) === 1 while true
end

Operator precedence matters! The code will return

LocalJumpError: no block given (yield)

As do-end is weaker than attracting arguments to the function, that’s why one either needs to surround the whole call to Benchmark.measure with parentheses, or to use curly brackets instead of do-end.

Ruby on Rails Interview Question #9

Provided we have a hash of a fixed structure (e. g. we receive this hash from a third-party data provider, that guarantees the structure):
(Question provided by anonymous Codementor)

input = [	{a1: 42, b1: { c1: 'foo' }}, 
{a2: 43, b2: { c2: 'bar' }}, 
{a3: 44, b3: { c3: 'baz' }},
… ]

How can one build an array of cN values (['foo', bar', baz'])?

Some examples would be

input.map { |v| v.to_a.last.last.to_a.last.last }

or

input.map { |v| v.flatten.last.flatten.last }

Here one iterates through the array, collecting nested hashes and using an index to build the requested key name, but there is more straightforward approach. Hash is an Enumerable, which gives the developer an ability to query it almost as what has been done with an array.

Ruby on Rails Interview Question #10

Given the code below, how one might access the @foo variable from outside? Is it an instance variable, or class variable? What object this variable is defined on?
(Question provided by anonymous Codementor)

class Foo
  class << self
    @foo = 42
  end
end

You can access the variable with

(class << Foo ; @foo ; end)

It’s an instance variable and defined on Foo’s Singleton method, or more specifically, the eigenclass of Foo.

Each class in Ruby has its own “eigenclass.” This eigenclass is derived from Classclass. Foo class is an instance of it’s eigenclass. This eigenclass has the only instance Foo; it as well has instance methods, defined as class methods on Foo, and it might have instance variables, e. g. @foo variable in this particular example.

Ruby on Rails Interview Question #11

What are the different uses of Ruby modules? Could you provide an example of each and explain why it is valuable?
(Question provided by anonymous Codementor)

  1. Traits/Mixins:
    Examples: Mappable, Renderable, Movable
    Traits/Mixins is a useful alternative to class inheritance when there is a need to acquire behavior that describes a trait (e.g. Renderable) instead of an is-a relationship (e.g. Vehicle), especially when there is a need for multiple traits since a class can only inherit once.

  2. Namespace:
    Examples: Graphics, Devise, Finance
    Namespace Ruby classes and modules to avoid naming clashes with similarly-named classes/modules from libraries

  3. Singleton class alternative:
    Examples: Application, Universe, Game
    Modules cannot be instantiated, therefore they can be used as an easy alternative to singleton classes to represent only one instance of a domain model via module methods (equivalent of class methods)

  4. Bag of stateless helper methods:
    Examples: Helpers, Math, Physics
    Stateless helper methods receive their data via arguments without needing a class to be instantiated nor keep any state (e.g. calculate_interest(amount, rate_per_year, period)), so a module is used instead for holding a bag of stateless helper methods.

In addition to knowing the 4 different functions of modules in Ruby cited above, it’s important to know when to use a module v.s. a superclass when doing object-oriented domain modeling since that can greatly impact maintenance of the code a few months down the road in a software project.

For a further practical example, a car-race-betting game allows players to bet on cars rendered on the screen as moving at different speeds. When the game is over, players can print a sheet of game results representing each car’s details and status to claim their prize at a casino.

If the candidate were to implement all of these details in a car object, and later introduce differences between a Jaguar, Mercedez, and Porche, the candidate would rely on a car superclass shared among three subclasses via inheritance. However, if in the future, trucks and horses are thrown into the mix as well, the candidate would have to split the ability of an object to move, the ability to render object on screen, and the ability to print object details into separate modules (Movable, Renderable, and Printable respectively), and mix them into each of Car, Truck, and Horse. Next, the three classes can each serve as a superclass for multiple subtypes as needed by the game (e.g. JaguarCar, FireTruck, ThoroughbredHorse). This will offer maximum separation of concerns and improved maintainability of the code.

Conclusion

Hope these 11 Ruby on Rails interview questions help you find and vet a Ruby developer's technical skill. What's more important is to also review a candidate's past projects, how the candidate goes about solving issues previously faced, as well as other soft skills. By no means is this an exhaustive list of interview questions, but these are some ones to help you get started.

Let us know below if you know any other Ruby on Rails interview questions!

Hire a top Ruby on Rails developer for your team.