I am trying to create a login system in Rails, but, when I go to the signup page, I get an error saying : "undefined method `first_name' for #". I checked my users database for the "first_name" column, and found that the only columns in the "users" table were "id", "created_at", and "updated_at". I created my app using rails new appname -d mysql, I have the mysql2 gem, I have updated my databases.yml file, and I have run "rake db:migrate". Still, nothing works. How can I fix this? This is my users controller:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to '/login'
else
redirect_to '/signup'
end
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password)
end
end
Here is my router:
Rails.application.routes.draw do
root 'main#index'
get '/login' => 'sessions#new'
get '/signup' => 'users#new'
resources :users
post 'login' => 'sessions#create'
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
Here is my model:
class User < ApplicationRecord
has_secure_password
end
Here is my Migration file:
class CreateUsers < ActiveRecord::Migration[5.0]
def change
create_table :users do |t|
t.string :first_name
t.string :last_name
t.string :email
t.string :password_digest
t.timestamps
end
end
end
Here is my view:
<div class="login">
<div class="container">
<div class="form">
<h1>Sign up</h1>
<%= form_for(#user) do |f| %>
<%= f.text_field :first_name, :placeholder => "First name" %>
<%= f.text_field :last_name, :placeholder => "Last name" %>
<%= f.email_field :email, :placeholder => "Email" %>
<%= f.password_field :password, :placeholder => "Password" %>
<%= f.submit "Create an account", class: "btn-submit" %>
<% end %>
</div>
</div>
</div>
I am using Rails 5.0.2 on Ubuntu 16.04
You might want to try to redo the migration with db:migrate:redo, if that doesnt work, create a new migration adding the respective columns.
class CreateUsers < ActiveRecord::Migration[5.0]
def change
add_column :products, :part_number, :string
add_column :products, :price, :decimal
end
end
Related
I want, that a user with the admin role can edit other users from users/index.html.erb view. I'm not the first one asking this question, but all the given answers lack clear instructions.
So the problem is; after editing the fields, when I click on the 'Updtate' button in users/index.html.erb view, the page is reloaded but no changes appens.
Aditionnal infos: I'm using Devise and Omniauth facebook.
The users are display in the users/index via a partial:
<td>
<div class="field">
<%= f.text_field :name, class: 'form-control' %>
</div>
</td>
<td>
<div class="field">
<%= f.text_field :email, class: 'form-control' %>
</div>
</td>
<td>
<div class="actions">
<%= f.submit I18n.translate('control.update'), class: 'btn sign-up-button' %>
</div>
</td>
<td>
<!-- display a delete button if the user is not the current_user -->
<%= link_to(I18n.translate('control.delete'), user_path(user), :data => { :confirm => "Are you sure?" }, :method => :delete, :class => 'btn') unless user == current_user %>
</td>
users/index.html.erb:
<div class="container">
<div class="row">
<div class="col-sm-6 col-sm-offset-3">
<h2 class="text-center"><%= I18n.translate('user.users_list') %></h2>
<div class="column">
<table class="table">
<tbody>
<% #users.each do |user| %>
<tr>
<%= render user %>
</tr>
<% end %>
</tbody>
</table>
</div>
<ul class=”pagination”>
<li><%= will_paginate(#users) %></li>
</ul>
</div>
</div>
</div>
My user_controller.rb
class UsersController < ApplicationController
before_action :ensure_admin, :except => :show
def index
#users = User.paginate(:page => params[:page], :per_page => 8)
end
def show
#user = User.find_by_name(params[:id])
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update(secure_params)
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
def destroy
user = User.find(params[:id])
user.destroy
redirect_to users_path, :notice => "User deleted."
end
private
def ensure_admin
if(current_user.role == 'admin')
return
end
redirect_to root_path, :alert => "Access denied."
end
def secure_params
params.require(:user).permit(:role)
end
def user_params
params.require(:user).permit(:email, :name, :password)
end
end
My routes:
Rails.application.routes.draw do
root to: 'pages#index'
get 'users/index'
# you can type '/users' to view the users
match '/users', to: 'users#index', via: 'get'
devise_for :users, :controllers => { :registrations => "registrations",
:path_prefix => 'd',
:omniauth_callbacks => "users/omniauth_callbacks" }
resources :users
scope '/:locale' do
devise_scope :user do
get 'login', to: 'devise/sessions#new'
end
devise_scope :user do
get 'signup', to: 'devise/registrations#new'
end
end
Here in the user_controller update action you are calling secure_params def, which is permitting only "role" field.
I think you should to use user_params in update action like below
def update
#user = User.find(params[:id])
if #user.update(user_params)
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
Or you will have to permit other params also in secure_params def like
def secure_params
params.require(:user).permit(:role,:name,:email)
end
Hope it would help you.
Short Version: I'm getting this error:
undefined method `map' for nil:NilClass
and the error highlights this code as the problem code:
def sizes
read_attribute(:sizes).map { |s| Size.new(s) }
end
Why and how do I stop that from happening?
Longer Version: (I based using the json column on this post)
The thing I'm not sure I'm accounting for in my attempt is that in this article they're describing how to do all of this using Postgres and I'm using MySQL.
I'm trying to take data from a form and split two of the pieces into a JSON format on one column. Here is the relevant portion of my schema:
create_table "products", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
t.string "name"
t.text "description", limit: 65535
t.integer "category_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.json "sizes"
t.index ["category_id"], name: "index_products_on_category_id", using: :btree
end
The JSON column will have amount(read sizes) and a cost associated to it. This is all about pizza and the many different sizes and associated prices based on size. Entering the data should look like this, I'm ignoring the category_id for now.
Product.create(name: "Neapolitan", sizes: { "small":9.95, "medium":10.95, "large":11.95, "xlarge":13.50, "xxlarge":29.95})
The above code works just I expect it to when I use rails console. The problem has something to do with my model and view/form. Basically, I keep asking myself, "why is 'nil' a problem here, nothing is set yet, that's the point of a form? Everything should be 'nil' "
Here is my Product.rb
class Product < ApplicationRecord
belongs_to :category
VARIANTS = ['small', 'medium', 'large', 'xlarge', 'xxlarge'].freeze
def sizes
read_attribute(:sizes).map { |s| Size.new(s) }
end
def sizes_attributes=(attributes)
sizes = []
attributes.each do |index, attrs|
next if '1' == attrs.delete("_destroy")
sizes << attrs
end
write_attributes(:sizes, sizes)
end
def build_size
s = self.sizes.dup
s << Size.new({amount: '', cost: 0})
self.sizes = s
end
class Size
attr_accessor :amount, :cost
def initialize(hash)
#amount =hash[:amount]
#cost =hash[:cost]
end
def persisted?() false; end
def new_record?() false; end
def mark_for_destruction?() false; end
def _destroy() false; end
end
end
amount acts as the key and the cost acts as the value. VARIANTS is where I'll pull my selection of locked in sizes.
Here are my forms:
_form.rb
<%= simple_form_for(#product) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :name %>
<%= f.input :description %>
<%= f.input :category_id %>
<div class="form-group">
<%# f.collection_select :sizes, Product::VARIANTS, :to_s, :to_s %>
</div>
<div class="form-group">
<%= f.label :sizes %>
<%= f.fields_for :sizes do |size_form| %>
<%= render "size_fields", f: size_form %>
<% end %>
</div>
<div class="form-actions">
<%= f.submit class: "btn btn-success" %>
</div>
</div>
<% end %>
_size_fields.rb
<fieldset>
<%= f.label :amount %>
<%= f.collection_select :amount, Product::VARIANTS, :to_s, :to_s %>
<%= f.label :cost %>
<%= f.text_field :cost %>
<%= f.hidden_field :_destroy %>
<%= link_to "remove", "#", class: "remove_fields" %>
</fieldset>
products_controller.rb
# GET /products/new
def new
#product = Product.new
#categories = Category.all
#product.build_size
end
...
# POST /products
# POST /products.json
def create
#product = Product.new(product_params)
#categories = Category.all
respond_to do |format|
if #product.save
format.html { redirect_to #product, notice: 'Product was successfully created.' }
format.json { render :show, status: :created, location: #product }
else
format.html { render :new }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
...
def product_params
params.require(:product).permit(:name, :description, :category_id, sizes_attributes: [:amount, :cost, :_destroy] )
end
What am I getting wrong? How do I fix it?
I'm new to ruby on rails so please forgive the question. I tried following this example How to "count" user inputs from a Controller but kept getting nothing in my view. I am trying to create a count functionality on my share feature (similar to retweet which works perfectly now). >> So whenever a user shares a tweet there is a counter that changes with it as well. I have listed below all my relevant code. Thank you so much guys!!
tweets_controller.rb
class TweetsController < ApplicationController
before_action :set_tweet, only: [:show, :edit, :update, :destroy, :share]
def index
#tweets = Tweet.all
end
def share
tweet = current_user.tweets.create(tweet_id: #tweet.id)
if tweet.save
redirect_to tweets_path
else
redirect_to :back, alert: "Unable to share"
end
end
_share.html.erb
<div>
<div>Shared by <%= tweet.user.username %></div>
<%= render partial: "tweets/tweet", locals: {tweet: tweet.tweet} %>
</div>
_tweet.html.erb
<div class="row">
<div class="col-md-3"><%= tweet.watches_count %><%= render partial: "watches", locals: {tweet: tweet} %></div>
<div class="col-md-3"><%= link_to '<i class="fa fa-share fa-lg" aria-hidden="true"></i>'.html_safe, share_tweet_path(tweet), method: :post %></div>
<div class="col-md-6"><span class="pull-right"><%= tweet.likes_count %><%= render partial: "likes", locals: {tweet: tweet} %></span></div>
</div>
</div>
routes.rb
Rails.application.routes.draw do
resources :tweets do
resource :like, module: :tweets
resource :watch, module: :tweets
member do
post :share
end
end
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }
resources :users, :controllers => "users_controller.rb"
root to: "tweets#index"
end
Migration CreateTweets
class CreateTweets < ActiveRecord::Migration[5.0]
def change
create_table :tweets do |t|
t.text :body
t.integer :user_id
t.integer :tweet_id
t.timestamps
end
end
end
Relevant Rake Routes
share_tweet POST /tweets/:id/share(.:format) tweets#share
tweets GET /tweets(.:format) tweets#index
POST /tweets(.:format) tweets#create
I am trying to store hash password in my users table while registration. Please see my code:
users_controller.rb
def login
#title = 'Login'
#render layout: 'login'
end
def create_login
user = User.authenticate(params[:user][:username], params[:user][:password])
if user
log_in user
redirect_to '/admin'
else
flash[:danger] = 'Invalid email/password combination' # Not quite right!
redirect_to :back
end
end
def register
#user = User.new
#title = 'Register'
end
def create_register
params[:user][:uniq_id] = generate_uniq
#user = User.new(create_user_params)
#raise #user.inspect
respond_to do |format|
if #user.save
format.html { redirect_to :success, success: 'Registration was successfully created.' }
format.json { redirect_to :register, status: :created, location: #users }
else
format.html { render :register }
format.json { render json: #users.errors, status: :unprocessable_entity }
end
end
end
private
def create_user_params
params.require(:user).permit(:uniq_id, :name, :username, :email, :password, :password_confirmation, :password_salt, :dob, :address)
end
register.html.erb
<%= form_tag("/register", method: "post") do %>
<%#= form_tag(#user) do |f| %>
<% if #user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% #user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= text_field :user, :name, placeholder:'NAME', required: true %>
<div style="position: relative;">
<span id="chk-username" style="position: absolute;font-size: 12px;right: 2%; bottom: 5%; z-index: 9; display: block;"></span>
<%= text_field :user, :username, placeholder:'USERNAME', 'data-validate':"/users/check_username", required: true %>
</div>
<div style="position: relative;">
<span id="chk-email" style="position: absolute;font-size: 12px;right: 2%; bottom: 5%; z-index: 9; display: block;"></span>
<%= text_field :user, :email, placeholder:'EMAIL', 'data-validate':"/users/check_email", required: true %>
</div>
<%= password_field :user, :password, placeholder:'PASSWORD', required: true %>
<%= password_field :user, :password_confirmation, placeholder:'CONFIRM PASSWORD', required: true %>
<div class="submit">
<input type="submit" value="REGISTER" >
<input type="button" onclick="location.href = '<%= request.base_url %>/login'" value="LOGIN" >
</div>
<p>Forgot Password ?</p>
<% end %>
user.rb
class User < ActiveRecord::Base
#has_secure_password
attr_accessor :password
before_save :encrypt_password
validates :name, presence: true
validates :name, length: { minumum:2, maximum: 30 }
validates :password, :presence =>true,
:length => { :minimum => 6, :maximum => 40 },
:confirmation =>true
validates :username, :presence => true,
:uniqueness => { :case_sensitive => false }
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, :presence => true,
:format => { :with => email_regex },
:uniqueness => { :case_sensitive => false }
def self.authenticate(input_username, input_password)
user = find_by_username(input_username)
if user && user.password == BCrypt::Engine.hash_secret(input_password, user.password_salt)
user
else
nil
end
end
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password = BCrypt::Engine.hash_secret(password, password_salt)
end
end
end
routes.rb
get 'register' => 'users#register'
post 'register' => 'users#create_register'
Here is my database table.
users.sql (customize table)
+----+----------+------------+-----------+----------------+
| id | name | username | password | password_salt |
+----+----------+------------+-----------+----------------+
| 1 | chinmay | chinu | NULL |$2a$10$15fWDt.. |
| 2 | sanjib | sanjib | NULL |$2a$10$85DyMr.. |
+----+----------+------------+-----------+----------------+
I get NULL value in my password column. Please help me and let me know where the error is in my code.
Your main error is that your are using attr_accessor :password to create a getter/setter for the password attribute that overrides the getter and setter that ActiveRecord creates from the database schema.
However your whole approach to password encryption is flawed - you should have password as a purely virtual attribute and name your database column password_digest or encrypted_password.
Unless its for pure learning purposes should use the has_secure_password macro that Rails provides instead of reinventing the password encryption wheel and getting hacked.
1. Add a password_digest column to user:
rails g migration AddPassWordDigestToUser password_digest:string:index
You might want to remove the password_salt column as well as it is not used by ActiveModel::SecurePassword.
class AddPassWordDigestToUser < ActiveRecord::Migration
def change
add_column :users, :password_digest, :string
add_index :users, :password_digest
remove_column :users, :password_salt
remove_column :users, :password
end
end
2. Add has_secure_password to the User model:
class User < ActiveRecord::Base
has_secure_password
end
3. RESTful routes
You may want to correct your routes so they are resource oriented and not action oriented and follow the rails conventions:
GET /registrations/new registations#new - sign up form
POST /registrations registations#create - create user
GET /sessions/new sessions#new - sign in form
POST /sessions sessions#create - sign in user
You can setup the routes with just:
resources :registrations, only: [:new, :create]
resources :sessions, only: [:new, :create]
See Rails Routing from the Outside In.
4. Binding forms and controllers.
You are setting up the controller properly however your form is not bound to the #user model instance you are creating in your controller.
This means that the values the user enters disappear after a unsuccessful form submission.
Also pay attention to the pluralization and naming of your variables! You are inconsistently using #user and #users. In this case #users will always be nil and cause an error.
app/controllers/registrations_controller.rb:
class RegistationsController < ApplicationController
def new
#user = User.new
end
def create
# Use a block instead of messing with the incoming params.
#user = User.new(user_params) do |u|
u.uniq_id = generate_uniq
end
if #user.save
respond_to do |format|
format.html { redirect_to root_path, success: "Welcome #{#user.email}" }
format.json { status: :created, location: #user }
end
else
respond_to do |format|
format.html { redirect_to :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
app/views/registrations/new.html.erb:
<%= form_for(#user, url: registrations_path) do |f| %>
<div class="row">
<%= f.label :email %>
<%= f.text_field :email %>
</div>
<div class="row">
<%= f.label :password %>
<%= f.password_field :password %>
</div>
<div class="row">
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation %>
</div>
<% end %>
So i am getting trouble in saving form data,.Any help will b appreciable
form is submitted without getting any error, but in database, nothing is stored
i am new on rails
users_controller
class UsersController < ApplicationController
def new
#user= User.new
end
def create
#user = User.new(params[:User])
if #user.save
flash[:notice]= "you signed up successfully"
flash[:color]= "valid"
else
flash[:notice]= "failed"
flash[:color]="invalid"
end
render "new"
end
end
new.html.erb
<% page_title="Signup" %>
<div class="Sign_Form">
<h1>Sign up</h1>
<%= form_for(:user, :url => {:controller => 'users', :action => 'create'}) do |f| %>
<p> Username:</br> <%= f.text_field :username %> </p>
<p> Email:</br> <%= f.text_field :email %> </p>
<p> Password:</br> <%= f.password_field :password %></p>
<p> Password Confirmation:</br> <%= f.password_field :password_confirmation %> </p>
<%= f.submit :Signup %>
<% end %>
<% if #user.errors.any? %>
<ul class="Signup_Errors">
<% for message_error in #user.errors.full_messages %>
<li>* <%= message_error %></li>
<% end %>
</ul>
<% end %>
</div>
in app/models/user.rb
class User < ActiveRecord::Base
attr_accessor :password
EMAIL_REGEX = /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i
validates :username, :presence => true, :uniqueness => true, :length => { :in => 3..20 }
validates :email, :presence => true, :uniqueness => true #:format => EMAIL_REGEX
validates :password, :presence =>true #:confirmation => true #password_confirmation attr
validates_length_of :password, :in => 6..20, :on => :create
end
In users_controller, create method, you are using
#user = User.new(params[:User])
replace it with following code, hope it will work fine.
#user = User.new(params[:user])
And,also use strong params if you are using rails 4. Like follwoing.
def create
#user = User.new(user_params)
if #user.save
flash[:notice]= "you signed up successfully"
flash[:color]= "valid"
else
flash[:notice]= "failed"
flash[:color]="invalid"
end
render "new"
end
private
def user_params
params.require(:user).permit(:username, :account, :email, :password, :password_confirmation)
end
If, it still not works, then, please display your log.
If you are using Rails 4 you need to use strong parameters to whitelist the parameters you want to assign to your models.
This became non-optional in Rails 4 to prevent mass-assignment vulnerabilities where a malicious user can assign any property to a model after Egor Homakovs much publicised Github attack.
Also note that Ruby is case sensitive. This applies to hash keys as well:
irb(main):003:0> hash = { a: 1 }
=> {:a=>1}
irb(main):004:0> hash[:A]
=> nil
Which is why why you do User.new(params[:User]) you are actually doing User.new(nil)
This is a corrected version of your controller
class UsersController < ApplicationController
def new
#user= User.new
end
def create
#user = User.new(params[:User])
if #user.save
flash[:notice]= "you signed up successfully"
flash[:color]= "valid"
# You should redirect instead of rendering the form again
redirect_to #user # or redirect_to root_path
else
flash[:notice]= "failed"
flash[:color]="invalid"
render "new" # Needs to be inside the "else" statement
# Otherwise you will get a double render error
end
end
def user_params
params.require(:user)
.allow(:username, :email, :password, :password_confirmation)
end
end
Added:
You can also simplify your form_for to
<%= form_for(:user) do |f| %>
Rails will by convention route the form to UserController#create.
Also you should use <label> tags for accessibility, as they help people who use assistive technology such as screen readers to find the correct inputs.
By using the built in label helper rails will set up the for attribute and you can translate the label texts with Rails built in I18n functionality.
<% page_title="Signup" %>
<div class="Sign_Form">
<h1>Sign up</h1>
<%= form_for(:user) do |f| %>
<div class="row">
<%= f.label :username %>:</br>
<%= f.text_field :username %>
</div>
<div class="row">
<%= f.label :email %>:</br>
<%= f.text_field :email %>
</div>
<div class="row">
<%= f.label :password %>:</br>
<%= f.password_field :password %>
</div>
<%= f.submit :Signup %>
<% end %>
<% if #user.errors.any? %>
<ul class="Signup_Errors">
<%# for loops are almost never used in ruby. %>
<%# .each is the idiomatically correct way %>
<% #user.errors.full_messages.each do |message_error| %>
<li>* <%= message_error %></li>
<% end %>
</ul>
<% end %>
</div>