First argument in form cannot contain nil or be empty in rails 4.0

1.1k views Asked by At

There are tons of these, especially for Rails 4.0 and Rails 3.x. I am new to nested routing and I find it very difficult, especially with forms.

So I have the following:

Routes

  resources :users do
    resources :api_keys, path: '/developmentcenter'
  end

The relationship here is: user has many api keys where api key belongs to user.

Controller

class ApiKeysController < ApplicationController
  before_action :authenticate_user!

  def new
    @user = User.find(params[:user_id])
    @api_key = ApiKey.new(:user => @user)
  end

  def index
    @user = User.find(params[:user_id])
    @api_key = @user.api_keys
  end

  def create
    @user = User.find(params[:user_id])
    @api_key = ApiKey.new(create_new_api_key)
    create_api_key(@api_key, @user)
  end

  def destroy
    @user = User.find(params[:user_id])
    destroy_api_key(@user)
  end

  private

  def create_new_api_key
    params.permit(:api_key, user_attributes: [:id])
  end
end

The above is pretty basic. create_api_key is a method that does something on save, or does something else on failure to save. While destroy_api_key Just find the api key based on the user id and deletes it, does something on success and something on failure.

So now lets create a form - which has a single button for creating the API key.

<h2>Create a new Key</h2>
<%= form_for ([@user, @api_keys]) do |f| %>
  <p class="button"><%= f.submit "Generate API Key" %></p>
<% end %>

All we need is a single submit button that upon click, creates a new api key for the user whom is logged in.

But wait we have a an error:

First argument in form cannot contain nil or be empty

This error takes place at:

 <%= form_for ([@user, @api_keys]) do |f| %>

So - I have looked at every single one of the stack questions (well most) that deal with this error, I have changed my controller based on a few, to what you see above. I have even look at the form helpers docs in the rails manual.

I cannot figure this out.

1

There are 1 answers

0
Beartech On

It is telling you that @user is empty or nil in the context it is using. Either this is a user who has not been created in the DB yet, or your User.find call is not working to return an actual user. The form needs to know who @user is before it can create a nested resource (@api_key) for it. Your `create_api_key' is completely wrong. You need to whitelist your params first, then find the user in the DB (or create them), then you can use the @user instance variable to create a form for that user to create a key. I think if you do it right, you shouldn't need to call @api_keys in the beginning of the form if you defined the relationships in your models (has_many or has_one, belongs_to etc.). Can you post the web server console output when you visit that page? First off you are calling

 @user = User.find(params[:user_id])

every time in your controller. You should DRY that up with a before_action.

private
def set_user
  @user = User.find(api_key_params[:user_id])
end

Then at the top of the controller you would have:

class ApiKeysController < ApplicationController
  before_action :authenticate_user!
  before_action: :set_user

Also you should make your .permit a method that returns a variable called api_key_params:

def api_key_params
  params.require(:user).permit(:api_key)
end

That way you have the things you want returned in a whitelist. To access these params you just call the method. You can specify a param you want returned from the method like my set_user example. But it also gives you the ability to do things like:

def create
  @api_key = Api_key.new(api_key_params)
end

That way the Api_key.new gets the user and creates the key. You don't show how or where you generate the key itself. If it gets created by some method, you should put that in the model for Api_key. There are some other things about your code that are confusing without seeing the rest of your files.