Rails 4 Devise First Name, Last Name Not Going to DB - mysql

I setup a devise on my rails 4 application. I followed this tutorial https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-sign-in-using-their-username-or-email-address and added the username value. I also wanted First Name and Last Name, so I assumed it was something close to the tutorial. I followed SOME of the parts and skipped the authentication parts, and update the views. It is somewhat working. When registering the fields show up and when you fill up, they pass all check but they DO NOT get entered in the DB. It just shows up NULL for the First name and Last name, but username is actually working. Here are the steps that I did.
I followed the whole tutorial (except for the last part about gmail and me.com).
I added the First Name and Last Name fields:
I ran the commands
rails generate migration add_firstname_to_users first_name:string:uniq
rails generate migration add_lastname_to_users last_name:string:uniq
Then did rake db:migrate. Then I added the fields to application controller to permit the fields. Here is my full application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_filter :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:first_name, :last_name, :username, :email, :password, :password_confirmation, :remember_me) }
devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:login, :username, :email, :password, :remember_me) }
devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:first_name, :last_name, :username, :email, :password, :password_confirmation, :current_password) }
end
end
Then I added the firstname and lastname to the attr_accessor. Here is my full user.rb model.
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
attr_accessor :login, :first_name, :last_name
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :authentication_keys => [:login]
def self.find_first_by_auth_conditions(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
where(conditions).where(["lower(username) = :value OR lower(email) = :value", { :value => login.downcase }]).first
else
where(conditions).first
end
end
end
Then I updated my views and added <%= f.text_field :first_name %> and <%= f.text_field :last_name %> to the registrations new and registrations edit views.
The fields show up and have no errors when submitting the form. They just do not update the DB. I added the name manually in the MYSQL database through PHPMyAdmin and then went to the edit page and it grabs it correctly. It would be great if you could help. Thanks! :)

Try to remove it from attr_accessor because attr_accessor works like instance variable
http://apidock.com/ruby/Module/attr_accessor

Related

Validation problems while creating record through file import using Roo gem and following railscast

I am trying to create records through file imports format can be csv excel etc
and I have implemented it following the Railscast396. but as I import file it says
"Validation failed: Email can't be blank, Password can't be blank. Here is my code
class Student < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
has_many :quizzes
has_many :classrooms
#to import file
**def self.attr_names
[:email, :password, :password_confirmation]
end**
def self.import(file)
spreadsheet = open_spreadsheet(file)
header = spreadsheet.row(1)
(2..spreadsheet.last_row).each do |i|
row = Hash[[header, spreadsheet.row(i)].transpose]
row.inspect
student = find_by_id(row["id"]) || new
student.attributes = row.to_hash.slice(*attr_names)
student.save!
end
end
def self.open_spreadsheet(file)
case File.extname(file.original_filename)
when ".csv" then Roo::CSV.new(file.path, file_warning: :ignore)
when ".xls" then Roo::Excel.new(file.path, file_warning: :ignore)
when ".xlsx" then Roo::Excelx.new(file.path, file_warning: :ignore)
else raise "Unknown file type: #{file.original_filename}"
end
end
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
view is
<%= form_tag addStudents_classrooms_path, multipart: true do %>
<%= file_field_tag :file %>
<%= submit_tag "Import" %>
<% end %>
routes.rb
resources :classrooms do
collection { post :addStudents }
csv file i am trying to load
id,email,password,password_confirmation
22,jim#gmail.com,password,password
23,jimhanks#gmail.com,password,password
This is probably happening because #to_hash doesn't return a hash with indifferent access. You're trying to slice symbolized keys and #to_hash created keys as strings.
Try this:
def self.attr_names
%w(email password password_confirmation)
end

Delete link for user isn't showing up

