-
-
Save RaZer/1376309 to your computer and use it in GitHub Desktop.
Ruby Questions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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