Skip to content

Instantly share code, notes, and snippets.

@RaZer
Forked from skwp/ruby_test.rb
Created November 18, 2011 12:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RaZer/1376309 to your computer and use it in GitHub Desktop.
Save RaZer/1376309 to your computer and use it in GitHub Desktop.
Ruby Questions
# Instructions for this test:
# 1. Please clone this gist as a git repo locally
# 2. Create your own github repo called 'rubytest' (or a name of your choice) and add this repo as a new remote to the cloned repo
# 3. Edit this file to answer the questions, and push this file with answers back out to your own 'rubytest' repo.
# Problem 1. Explain briefly what this code does, fix any bugs, then clean it up however you
# like and write a unit test using RSpec.
def bracketed_list(values)
temp=""
temp += "["
values.each {|val| temp += "#{val.to_s}, "}
temp += "]"
return temp
end
# Method below returns passed variable arguments converted to string as a bracketed string
# Issues with original code:
# - it accept only 1 variable
# - result ver have excess coma and space before closing bracket
def bracketed_list(*values)
'[' + values.map{|v| v.to_s}.join(', ') + ']'
end
# Spec
describe "#bracketed_list" do
it "returns bracketed string of all passed variables converted to string" do
args = [1, 2, [3, 4], 5]
list = bracketed_list(*args)
parsed_list = list[1..-2].split(', ')
parsed_list.size.should eq(args.size)
parsed_list.each_with_index do |item, i|
item.should eq(args[i].to_s)
end
end
end
# Problem 2. This is a piece of code found in a fictional Rails controller and model.
#
# Point out any bugs or security problems in the code, fix them, and refactor the code to
# make it cleaner. Hint: think 'fat model, skinny controller'. Explain in a few sentences
# what 'fat model, skinny controller' means.
class CarsController
def break_random_wheel
Car.find_by_name_and_user_id(params[:name], params[:user_id]).break_random_wheel!
end
end
class Car < ActiveRecord::Base
has_many :components
def break_random_wheel!
components.wheels.random.break!
self.functioning_wheels -= 1
end
end
class Component < ActiveRecord::Base
belongs_to :car
scope :wheels, where(:kind => 'wheel')
def random
offset(rand(self.count)).first
end
end
class User < ActiveRecord::Base
end
# fat model, skinny controller is an approach when as many logic as possible is incapsulated in models,
# and controlerls are mostly used to call model's methods
# Problem 3. You are running a Rails application with 2 workers (imagine a 2-mongrel cluster or a Passenger with 2 passenger workers).
# You have code that looks like this
class CarsController
def start_engine
@car = Car.first # bonus: there is a bug here. what is it?
#Bug: method Car.first is not defined
@car.start_engine
end
end
class Car
def start_engine
api_url = "http://my.cars.com/start_engine?id={self.id}"
RestClient.post api_url
end
end
# 3a. Explain what possible problems could arise when a user hits this code.
# Request to API always need some additional time to be processed.
# Since 1 Mongrel is able to handle only 1 request at the moment, we will get a growing pool of request.
# Response time for users who are trying to browse site at the same rime will grow accordingly.
# Incuding bad user experience because of long waiting time may have a situation when users will be dropped because of timeout (DoS).
# In this case we don't have any data transmitted to user from API. So we can process such requests asynchronously or in background
# 3b. Imagine now that we have changed the implementation:
class CarsController
def start_engine
sleep(30)
end
def drive_away
sleep(10)
end
def status
sleep(5)
render :text => "All good!"
end
end
# Continued...Now you are running your 2-worker app server in production.
#
# Let's say 5 users (call them x,y,z1,z2,z3), hit the following actions in order, one right after the other.
# x: goes to start_engine
# y: goes to drive_away
# z1: goes to status
# z2: goes to status
# z3: goes to status
#
# Explain approximately how long it will take for each user to get a response back from the server.
#
# Example: user 'x' will take about 30 seconds. What about y,z1,z2,z3?
# User 'y' will take about 10 seconds
# User 'z1' will take about 15 seconds
# User 'z2' will take about 20 seconds
# User 'z3' will take about 25 seconds
# Approximately how many requests/second can your cluster process for the action 'start_engine'? What about 'drive_away'?
# What could you do to increase the throughput (requests/second)?
# For 'start_engine': 1/15 request/second
# For 'drive_away': 1/5 request/second
# To increase thhougput we can increase number of mongrels/workers, switching to
# Thin also would help. But basicaly we need to have such time consuming stuff
# processed asynchronously in background.
# Problem 4. Here's a piece of code to feed my pets. Please clean it up as you see fit.
[Cat.new, Dog.new(:meat), Cow.new].each do |pet|
pet.feed
end
class Pet
attr_accessor :food
def initialize(food = nil)
@food = (food.nil?) ? self.class::FAVOURITE_FOOD : food
end
def feed
puts "Thanks for #{@food}!"
end
end
class Cat < Pet
FAVOURITE_FOOD = :milk
end
class Dog < Pet
FAVOURITE_FOOD = :dogfood
end
class Cow < Pet
FAVOURITE_FOOD = :grass
end
# Problem 5. Improve this code
class ArticlesController < ApplicationController
def index
@articles = Article.published.by_date
end
end
class Article < ActiveRecord::Base
scope :published, where(:state => STATES[:published])
scope :by_date, order('created_at DESC')
end
# Problem 6. Explain in a few sentences the difference between a ruby Class and Module and when it's appropriate to use either one.
# Unlike classed modules can't have instance.
# Modules are useful to group related stuff in single location (namespaces) or to emulate multiple inheritance (mixins)
# Problem 7. Explain the problem with this code
class UsersController
def find_active_users
User.find(:all).select {|user| user.active?}
end
end
# The problem is in that we select all users at first and only then filter active.
# With huge amounts of data we may have serious memory and performance issues. Database can handle this much more efficiently than ruby.
# Correct example User.where(:status => 'active')
# Problem 8. Explain what's wrong with this code and fix it. (Hint: named_scope)
# There is no need in using anonymous scope which is returned by method scoped in this case.
# It's better to create parametrized scope or method in Car model which will find cars by color.
class User < ActiveRecord::Base
has_many :cars
def red_cars
cars.painted_in :red
end
def green_cars
cars.painted_in :green
end
end
class Car < ActiveRecord::Base
belongs_to :user
scope :painted_in, lambda {|color| where(:color => color)}
end
# Problem 9. Here's a piece of code that does several actions. You can see that it has duplicated
# error handling, logging, and timeout handling. Design a block helper method that will remove
# the duplication, and refactor the code to use the block helper.
def do_something_in(timeout, action, &block)
raise ArgumentError, 'Block should be given' unless block_given?
logger.info "About to do #{action}"
Timeout::timeout(timeout) do
begin
yield
rescue => e
logger.error "Got error: #{e.message}"
end
end
end
do_something_in 5, 'action1' do
action1
end
do_something_in 10, 'action2' do
action2
end
do_something_in 3, 'action3' do
action2
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment