Ruby interview questions
Tags: ruby, interview, middle, career
Published: 2022-05-23 20:48:18

In this post, I've created a collection of Ruby basics for a **Middle-level developer**.

In common cases, interviewers have a pre-prepared set of tasks. While others prefer a conversation about experience mixed with technical questions. On the other hand, some companies care most about soft skills. But all of these understand one thing correctly. An interview is a costly process. All companies want to decrease costs.

After interviewing for many companies, there is an understanding that the candidate should know and be able to apply the basic principles on which modern applications are built. Although in my practice there has been no such thing as an interview ending after the first wrong answer, there are basics that need to be understood ahead of time.

A Junior developer is not tied to the programming language in any way, but a Senior can write more complex code. Then if you can solve these problems without any hints, most of the practical preparation for the Middle position is passed and you can focus on other topics during preparing for an interview.

### 1. Implement the method Array#map

{% details A possible answer %}
```ruby
class Array
  def map
    return unless block_given?

    result = []
    i = 0

    while self[i] do
      result << yield(self[i])
      i += 1
    end

    result
  end
end
```
Checking the usage
```ruby
[1,2,3,4].map { |x| x * 10 }
```
{% enddetails %}

#### 2. Implement the method attr_accessor

{% details A possible answer %}
```ruby
module AttrAccessor
  def self.included(klass)
    klass.extend(ClassMethods)
  end

  module ClassMethods
    def custom_attr_accessor(*args)
      args.each do |arg|
        define_method(arg) do
          self.instance_variable_get("@#{arg}")
        end

        define_method("#{arg}=") do |value|
          self.instance_variable_set("@#{arg}", value)
        end
      end
    end
  end
end
```

Checking the usage
```ruby
class Car
  include AttrAccessor
  custom_attr_accessor :wheels_count, :driver_name
end

car = Car.new
car.wheels_count = 4
car.wheels_count

car.driver_name = 'Kevin'
car.driver_name
```
{% enddetails %}

#### 3. Refactor the code by removing `if`s
```ruby
class HtmlFormatter
  def initialize(body)
    @body = body
  end

  def to_s
    "<body>#{@body}</body>"
  end
end

class JsonFormatter
  def initialize(body)
    @body = body
  end

  def to_s
    "{ body: #{@body} }"
  end
end

class Text
  def initialize(body)
    @body = body
  end

  def formatted(type=nil)
    if type == :json
      JsonFormatter.new(@body)
    elsif type == :html
      HtmlFormatter.new(@body)
    else
      raise 'an unknown format type'
    end
  end
end
```
{% details A possible answer %}
```ruby
class Text
  def initialize(body, formatter)
    @body = body
    @formatter = formatter
  end

  def formatted
    @formatter.render(@body)
  end
end

class BaseFormatter
  def render(text)
    raise 'Not implemented :render method'
  end
end

class HtmlFormatter < BaseFormatter
  def render(text)
    "<body>#{text}</body>"
  end
end

class JsonFormatter < BaseFormatter
  def render(text)
    "{ body: #{text} }"
  end
end

class XmlFormatter < BaseFormatter
end
```

Checking the usage
```ruby
text = Text.new('Text', JsonFormatter.new)
puts text.formatted
```
{% enddetails %}

#### 4. Write a code which help a command `(1..10).select(&3)` return `[3, 6, 9]`

{% details A possible answer %}
```ruby
class Fixnum
  def to_proc
    Proc.new { |item| item % self == 0 }
  end
end
```
{% enddetails %}

#### 5. There is a code
```ruby
class Post
  attr_reader :state, :title, :body

  def initialize(title, body)
    @title = title
    @body = body
    @state = :draft
  end

  def publish!
    @state = :published if @state == :draft
  end

  def delete!
    @state = :deleted if @state == :published
  end
end
```
Extend the functionality of the class Post which allows changing any attribute of an instance of Post.
For example, `Post.new.force_title('An awesome article')` will be correct.

{% details A possible answer %}
```ruby
module Forceable
  def method_missing(method, *args, &block)
    if method =~ /force_(\w+)/ && previous_value = self.instance_variable_get("@#{$1}")
      self.instance_variable_set("@#{$1}", args.first)

      puts "Post changes #{$1} value from #{previous_value} to #{args.first}"
    else
      super
    end
  end
end

Post.include(Forceable)
```

Checking the usage
```ruby
post = Post.new('Title', 'Body')
post.force_state(:draft)
p post.state
```
{% enddetails %}