I am new to web development (I have been coding for about 6/7 months now) in hopes of building my own app. I have been reading and working of the Ruby on Rails tutorial by Michael Hartl. I am on the chapter 9 section deleting users and am stuck. Everything works for the most part except that the user link. It just doesn't show on the display page. The users and their avatars fine. I just can't figure out a way to get the user delete link to show. Below is the code for the user model, the user controller, the session controller where the code current user lives and the view.
Also, when ever i take out the if statement from the user partial the delete link shows. I am not sure whats wrong since I already defined admin, and the current_user.
I would be entirely grateful for any kind of guidance. My project is also on github if the information below isn't sufficient enough
https://github.com/krischery2150/Try2150-master/tree/updating-users
class UsersController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
def new
#user = User.new
end
def show
#user = User.find(params[:id])
end
def create
#user = User.new(user_params)
if #user.save
log_in #user
flash[:success] = "Welcome to the Sample App!"
redirect_to #user
else
render 'new'
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
flash[:success] = "Profile Updated"
redirect_to #user
else
render 'edit'
end
end
def index
#users = User.paginate(page: params[:page])
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "Your profile was deleted"
redirect_to users_url
end
private
def user_params
params.require(:user).permit(:username, :email, :password,
:password_confirmation, :user_about_me,
:birthday, :avatar, :gender)
end
##Before filters method
# Confirms that a given user is logged in. Only when these conditions are met the user will
# be able to update or edit their page
def logged_in_user
unless logged_in?
store_location
flash[:danger]= "Please Log In"
redirect_to login_url
end
end
# Confirms the correct user.
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
end
# Confirms an admin user.
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end
.
class User < ActiveRecord::Base
before_save {self.email = email.downcase}
attr_accessor :remember_token
has_attached_file :avatar, styles: { medium: "300x300>", thumb: "50x50>" }, default_url: "/images/:thumb/missing.png"
validates_attachment_content_type :avatar, content_type: /\Aimage\/.*\Z/
# this before_save is a callback method. What it does is before it saves the email
#address it calls back and transforms all the letters into lower case. Had to do the indexing
#in active record in order for the method to work
validates :username , presence: true, length: {maximum: 250}
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
#code that ensures that a user puts the right format for emails in signup
#fields
validates :email, presence: true, length:{maximum: 50},
format:{with: VALID_EMAIL_REGEX },
uniqueness:{ case_sensitive: false }
#rails still assumes that uniquess is true
#whether the user types CAMELcase or lowercase
validates :password, presence: true, length:{maximum: 50}, allow_nil: true
validates :user_about_me, presence: true
validates :birthday, presence:true
has_secure_password
# Returns the hash digest of the given string.
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
## returns a random user token
def User.new_token
SecureRandom.urlsafe_base64
end
# Remember a given user to the database for use of persistent sessions
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
end
##returns true if given token matches the digest
def authenticated?(remember_token)
return false if remember_digest.nil?
BCrypt::Password.new(remember_digest).is_password?(remember_token)
end
def forget
update_attribute(:remember_digest, nil)
end
def log_out
forget(current_user)
session.delete(:user_id)
#current_user = nil
end
end
This is the code in the partial rendering on the index page where all users show up.
<div class="col-md-9 col-offset-3" id="index-profile">
<li class="users">
<div class="col-xs-3 profilepic-container">
<%= image_tag user.avatar.url %>
</div>
<%= link_to user.username, user %>
<% if current_user.admin? && !current_user?(user) %>
| <%= link_to "delete", user, method: :delete,
data: { confirm: "You sure?" } %>
<% end %>
</li>
The delete link in your partial has two conditions that are required to be true. The user must be an admin, and the profile must not be their own profile. So if the admin user is the only user, then no delete link will show up.
Try creating a second user and see if the delete link shows up for that user.

Ruby on Rails updating specific column based on another table value

