Confirm Rails Devise user with checkbox - ruby-on-rails

While administering users, I'd like the administrator to be able to confirm a user's account using a checkbox to trigger the user's devise user.confirm method.
I initially thought I'd be able to use attr_accessor to setup a variable called confirm_now to be used as a boolean on the user form, then in the controller update action (or by before/after action callback) evaluate the boolean and confirm the user accordingly.
I'm still not quite sure if I need methods in the model to set and read the attr_accessor variable, I'm still getting my head around that...or perhaps I'm over-complicating this and should call the user.confirm method when clicking save (is this bad practice? - I'm hesitant to add this field into the user_params).
Model:
class User < ApplicationRecord
attr_accessor :confirm_now
...
Controller:
class UsersController < ApplicationController
...
def update
if params[:user][:password].blank?
params[:user].delete(:password)
params[:user].delete(:password_confirmation)
end
if !#user.confirmed? && #user.confirm_now
#user.confirm
end
...
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :account_active, :confirm_now)
end
Form snippet (this evaluates and works as expected):
<% if !#user.confirmed? %>
<%= f.input :confirm_now, label: 'Confirm User', as: :boolean, checked_value: true, unchecked_value: false %>
<% end %>
Rails Log (the attr_accessor is set when checked on the form):
Processing by UsersController#update as HTML
Parameters: {"authenticity_token"=>"...", "user"=>{"first_name"=>"gg", "last_name"=>"gg", "email"=>"gg#gg.com", "account_active"=>"1", "confirm_now"=>"true", "password"=>"[FILTERED]"}, "commit"=>"Save", "id"=>"45"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."first_name" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."first_name" ASC LIMIT $2 [["id", 45], ["LIMIT", 1]]
CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."first_name" ASC LIMIT $2 [["id", 45], ["LIMIT", 1]]
Redirected to http://localhost:3000/users
Completed 302 Found in 6ms (ActiveRecord: 0.6ms | Allocations: 2665)
Thanks in advance!

To simplify I would make it a button, and pass the confirmed_at field directly.
Parameter whitelisting:
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :account_active, :confirmed_at)
end
Form:
<%=
if #user.confirmed?
link_to 'Unconfirm user',
user_path(#user, { user: { confirmed_at: nil } }),
method: :patch
else
link_to 'Confirm user',
user_path(#user, { user: { confirmed_at: Time.current } }),
method: :patch
end
%>
Please adjust the paths/method types according to your routes.

Related

Why is rails returning a 401 Unauthorized status and why does it say that the user exists?

I'm in the process of writing a single page app with rails, react, and redux; and this is what the full error message looks like:
Started POST "/api/users" for ::1 at 2020-01-27 21:48:27 -0800
Processing by Api::UsersController#create as JSON
Parameters: {"user"=>{"username"=>"racookin", "password"=>"[FILTERED]", "nickname"=>"racookin", "gender"=>"female", "birthday"=>"1990-07-07"}}
(0.2ms) BEGIN
↳ app/controllers/api/users_controller.rb:6
User Exists (0.4ms) SELECT 1 AS one FROM "users" WHERE "users"."username" = $1 LIMIT $2 [["username", "racookin"], ["LIMIT", 1]]
↳ app/controllers/api/users_controller.rb:6
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE "users"."email" IS NULL LIMIT $1 [["LIMIT", 1]]
↳ app/controllers/api/users_controller.rb:6
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE "users"."session_token" = $1 LIMIT $2 [["session_token", "9B3Uo1FGMnJDeASGGucj_Q"], ["LIMIT", 1]]
↳ app/controllers/api/users_controller.rb:6
(0.2ms) ROLLBACK
↳ app/controllers/api/users_controller.rb:6
Completed 401 Unauthorized in 274ms (Views: 0.2ms | ActiveRecord: 10.6ms)
I'm trying to understand what this is saying so that I can figure out where to even start looking for the bug; it seems that it returns the 401 Unauthorized error, and it's saying that the user exists (even when I try signing up with a new user). What exactly is happening here? Also, is it hitting the database 3 times?
Update: I think I understand that it's failing because the user isn't being saved, which means it's probably failing one of the validations.
Here's what I have on my User model:
class User < ApplicationRecord
attr_reader :password
validates :username, :email, :nickname, :gender, :birthday, :password_digest, :session_token, presence: true
validates :username, :email, :session_token, uniqueness: true
validates :password, length: {minimum: 6}, allow_nil: true
after_initialize :ensure_session_token
...
And here's a more detailed look at my schema for the users table:
# Table name: users
#
# id :bigint not null, primary key
# username :string not null
# email :string not null
# nickname :string not null
# gender :string not null
# birthday :date not null
# password_digest :string not null
# session_token :string not null
# created_at :datetime not null
# updated_at :datetime not null
Could one of these validations be failing? I assumed that because the values for these columns are being set in the form, my user would pass the validations; but am I wrong?
UPDATE: I've fixed the email parameter issue, but I still seem to be getting the 401 status error:
Started POST "/api/users" for ::1 at 2020-01-28 08:54:20 -0800
Processing by Api::UsersController#create as JSON
Parameters: {"user"=>{"username"=>"", "password"=>"[FILTERED]", "nickname"=>"pooh", "gender"=>"male", "birthday"=>"2002-02-02", "email"=>"pooh10"}}
(0.2ms) BEGIN
↳ app/controllers/api/users_controller.rb:6
User Exists (1.3ms) SELECT 1 AS one FROM "users" WHERE "users"."username" = $1 LIMIT $2 [["username", ""], ["LIMIT", 1]]
↳ app/controllers/api/users_controller.rb:6
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = $1 LIMIT $2 [["email", "pooh10"], ["LIMIT", 1]]
↳ app/controllers/api/users_controller.rb:6
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE "users"."session_token" = $1 LIMIT $2 [["session_token", "232kGIICEzCUSSesxgeYfg"], ["LIMIT", 1]]
↳ app/controllers/api/users_controller.rb:6
(0.2ms) ROLLBACK
↳ app/controllers/api/users_controller.rb:6
Completed 401 Unauthorized in 284ms (Views: 0.3ms | ActiveRecord: 12.3ms)
As per your request params for api/users.
Parameters: {"user"=>{"username"=>"racookin", "password"=>"[FILTERED]", "nickname"=>"racookin", "gender"=>"female", "birthday"=>"1990-07-07"}}
You are not passing email in your params. And according to your models, you added uniqueness validation for email.
validates :username, :email, :session_token, uniqueness: true
Now when active record trying to validate your record with existing records, it found a record with null email so it returned validation failed message to you.
If you want to save record with null values you need to add :allow_nil => true in your models for the email field.
Edit
to set username random in your users model.
class User < ApplicationRecord
.....
.....
.....
before_save :assign_username
def assign_username
return unless self.username.blank?
self.username = SecureRandom.alphanumeric(8)
end

uninitialized constant Degree(error)

I am getting the error uninitialized constant Degree. I have a column in database with column name type. When i give data to that field and save, the data i gave is getting saved in database but error message is displayed after that and i am not able to reload that page.
Controller code
class ProfileController < ApplicationController
before_action :set_user, only: %i[index update_profile]
def index; end
def update_profile
if #user.update(user_params)
redirect_to profile_index_path, notice: 'Profile was successfully updated.'
else
render :index
end
end
private
def set_user
#user = User.find(current_user.id)
#user.education || #user.build_education
end
def user_params
params.require(:user).permit(:name, education_attributes: %i[id type name issue_institute education_status])
end
end
education.rb
class Education < ApplicationRecord
belongs_to :user
validates_presence_of :user_id
end
user.rb
class User < ApplicationRecord
has_one :education, dependent: :destroy
accepts_nested_attributes_for :education
end
View code
<%= form_for(#user, url: {action: 'update_profile'}, html: {class: 'm-form m-form--fit m-form--label-align-right'}) do |f| %>
<%= f.fields_for :education, #user.education do |e| %>
<%= e.select :type, options_for_select(%w(Degree Certification), params[:type]), prompt: 'Degree/Certification', class: 'form-control m-input' %>
<%= end %>
<%= f.submit 'Save Changes'%>
<%= end %>
Terminal Log when i save that field
Started PATCH "/profile/update_profile" for 127.0.0.1 at 2018-05-30 09:04:37 +0530
Processing by ProfileController#update_profile as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"9QiEdSxqwkhHqZHiraiQjJcUvUS+oJknYjYaxWUSQrh+je0ASeYQvs//Z+p+oZkOqyAiwxc3nsxp/iohO9B1BA==", "user"=>{"name"=>"Admin", "email"=>"admin#gmail.com", "address_attributes"=>{"area"=>"5, nehru Street", "city"=>"pune", "state"=>"mumbai", "country"=>"india", "postcode"=>"626781", "id"=>"1"}, "education_attributes"=>{"type"=>"Degree", "name"=>"ffgxh", "issue_institute"=>"", "education_status"=>"", "id"=>"1"}, "fee_attributes"=>{"fee_hour"=>"", "fee_month"=>"", "id"=>"1"}}, "commit"=>"Save Changes"}
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 2 ORDER BY `users`.`id` ASC LIMIT 1
User Load (0.2ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 2 LIMIT 1
Address Load (0.2ms) SELECT `addresses`.* FROM `addresses` WHERE `addresses`.`user_id` = 2 LIMIT 1
Fee Load (0.2ms) SELECT `fees`.* FROM `fees` WHERE `fees`.`user_id` = 2 LIMIT 1
Education Load (0.2ms) SELECT `educations`.* FROM `educations` WHERE `educations`.`user_id` = 2 LIMIT 1
Unpermitted parameter: :email
(0.2ms) BEGIN
SQL (0.4ms) UPDATE `educations` SET `type` = 'Degree', `updated_at` = '2018-05-30 03:34:37' WHERE `educations`.`id` = 1
(5.3ms) COMMIT
Redirected to http://localhost:3000/profile
Completed 302 Found in 19ms (ActiveRecord: 7.1ms)
Started GET "/profile" for 127.0.0.1 at 2018-05-30 09:04:37 +0530
Processing by ProfileController#index as HTML
User Load (0.7ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 2 ORDER BY `users`.`id` ASC LIMIT 1
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 2 LIMIT 1
Address Load (0.4ms) SELECT `addresses`.* FROM `addresses` WHERE `addresses`.`user_id` = 2 LIMIT 1
Fee Load (0.3ms) SELECT `fees`.* FROM `fees` WHERE `fees`.`user_id` = 2 LIMIT 1
Education Load (0.8ms) SELECT `educations`.* FROM `educations` WHERE `educations`.`user_id` = 2 LIMIT 1
Completed 401 Unauthorized in 12ms (ActiveRecord: 2.4ms)
NameError - uninitialized constant Degree:
app/controllers/profile_controller.rb:41:in `set_user'
Data gets saved but i have error page after that. Can someone help me with it? Thanks in advance.
I am posting my comment as an answer so that anyone can reference that in future:
type is a reserved keyword for AR. Check list of reserve active record keywords from here. Change the column name. It will resolve the error.

RoR - ActiveAdmin Impossible to create a new product with nested form

I am new to rails and I'm trying to customize ActiveAdmin.
My app has 3 models: User (which has many products), Product (which has many prices) and Price. I am trying to customize ActiveAdmin with a nested form to be able to create/update a product directly with its prices.
Although the update works perfectly (updating product and even adding a new price), the create action doesn't work. I get "rollback" in the console but no specific error message.
Can you tell me what I'm doing wrong?
# app/admin/products.rb
ActiveAdmin.register Product do
form do |f|
f.semantic_errors
f.inputs do
f.input :name
f.input :size
end
f.inputs "Prices" do
f.has_many :prices do |price|
price.input :value
price.input :currency, :collection => ["dollars", "euros", "pounds"]
end
end
f.actions
end
permit_params :name, :size, :user_id, prices_attributes: [:id, :currency, :value, :product_id, :_edit, :_update, :_new, :_create]
I have also put "accepts_nested_attributes_for :prices" in app/models/product.rb.
The logs form the console
Started POST "/admin/products" for 127.0.0.1 at 2018-03-28 14:46:05 +0200
Processing by Admin::ProductsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"W9OJHqyFzXREfi4/DuBN1xnf0KIhAjJDfGsZjQqvGJ7BZOb11fVAz78djOf1k3XZdmpJxuPYinYi6Knu2agvnQ==", "product"=>{"name"=>"Shorts", "size"=>"XL", "prices_attributes"=>{"0"=>{"value"=>"40", "currency"=>"euros"}}}, "commit"=>"Create Product"}
User Load (0.9ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
AdminUser Load (0.4ms) SELECT "admin_users".* FROM "admin_users" WHERE "admin_users"."id" = $1 ORDER BY "admin_users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
(0.2ms) BEGIN
(0.3ms) ROLLBACK
Rendering /Users/alex/.rbenv/versions/2.4.3/lib/ruby/gems/2.4.0/gems/activeadmin-1.2.1/app/views/active_admin/resource/new.html.arb
Rendered /Users/alex/.rbenv/versions/2.4.3/lib/ruby/gems/2.4.0/gems/activeadmin-1.2.1/app/views/active_admin/resource/new.html.arb (100.1ms)
Completed 200 OK in 248ms (Views: 132.6ms | ActiveRecord: 1.9ms)
Thank you very much for you help.

Rails 5.1 strong parameters deprecated?

I'm working on using form_with in Rails 5.1.3 & Ruby 2.3.0
The best documentation I've found is here
Rails Github Repo on line 533, but it's still unclear to me.
# The parameters in the forms are accessible in controllers according to
# their name nesting. So inputs named +title+ and <tt>post[title]</tt> are
# accessible as <tt>params[:title]</tt> and <tt>params[:post][:title]</tt>
# respectively.
Code:
# friendships_controller.rb
...
private
def friendship_params
params.require(:friendship).permit(:user_id, :id)
end
# Works
def destroy
Friendship.remove_friend(current_user.id, params[:id].to_i)
end
# Doesn't work
def destroy
Friendship.remove_friend(friendship_params[:user_id].to_i, friendship_params[:id].to_i)
end
# form_with
<%= form_with model: Friendship,
url: user_friendship_path(
user_id: current_user.id
id: other_user.id,
), method: :delete do |f| %>
<button class='button'></button>
<% end %>
I know the naming is a little confusing, I still haven't figured out how to get form_with to map correctly to the route I need.
friendships#destroy located at path
/users/:user_id/friendships/:id(.:format)
resources :users do
resources :friendships
end
Error Message
Started DELETE "/users/1/friendships/8" for 127.0.0.1 at 2018-01-02 03:21:40 +0700
Processing by FriendshipsController#destroy as JS
Parameters: {"utf8"=>"✓", "authenticity_token"=>"fWnYwpNDItctgg3q/TYIXy5VRO55nwQRINCCykMsDPAFBfwJKjMZ1dneNbg 5yFNHaQP+lXR4ViTje6mK+dCmVg==", "user_id"=>"1", "id"=>"8"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
(0.2ms) BEGIN
(0.1ms) COMMIT
Friendship Load (1.0ms) SELECT "friendships".* FROM "friendships" WHERE "friendships"."friend_id" = $1 AND "friendships"."user_id" = $2 ORDER BY "friendships"."id" ASC LIMIT $3 [["friend_id", 1], ["user_id", 8], ["LIMIT", 1]]
SQL (1.2ms) DELETE FROM "friendships" WHERE "friendships"."id" = $1 [["id", 499]]
Friendship Load (0.5ms) SELECT "friendships".* FROM "friendships" WHERE "friendships"."friend_id" = $1 AND "friendships"."user_id" = $2 ORDER BY "friendships"."id" ASC LIMIT $3 [["friend_id", 8], ["user_id", 1], ["LIMIT", 1]]
SQL (0.9ms) DELETE FROM "friendships" WHERE "friendships"."id" = $1 [["id", 498]]
Completed 400 Bad Request in 13ms (ActiveRecord: 4.2ms)
ActionController::ParameterMissing (param is missing or the value is empty: friendship):
app/controllers/friendships_controller.rb:40:in `friendship_params'
app/controllers/friendships_controller.rb:22:in `destroy'
Maybe I shouldn't say it doesn't work, the behavior is how I expect with or without the friendship_params. However, the server logs returning a 400 Bad Request is a red flag to me.
Thanks for any suggestions in advance~!
The route is a nested resource, so you should provide two arguments to the path. you also need to provide the scope to give it the "friendship" key in the params.
<%= form_with scope: :friendship, url: user_friendship_path(current_user, other_user), method: :delete do |f| %>
<button class='button'></button>
<% end %>
This code is little bit confusing, I suggesting the way for simple like change your route like below
resources :users do
resources :friendships
end
Change to
resources :users
resources :friendships
and on the view
<%= link_to "Remove Friend", friendship_path(friend_id), method: :delete, data: {confirm: "Are you sure"}, class: "btn btn-xs btn-danger" %>
and finally the controller
def destroy
#friendship = current_user.friendships.where(friend_id: params[:id]).first
#friendship.destroy
flash[:notice] = "Friend was removed"
redirect_to(my_friends_path)
end
Hope to help
The answer was that the arguments to the form_with don't pass the actually parameters to the key Friendship, we have to pass them manually within the body of the form.
<%= form_with model: Friendship, url:
user_friendship_path(
current_user, other_user
), method: :delete do |f| %>
<%= f.hidden_field :id, value: other_user.id %>
<button class='button'></button>
<% end %>

How to update a model record? [Rails 4]

I need to update my Pack model in my Rails application, but every time I submit the form, nothing happens. Here's the code:
packs_controller.rb
def edit
#pack = Pack.find(params[:id])
end
def update
#pack = Pack.find(params[:id])
if #pack.update_attributes(pack_params)
flash[:success] = "Update successful!"
redirect_to '/packs'
else
render 'edit'
end
end
private
def pack_params
params.require(:pack).permit(:amount)
end
_edit_packs.html.haml
= form_for(#pack) do |f|
.form-group
= f.label :amount
= f.text_field :amount, :autofocus => true, class: 'form-control'
= f.submit 'Submit', :class => 'button right'
Any ideas?
EDIT
Here is the log when pressing submit:
Started PATCH "/packs/11" for 127.0.0.1 at 2014-06-13 19:27:12 +0200
Started PATCH "/packs/11" for 127.0.0.1 at 2014-06-13 19:27:12 +0200
Processing by PacksController#update as HTML
Processing by PacksController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"oa20eUeZPxbV+mX0mQyDyFibwWCnOlrtP/9uoKq2HU0=", "pack"=>{"amount"=>"1"}, "commit"=>"Submit", "id"=>"11"}
Parameters: {"utf8"=>"✓", "authenticity_token"=>"oa20eUeZPxbV+mX0mQyDyFibwWCnOlrtP/9uoKq2HU0=", "pack"=>{"amount"=>"1"}, "commit"=>"Submit", "id"=>"11"}
Pack Load (0.1ms) SELECT "packs".* FROM "packs" WHERE "packs"."id" = ? ORDER BY created_on DESC LIMIT 1 [["id", 11]]
Pack Load (0.1ms) SELECT "packs".* FROM "packs" WHERE "packs"."id" = ? ORDER BY created_on DESC LIMIT 1 [["id", 11]]
(0.1ms) begin transaction
(0.1ms) begin transaction
Pack Exists (0.1ms) SELECT 1 AS one FROM "packs" WHERE ("packs"."created_on" = '2014-06-13' AND "packs"."id" != 11) LIMIT 1
Pack Exists (0.1ms) SELECT 1 AS one FROM "packs" WHERE ("packs"."created_on" = '2014-06-13' AND "packs"."id" != 11) LIMIT 1
(0.0ms) rollback transaction
(0.0ms) rollback transaction
Rendered packs/_edit_packs.html.haml (1.8ms)
Rendered packs/_edit_packs.html.haml (1.8ms)
Rendered packs/edit.html.haml within layouts/application (2.5ms)
Rendered packs/edit.html.haml within layouts/application (2.5ms)
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 3 ORDER BY "users"."id" ASC LIMIT 1
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 3 ORDER BY "users"."id" ASC LIMIT 1
Rendered layouts/_navigation_links.html.erb (1.2ms)
Rendered layouts/_navigation_links.html.erb (1.2ms)
Rendered layouts/_navigation.html.haml (1.9ms)
Rendered layouts/_navigation.html.haml (1.9ms)
Rendered layouts/_messages.html.haml (0.1ms)
Rendered layouts/_messages.html.haml (0.1ms)
Completed 200 OK in 28ms (Views: 22.0ms | ActiveRecord: 0.6ms)
Completed 200 OK in 28ms (Views: 22.0ms | ActiveRecord: 0.6ms)
EDIT 2
After having looked through some of the comments, and thinking about the problem, it seems like there's a problem with my uniqueness validation on the :created_on field, which stores the date the record was created on.
EDIT 3
Confirmed: :created_on is the root of the problem! I have a validation that enforces the uniqueness of :created_on. When I remove it, the form works. Here is my model:
pack.rb
class Pack < ActiveRecord::Base
belongs_to :user
default_scope -> { order('created_on DESC') }
scope :today, -> { where(:created_at => (Time.now.beginning_of_day..Time.now)) }
scope :week, -> { where(:created_at => (Time.now.beginning_of_week..Time.now))}
scope :month, -> { where(:created_at => (Time.now.beginning_of_month..Time.now))}
scope :year, -> { where(:created_at => (Time.now.beginning_of_year..Time.now))}
validates :user_id, presence: true
validates :created_on, uniqueness: true
validates :amount, presence: true, length: { maximum: 1 }
end
Any suggestions on how to get around this? Should I start a new question?
Based on your logs, it looks like you have multiple packs in your database that were created_on is equal to '2014-06-13' which will cause the created_on uniqueness validation to fail.
Check this in the rails console:
Pack.where(created_on: '2014-06-13')
If there is more than one, delete the extras and it should work.
If you are trying to limit a pack to one per user per day you need to add a scope option to the created on validation:
validates :created_on, uniqueness: true, scope: user

Resources