6. Introduction to Rails Callbacks

In the previous lesson, we created the backend logic for updating and deleting posts. The problem that we had is that although we hid the link to update and delete the post on the front end, users could potentially type in their browser a URL like http://localhost:3000/posts/1/edit and access the edit page for a post that wasn't created by themselves.

In order to prevent this, we're going to use something called filters. Take 10 minutes to read this article on filters, as it explains the topic really well.

Let's go ahead and implement a before_action filter in our app. What we want to do is to check if the post is owned by the current_user, and if it isn't, we want to redirect them to the root_path so they can't access the edit page.

In posts_controller.rb below our before_action :authenticate_user!, let's add the following line of code:

before_action :is_owner?

let's add the following code:


private

def is_owner?
  if Post.find(params[:id]).user != current_user
    redirect_to root_path
  end
end

Let's first go over what we just did, and then let's go over what private means again.

We first set a before_action filter in our posts_controller.rb. Using before_action will trigger the method we specified will be run (in this case the is_owner? method) before any of the actions (methods) are triggered.

The is_owner? method just checks if the post's user and the current_user do not match, and if so, it redirects the user to the root_path.

The private keyword is used to set private methods. In short, private methods are methods that can only be accessed by the same class.

The filter we just added is pretty cool, but we don't want to apply this for the index, new, or create actions. Let's specify this:

before_action :is_owner?, only: [:edit, :update, :destroy]

Now if we try to visit an edit page that we aren't supposed to be able to visit, then we will be redirected automatically to the root page.

Awesome. Now that the code is working, we can work on refactoring the code. Let's take a look at the is_owner? method once again.

def is_owner?
  if Post.find(params[:id]).user != current_user
    redirect_to root_path
  end
end

Here we have an if statement. We can actually reduce this to a one-liner:

def is_owner?
  redirect_to root_path if Post.find(params[:id]).user != current_user
end

One-liner if statements

Many Rails developers tend to like to write one line statements if possible. There are several ways to do this. Let's take a look at an if statement.

if 2 + 2 == 4
  puts "I'm really smart"
end

This is great, but we're taking 3 lines to accomplish very little. We can reduce this to one line, like this:

puts "I'm really smart" if 2 + 2 == 4

Ternary Operators

Let's look at another example. Let's say we have a is_adult? method inside of a Person model that has an age attribute.

class Person
  attr_accessor :age

  def initialize(age)
    @age = age
  end

  def is_adult?
    if age >= 18
      true
    else
      false
    end
  end
end

person = Person.new(20)
person.is_adult?
# => returns true

child = Person.new(10)
child.is_adult?
# => returns false

We can refactor the is_adult? method like this:

class Person
  attr_accessor :age

  def initialize(age)
    @age = age
  end

  def is_adult?
    age >= 18 ? true : false
  end
end

person = Person.new(20)
person.is_adult?
# => returns true

child = Person.new(10)
child.is_adult?
# => returns false

Woah, this is totally cleaner code! But you are also probably confused because of the new syntax. This syntax is called is the ternary operator. It makes writing one-line if statements really easy. This is how it works:

if_this_is_a_true_value ? then_the_result_is_this : else_it_is_this

Pretty cool right? :)

Lesson list