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 aPerson
model that has anage
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? :)