Skip to content

Instantly share code, notes, and snippets.

@delbetu
Last active February 18, 2020 14:25
Show Gist options
  • Save delbetu/6b819152df5aa1b2dfb9fd425c991039 to your computer and use it in GitHub Desktop.
Save delbetu/6b819152df5aa1b2dfb9fd425c991039 to your computer and use it in GitHub Desktop.
Pexels Qualification Test - Marcos Bellucci

Problem

Models

class Post < ApplicationRecord
    has_many :comments
end

class User < ApplicationRecord
  has_many :comments
end

class Comment < ApplicationRecord
  belongs_to :post
  belongs_to :user
end

Controllers

class PostsController < ApplicationController
  def show
    @post = Post.where("published = true").where("id = #{params[:id]}").first
    @post ||= Post.last
  end
end

View

<% @post.comments.each do |comment| %>
  <p> <b><%= comment.user.name %> wrote a comment:</b>
      <%= comment.text %>
  </p>
<% end %>

Solution

The intention of this code is to render a post and its comments. With this code in mind, please answer the following:

  1. Write a short paragraph describing what is happening in the show action of PostsController (consider both the controller action and the view).
  2. How would you improve the existing code? Please show what changes you would make and describe why.
  1. Controller is filling an instance variable, that instance variable will be available to the rendered erb.
    In order to fill up @post it makes a db query using active record api.
    select posts.* from posts where published = true and id = 1234
    That query will return a ActiveRecordQuery (ActiveRecord::Relation) object which will be resolved when .first is executed.
    If the post is not found it will get the last created post from the database.

The view will execute @post.comments
that will perform another query
select coments.* from comments where post_id = 1234
the result will be converted into an array and it will start iterating over that result.
For each comment it will perform comment.user.name
Each time this code executes a new query will be executed.
select users.* from users where comment_id = 9876

So in case the user have one post with 100 comments.
This is performing

  • 1 query for fetching the post
  • 1 query for fetching the comments
  • 100 queries for fetching the users
  1. This can be avoided by providing the view with a structure of related objects like this

Post -*> Comments --> User

You can perform only one query.

select *.user, *.posts, *.comments
from comments
inner join posts on comments.posts_id = posts.id
inner join users on comments.user_id = users.id
where posts.publisehd = true and posts.id = zzzz

The way to tell rails to cache all the relations is by using includes instead of joins.

  @comments = Comment.includes(:users).includes(:posts).where(posts: { id: zzzz, published: true }).to_a

(I haven't tried this code and probably it doesn't work but it is just a guide to the solution)

The view would change a little bit:

<% @comments.each do |comment| %>
  <p> <b><%= comment.user.name %> wrote a comment:</b>
      <%= comment.text %>
  </p>
<% end %>

The execution of comment.user.name won't perform any new query to the database because includes loaded the objects on memory.

(I haven't tried this code and probably it doesn't work but it is just a guide to the solution)  
83
require 'rspec/autorun'
def reverse_string(input)
string = input.to_s
reversed = ''
(string.length-1 ).downto(0) do |position|
reversed += string[position]
end
reversed
end
def assert_reversed(input, expected_result)
expect(reverse_string(input)).to eq(expected_result)
end
describe 'reverse_string' do
it 'returns empty when passing an empty string' do
expect(reverse_string('')).to be_empty
end
it 'returns empty when passing nil' do
expect(reverse_string(nil)).to be_empty
end
it 'returns same string when input is one char length' do
assert_reversed('a', 'a')
end
it { assert_reversed('ab', 'ba') }
it { assert_reversed('abc', 'cba') }
it { assert_reversed('abc defgh', 'hgfed cba') }
it 'can handl long strings' do
input_string = "It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like)."
assert_reversed(input_string, input_string.reverse)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment