Eager loading @current_user to save time on pundit policies
Tags: railspolicypundit, rails
Published: 2018-06-13 04:39:23

---
title: Eager loading @current_user to save time on pundit policies
published: true
description: Load up current user with everything you need so that you dont have to make a ton of queries in your rails policy 
tags: rails policy pundit, rails
cover_image: https://thepracticaldev.s3.amazonaws.com/i/ge4popxc4ic41blkzfem.jpg
---

## The Problem:
> If your rails controllers have the ability to run batch Create/read/update/delete, how can you quickly run policy checks without bogging down your server?

[Photo by Alberto Joel Filipe](https://unsplash.com/@joelfilip)

_This is article is not about installing pundit, if you are curious about set up , click [here](https://github.com/varvet/pundit)_


### The Context: 
- Our Controllers are Search/save/delete , if you do not pass search an `id` it will assume you are running a `GET ALL` and it will return a collection of database objects.
- we have Pundit Policies set up for each database model/controller.
- each policy extends a base policy as well.
- all of our controllers also extend a base api controller. 


Lets say we want to get a list of projects that our user is a part of: 

`frontend_api_request.js`

```javascript

//pass it an empty object for a standard get all request to the controller.
const ApiRequestSearch = async (data) => {
    const response = await fetch('https://yourapi.com/projects',data);
    const json = await response.json();
}

ApiRequestSearch({data:{});

```

When it gets to the backend it hits our base controller: 

`base_controller.rb`

```ruby
class Api::ApiController < ActionController::Base
    include Pundit
    before_action :set_current_user
    ...
end

```

`base_controller_helper.rb`
Our base controller uses a helper to:
- set current authenticated user [Like this](https://rails-bestpractices.com/posts/2010/08/23/fetch-current-user-in-models/):
```ruby
def set_current_user
  User.current = current_user    
end

```
- instantiate pundit policy (let me know if you know a better way)
```ruby
def yourPolicy
  "#{controller_name.classify}Policy".constantize
end

```


When our front end request is received, first it hits our base search method , where we pass the data , and the current user into our pundit policy.

`base_controller.rb`

```ruby
def search
...
    #If the policy returns false, we return a 403 to the front end.
    if ! self.yourPolicy.new(current_user, @collection).search?
        ApiErrors.raise_custom(message: 'You are not authorized to search!',     
        status: 403)
    end
...
end
```

now we hit the base application policy where we initialize all the stuff we will need for the to run our policy checks, also if we just want to run a generic search policy check, we can add it here

`base_policy.rb`
```ruby
class ApplicationPolicy
  attr_reader :user, :record, :collection

  def initialize(user, record)
    @user   = user
    @record = record
    @collection = record
  end

```


So if wanted to make our requests super laggy and inefficient , we might do something like this : (and yes, this was my first idea -.-)

`projects_policy.rb`

```ruby
class ProjectPolicy < ApplicationPolicy
# BAD ! DONT DO IT
    def search?
        @collection.map do |project|
            return true if project.user_id == @current_user.id 
            return false
        end
    end
end
```

## How can we do this better?

Instead lets use [eager loading](https://www.allerin.com/blog/eager-loading-inwith-rails) (ahead of time) to get all of our associations we need before hand, so we don't have to spend time checking each object in the array.

Lets go back to where set up our current user, and add a class variable for current user with the loaded associations.

`base_controller_helper.rb`


```ruby
def set_current_user
  User.current = current_user
  # eager load the associations we need 1
  # time, so we don't need to do it later
  @current_user = User.includes(:projects).find(current_user.id) if current_user
end
```

setting this will give us a user model with associations loaded up. 
so that when we hit our `base_policy.rb` we can use the associations and check against them, without having to make additional queries , or mapping through and running a check for each object.

Lets go back to our base policy and pass in our new `@current_user` : 

`base_controller.rb`

```ruby
def search
...
    #If the policy returns false, we return a 403 to the front end.
    if ! self.yourPolicy.new(@current_user, @collection).search?
        ApiErrors.raise_custom(message: 'You are not authorized to search!',     
        status: 403)
    end
...
end
```

Now inside of our policy we can do something like this : 

`projects_policy.rb`

```ruby
class ProjectPolicy < ApplicationPolicy
# GOOD ! DO IT
    def search?
        # get all IDS of the projects current user can access
        current_user_project_ids = @current_user.projects.pluck(:id)
        # get the ids of the collection that the user is searching
        collection_ids = @collection.pluck(:id)
        #compare both arrays, if empty , return true! 
        (collection_ids - current_user_project_ids).empty?
    end
end
```
So Array comparison , like this is much faster then making additional queries, or mapping through a list of array items right?

If you have any improvement suggestions , or i just made silly mistakes , please let me know below in the comments , im totally open for constructive criticism!

`thanks for reading !`