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
ifstatement.if 2 + 2 == 4 puts "I'm really smart" endThis 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 == 4Ternary Operators
Let's look at another example. Let's say we have a
is_adult?method inside of aPersonmodel that has anageattribute.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 falseWe 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 falseWoah, 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_thisPretty cool right? :)