Обложка: Примеры задач с собеседования на позицию Middle Ruby разработчика

Примеры задач с собеседования на позицию Middle Ruby разработчика

Андрей Молчанов
Андрей Молчанов

Senior Software Engineer в Uscreen

Позиции Junior, Middle и Senior в СНГ различаются не только от компаний к компаниям, но и в принципе отсутствуют в некоторых странах. Для проведения интервью у кого-то есть заранее подготовленный набор тестовых заданий, кто-то предпочитает беседу за жизнь вперемешку с конкретными техническими вопросами. Есть компании, которым важны преимущественно soft skills. И все-таки выявить подходящего кандидата — дорогостоящий процесс.

После прохождения интервью во множество компаний есть понимание, что кандидат должен знать и уметь применять основные принципы, на которых строятся современные приложения. И хотя в моей практике не было такого, чтобы интервью закончилось после первого неправильного ответа или вопроса, — есть основы, в которых надо разобраться заранее.

В этом материале создана подборка по основам Ruby, часто используемым функциям языка. И если Junior разработчик никак не привязан к языку программирования, а Senior может писать более сложный код, то если вы сможете решить эти задачи без подсказок — бо́льшая часть практической подготовки на позицию Middle пройдена и можно сфокусироваться на других темах при подготовке к собеседованию.

Реализуйте метод Array#map

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

Проверка использования: [1,2,3,4].map { |x| x * 10 }

Реализуйте метод attr_accessor

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

Проверка использования:

class Car
  include AttrAccessor
  custom_attr_accessor :wheels_count, :driver_name
end

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

car.driver_name = 'Kevin'
p car.driver_name

Перепишите код без использования if

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

  def to_s
    "#{@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

Проверка использования:

text = Text.new('New text')
puts text.formatted(:json)
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)
    "#{text}"
  end
end

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

class XmlFormatter < BaseFormatter
end

Проверка использования:

text = Text.new('Text', JsonFormatter.new)
puts text.formatted

Напишите код, чтобы строка (1..10).select(&3) возвращала результат [3, 6, 9]

class Fixnum
  def to_proc
    Proc.new { |item| item % self == 0 }
  end
end

Добавьте функциональность объекта post, изменяющую значение любого параметра объекта на требуемое и логирующую это изменение

Класс Post:

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
require 'logger'

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)

Проверка использования:

post = Post.new('Title', 'Body')
post.publish!
p post.state
post.publish!
p post.state
post.force_state(:draft)
p post.state

Хинт для программистов: если зарегистрируетесь на соревнования Huawei Cup, то бесплатно получите доступ к онлайн-школе для участников. Можно прокачаться по разным навыкам и выиграть призы в самом соревновании.

Перейти к регистрации

Что думаете?