I am working with a Ruby on Rails test site that is basically a carbon copy of our actual site. Upon updating user data, I have noticed in both test and the live environment that the user table has both department_id and department_name in it, rather than just joining on department_id and always pulling the info from the department table. The existing code updates department_id in the users table when someone switches departments, but the department_name is not updated. How this was never realized I'm not sure, because there are a few places that pull department_name from the users table directly where I've manually updated the incorrect department_name fields. What do I need to do in the users controller to get it to update the department_name in the user table based on what is in the department table for that department_id? (I know I could recreate the pages to join on the department_id and pull the name from the dept table instead, but I really don't feel like rewriting a bunch of different pages).
users_controller.rb update method
def update
#user = User.find(params[:id])
email_changed = #user.email != params[:user][:email]
#need to set user's department_name so it is updated in users table
#user.update_without_password(params[:user])
successfully_updated = true
if successfully_updated
flash[:notice] = "Profile was successfully updated"
redirect_to #user
else
render "edit"
end
end
Form Control that allows department change in users/_form.html.erb
<% if current_user.is_admin? %>
<%= f.association :department, label: false, :collection =>
#departments, :prompt => "Select Department" %>
...
Model file
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me,
:username, :first_name, :middle_name, :last_name, :suffix, :department_id,
:department_name
belongs_to :department
...
In your User model:
before_save do
self.department_name = department.name if department_id_changed?
end

Creating a new Table Entry with an ID from another table

I'm trying to attach user_id (so I can access user details) to comments that are already attached to a page called Park.
I have three tables set up up: Park, Comment, and User:
class Park < ActiveRecord::Base
has_many :comments
has_many :users
end
class Comment < ActiveRecord::Base
belongs_to :park
has_one :user
end
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :comments
validates_presence_of :username
validates :username, uniqueness: true
def admin?
admin
end
end
As you can see above, I'm using the Devise gem for users and authorizations.
Comments show on Park and so they are nested:
park_comment GET /parks/:park_id/comments/:id(.:format) comments#show
My comment controller is set up like this:
class CommentsController < ApplicationController
before_action :authenticate_user!, only: [:create]
before_action :authorize_admin!, only: [:delete]
def create
#park = Park.find(params[:park_id])
#comment = #park.comments.create(comment_params)
redirect_to park_path(#park)
end
def destroy
#park = Park.find(params[:park_id])
#comment = #park.comments.find(params[:id])
#comment.destroy
redirect_to park_path(#park)
end
private
def comment_params
params.require(:comment).permit(:comment, :user_id, :park_id)
end
end
I've been able to oscillate from park_id to user_id, but the trade off has consistently been to receive a big fat nil on the other term.
I just tried to add a hidden field to my comment form
<%= form_for([#park, #park.comments.build]) do |f| %>
<p>
<%= f.label :comment %><br>
<%= f.text_area :comment %>
<%= hidden_field_tag(:user_id, current_user.id) %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
But that didn't seem to yield any results.
I had tried some ActiveRecord joining work, but it didn't want to latch on to my create so I gave that up. Having spent a while on this now, I'm convinced there's an easy solution I'm just not seeing. Ideas?
Your hidden_field_tag isn't working. It'll create a separate param, not one nested under the param key that you want.
My suggestion would be to remove the hidden_field altogether and specify the current_user in the controller.
class CommentsController < ApplicationController
def create
#park = Park.find(params[:park_id])
#comment = #park.
comments.
create(
comment_params.
merge(user_id: current_user.id) # adding here, on creation
# or merge(user: current_user)
)
redirect_to park_path(#park)
end
def comment_params
params.
require(:comment).
permit(:comment)
end
end
Why? Because otherwise a user can potentially change the value of that hidden_field and you'll be incorrectly storing the comment's user_id value.
I'm not sure if I understood you correctly, but looking at your code I can see some problems.
first of all, you create a hidden field but not using the form helper. That's why your user_id is not visible in your comment_params, causing your
big fat nil
:) .
Try to change
<%= hidden_field_tag(:user_id, current_user.id) %>
to
<%= f.hidden_field(:user_id, :value => current_user.id) %>
But a better Idea for this is to just remove the hidden field and change your controller to
def create
#park = Park.find(params[:park_id])
#comment = #park.comments.new(comment_params)
#comment.user = current_user
if #comment.save
redirect_to park_path(#park)
else
...
end
end
You can now remove your :user_id from your comment_params like this:
params.require(:comment).permit(:comment, :park_id)
Actually, you don't even need :park_id in there

RoR nested attributes produces duplicates when edit

I'm trying to follow Ryan Bates RailsCast #196: Nested model form part 1. There're two apparent differences to Ryans version: 1) I'm using built-in scaffolding and not nifty as he's using, and 2) I'm running rails 4 (I don't really know what version Ryans using in his cast, but it's not 4).
So here's what I did
rails new survey2
cd survey2
bundle install
rails generate scaffold survey name:string
rake db:migrate
rails generate model question survey_id:integer content:text
rake db:migrate
Then I added the associations to the models like so
class Question < ActiveRecord::Base
belongs_to :survey
end
and so
class Survey < ActiveRecord::Base
has_many :questions
accepts_nested_attributes_for :questions
end
Then I added the nested view part
<%= form_for(#survey) do |f| %>
<!-- Standard rails 4 view stuff -->
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.fields_for :questions do |builder| %>
<div>
<%= builder.label :content, "Question" %><br/>
<%= builder.text_area :content, :rows => 3 %>
</div>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
and finally the controller so that 3 questions are created whenever a new survey is instantiated
class SurveysController < ApplicationController
before_action :set_survey, only: [:show, :edit, :update, :destroy]
# Standard rails 4 index and show
# GET /surveys/new
def new
#survey = Survey.new
3.times { #survey.questions.build }
Rails.logger.debug("New method executed")
end
# GET /surveys/1/edit
def edit
end
# Standard rails 4 create
# PATCH/PUT /surveys/1
# PATCH/PUT /surveys/1.json
def update
respond_to do |format|
if #survey.update(survey_params)
format.html { redirect_to #survey, notice: 'Survey was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #survey.errors, status: :unprocessable_entity }
end
end
end
# Standard rails 4 destroy
private
# Use callbacks to share common setup or constraints between actions.
def set_survey
#survey = Survey.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def survey_params
params.require(:survey).permit(:name, questions_attributes: [:content])
end
end
So, creating a new survey with three questions is fine. However, if I try to edit one of the surveys, the original three questions are maintained, while an additional three more are created. So instead of having 3 questions for the edited survey, I now have 6. I added
Rails.logger.debug("New method executed")
to the new method in the controller, and as far as I can tell, it is not executed when I'm doing an edit operation. Can anyone tell me what I'm doing wrong?
Any help is greatly appreciated!
I had to add :id to the permitted params in the survey_params method. It now looks like this:
# Never trust parameters from the scary internet, only allow the white list through.
def survey_params
params.require(:survey).permit(:name, questions_attributes: [:id, :content])
end
which works perfectly. I guess new id's were generated instead of being passed to the update action.
Using cocoon gem on Rails 4, I was still getting duplicate fields even after adding :id to the permitted list when editing. Noticed the following as well
Unpermitted parameters: _destroy
Unpermitted parameters: _destroy
So I added the :_destroy field to the permitted model_attributes: field and things worked smoothly after that.
For example...
def survey_params
params.require(:survey).permit(:name, questions_attributes: [:id, :content, :_destroy])
end