Reputation: 1608
I'm struggling with one issue of the Hartl tutorial (where I've added my own custom needs, which is that a user belongs to a company, and the creation form is a company#new.) now everything is working, such as password reset etc. Except one thing which is the activation_token method.
Now I've tried to debug to get down to the exact line which is the problem and it is this one:
@user.activation_token
when I try printing that out just generally for a logged in user it's blank. Now the accurate error I get is:
No route matches {:action=>"edit", :controller=>"account_activations", :email=>"[email protected]", :id=>nil} missing required keys: [:id]
the request parameters are as follows:
"company"=>{"users_attributes"=>{"0"=>{"first_name"=>"demo", "last_name"=>"demo", "email"=>"[email protected]", "password"=>"demodemo"}}, "name"=>"demo"}
and the errors happen on:
<%= link_to "Activate", edit_account_activation_url(@user.activation_token, email: @user.email) %>
my User.rb:
class User < ActiveRecord::Base
attr_accessor :remember_token, :activation_token, :reset_token
before_create :create_activation_digest
...
class << self
# Returns the hash digest of the given string.
def digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
# Returns a random token
def new_token
SecureRandom.urlsafe_base64
end
end
private
# Creates and assigns the activation token and digest.
def create_activation_digest
self.activation_token = User.new_token
self.activation_digest = User.digest(activation_token)
end
now it works in obtaining the ID when I have it in my test and do the following:
def account_activation
user = User.first
user.activation_token = User.new_token
UserMailer.account_activation(user)
end
and my companycontroller#create looks like this:
def create
@company = Company.new(company_params)
if @company.save
user = @company.users.first
user.send_activation_email
flash[:info] = "registerd and email sent"
redirect_to root_url
else
flash.now[:danger] = 'issues with creating user.'
render 'new'
end
end
my company params:
def company_params
params.require(:company).permit(:name, users_attributes: [:id, :first_name, :last_name, :email, :password])
end
routes:
Rails.application.routes.draw do
get 'password_resets/new'
get 'password_resets/edit'
root 'welcome#index'
get 'company/users' => 'companies#users'
get 'login' => 'sessions#new'
post 'login' => 'sessions#create'
delete 'logout' => 'sessions#destroy'
resources :companies
resources :users
resources :account_activations, only: [:edit]
resources :password_resets, only: [:new, :create, :edit, :update]
end
note: the user does successfully save, as does the company. I'm also able to obtain general user info, when I uncomment .activation_token I can see that I'm able to get the @user.first_name etc in the email from my console.
on the picture you can see that the @user.activation_token is nil, but it finds the @user object.
Upvotes: 1
Views: 118
Reputation: 76774
The problem isn't your activation_token
method, as that seems to be created and called - according to your screenshot.
The problem is you're populating the edit_activations_path
with @user.activation_token
(which won't include id
), when that path requires id
.
You should change it to:
<%= link_to "Activate", edit_account_activation_url @user %>
According to your routes (which I've improved below), and the Rails standard practice of passing objects to route actions means that you should pass the entire object, and let Rails determine the specifics.
Your error states that it's id
that's not being passed - showing us that if you passed the @user
object, instead of @user.activation_token
, it will give Rails the ability to use it.
The only caveat here is that you may be calling activation_token
and email
in your Activations
controller. If this is the case, you need to rework your routes to accept the token and email respectively (below):
There are a number of improvements you could make to your code:
--
Your routes can be made much more efficient:
#config/routes.rb
root 'welcome#index'
resource :company, only: [] do
get :users, on: :collection
end
resources :companies, :users
resources :account_activations, only: [:edit]
resources :password_resets, only: [:new, :create, :edit, :update]
resources :sessions, only: [:new, :create, :destroy], path_names: { new: "login", create: "login", destroy: "logout" }
Now, the important thing to note here is the account_activations
resource is going to be the standard Rails
resource... IE will expect id
to formulate the route. Typical ID will be the primary_key of the object, you want to use the activation_token
. To fix this, you should use the following:
<%= link_to "Activate", edit_account_activation_url(id: @user.activation_token, email: @user.email) %>
This will still accept the params as :id
(required) and :email
, but will be populated with the data you need, allowing you to use:
#app/controllers/activations_controller.rb
class ActivationsController < ApplicationController
def edit
token = params[:id]
email = params[:email]
end
end
--
The model invokes User
twice. You should call the following:
def create_activation_digest
activation_token = class.new_token
activation_digest = class.digest(activation_token)
end
Upvotes: 1