Unable to update password with Bcrypt - ruby-on-rails

I've tried this many ways but it seems BCrypt is encrypting a users submitted password twice.
When a user signs up- Bcrypt works great, and I am able to sign in. But when I try and update their password in my password_resets_controller, I'm no longer able to log in. My database shows that the password is being updated and hashed, but I can't sign in.
I even removed the line #customer.save, yet my database is still showing that the password is being updated !
Is something being updated under the hood I'm not aware of? See relatd SO thread:
Updating password with BCrypt
In my Customer.rb
require 'bcrypt'
class Customer < ActiveRecord::Base
include BCrypt
def password
#password ||= Password.new(password_hash)
end
def password=(new_password)
#password = Password.create(new_password)
self.password_hash = #password
end
def self.authenticate(email, password)
#customer = Customer.find_by_email(email)
if #customer && #customer.password == password
return #customer
else
return nil
end
end
end
In my customer_controller, the create code that actually works
require 'bcrypt'
class CustomersController < ApplicationController
def create_customer_account_iphone
#customer_count = Customer.where(email: params[:email]).size rescue nil
if(#customer_count == 0 || #customer_count == nil ||)
#customer = Customer.new(first_name: params[:first_name], email: params[:email])
#customer.password = params[:password] //this calls my model methods
#customer.save //here I am saving
unless (!#customer.save)
respond_to do |format|
msg = {:status => "SUCCESS", :messages => "Customer created", :data => #customer.as_json}
format.json { render :json => msg } # don't do msg.to_json
end
else
respond_to do |format|
msg = {:status => "FAILED", :messages => "Customer Not Saved"}
format.json { render :json => msg } # don't do msg.to_json
end
end
def sign_in_iphone
#customer = Customer.authenticate(params[:email], params[:password])
unless (#customer == 0 || #customer == nil)
respond_to do |format|
msg = {:status => "SUCCESS", :message => "CUSTOMER", :data => #customer.as_json}
format.json { render :json => msg } # don't do msg.to_json
end
else
respond_to do |format|
msg = {:status => "FAILED"}
format.json { render :json => msg } # don't do msg.to_json
end
end
end
In my password_reset_controller
class CustomerPasswordResetsController < ApplicationController
def edit
#customer = Customer.find_by_password_reset_token!(params[:id])
end
def update
#customer = Customer.find_by_password_reset_token!(params[:id])
if #customer.password_reset_sent_at < 2.hours.ago
redirect_to new_customer_password_reset_path, :alert => "Password reset has expired."
else
#customer.password_hash = BCrypt::Password.create(params[:password])
# #customer.save
unless !#customer.save
redirect_to new_customer_password_reset_path, :alert => "Password has been reset!"
else
render :edit
end
end
end
In my password_reset.html.erb
<%= form_for #customer, :url => customer_password_reset_path(params[:id]), :method => :patch do |f| %>
<% if #customer.errors.any? %>
<div class="error_messages">
<h2>Form is invalid</h2>
<ul>
<% for message in #customer.errors.full_messages %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :password %>
<%= f.password_field :password %>
</div>
<div class="field">
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation %>
</div>
<div class="actions"><%= f.submit "Update Password" %></div>

Given your form the new password will be in params[:customer][:password] not params[:password] - your existing code always sets the password to nil.
Changing the password resets controller update action to instead do
#customer.password = params[:customer][:password]
should do the trick. As a side note the commented out customer.save doesn't matter because you save again on the next line.
Next time something like this happens consider using the debugger to examine what is happening in your action - it would be easy enough to spot that the password was being set to nil. The debugging guide has lots more tip on this.

It's possible that you're assigning password as well as doing some post-processing with password_hash. The way this is intended to be used is via password alone if you have the model code with that password= method. This means you won't need to do any additional work beyond simply assigning it.
What you want in your password_reset method is:
#customer.password = params[:password]
#customer.save!
That should take care of it by running it through the appropriate model code.

Related

Rails 4 - Devise Omniauth and allowing a single user to authenticate with multiple social media strategies

I am trying to make an app with Rails 4.
I have been trying (for 3+ years) to figure out how to get Devise and Omniauth to works, so that users can add multiple social media accounts to their user profile.
I've read all of the devise and omniauth documentation. The best I can get to with those docs is to add 1 single social media account. That's not what I want.
I've tried this site point tutorial
sitepoint.com/rails-authentication-oauth-2-0-omniauth
I've tried this willschenck tutorial
http://willschenk.com/setting-up-devise-with-twitter-and-facebook-and-other-omniauth-schemes-without-email-addresses/
I've tried this jorge.caballeromurillo tutorial: http://jorge.caballeromurillo.com/multiple-omniauth-providers-for-same-user-on-ruby-on-rails/
I've also tried this sourcey tutorial: http://sourcey.com/rails-4-omniauth-using-devise-with-twitter-facebook-and-linkedin/
I've pledged thousands of points in bounties on SO in trying to find help with this problem - but not yet figured it out. I've been to every rails meetup in my area for the last 3 years and wasted $$$ on codementor in trying to find help. Enough time has passed since the most recent frustrating attempt to be ready to give it another go. Please help.
Here's what I have so far:
User.rb
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable,
:confirmable, :lockable,
# :zxcvbnable,
:omniauthable, :omniauth_providers => [:facebook, :linkedin, :twitter, :google_oauth2 ]
has_many :identities, dependent: :destroy
def self.find_for_oauth(auth, signed_in_resource = nil)
# Get the identity and user if they exist
identity = Identity.find_for_oauth(auth)
# If a signed_in_resource is provided it always overrides the existing user
# to prevent the identity being locked with accidentally created accounts.
# Note that this may leave zombie accounts (with no associated identity) which
# can be cleaned up at a later date.
user = signed_in_resource ? signed_in_resource : identity.user
# p '11111'
# Create the user if needed
if user.nil?
# p 22222
# Get the existing user by email if the provider gives us a verified email.
# If no verified email was provided we assign a temporary email and ask the
# user to verify it on the next step via UsersController.finish_signup
email_is_verified = auth.info.email && (auth.info.verified || auth.info.verified_email)
email = auth.info.email if email_is_verified # take out this if stmt for chin yi's solution
user = User.where(:email => email).first if email
# Create the user if it's a new registration
if user.nil?
# p 33333
user = User.new(
# at least one problem with this is that each provider uses different terms to desribe first name/last name/email. See notes on linkedin above
first_name: auth.info.first_name,
last_name: auth.info.last_name,
email: email ? email : "#{TEMP_EMAIL_PREFIX}-#{auth.uid}-#{auth.provider}.com",
#username: auth.info.nickname || auth.uid,
password: Devise.friendly_token[0,20])
# fallback for name fields - add nickname to user table
# debugger
# if email_is_verified
user.skip_confirmation!
# end
# user.skip_confirmation!
user.save!
end
end
# Associate the identity with the user if needed
if identity.user != user
identity.user = user
identity.save!
end
user
end
def email_verified?
self.email && TEMP_EMAIL_REGEX !~ self.email
end
Identity.rb
belongs_to :user
validates_presence_of :uid, :provider
validates_uniqueness_of :uid, :scope => :provider
def self.find_for_oauth(auth)
find_or_create_by(uid: auth.uid, provider: auth.provider)
end
Users controller:
class UsersController < ApplicationController
before_action :set_user, only: [ :show, :edit, :update, :finish_signup, :destroy]
def index
# if params[:approved] == "false"
# #users = User.find_all_by_approved(false)
# else
#users = User.all
authorize #users
# end
end
# GET /users/:id.:format
def show
# authorize! :read, #user
end
# GET /users/:id/edit
def edit
# authorize! :update, #user
authorize #user
end
# PATCH/PUT /users/:id.:format
def update
# authorize! :update, #user
respond_to do |format|
authorize #user
if #user.update(user_params)
sign_in(#user == current_user ? #user : current_user, :bypass => true)
# I'm trying to get the user matched to an organisation after the email address (in finish sign up) updates the user model.
UserOrganisationMapperService.call(#user)
format.html { redirect_to #user }#, notice: 'Your profile was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# GET/PATCH /users/:id/finish_signup
def finish_signup
# authorize! :update, #user
if request.patch? && params[:user] #&& params[:user][:email]
if #user.update(user_params)
#user.skip_reconfirmation!
# #user.confirm!
sign_in(#user, :bypass => true)
redirect_to root_path#, notice: 'Your profile was successfully updated.'
# redirect_to [#user, #user.profile || #user.build_profile]
# sign_in_and_redirect(#user, :bypass => true)
else
#show_errors = true
end
end
end
# DELETE /users/:id.:format
def destroy
# authorize! :delete, #user
#user.destroy
authorize #user
respond_to do |format|
format.html { redirect_to root_url }
format.json { head :no_content }
end
end
private
def set_user
#user = User.find(params[:id])
authorize #user
end
def user_params
# params.require(:user).permit(policy(#user).permitted_attributes)
accessible = [ :first_name, :last_name, :email, :avatar, {role_ids: []} ] # extend with your own params
accessible << [ :password, :password_confirmation ] unless params[:user][:password].blank?
# accessible << [:approved] if user.admin
params.require(:user).permit(accessible)
end
end
Identities controller
class IdentitiesController < ApplicationController
before_action :set_identity, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
# GET /identities
# GET /identities.json
def index
#identities = Identity.all
end
# GET /identities/1
# GET /identities/1.json
def show
end
# GET /identities/new
def new
#identity = Identity.new
end
# GET /identities/1/edit
def edit
end
# POST /identities
# POST /identities.json
def create
#identity = Identity.new(identity_params)
respond_to do |format|
if #identity.save
format.html { redirect_to #identity, notice: 'Identity was successfully created.' }
format.json { render :show, status: :created, location: #identity }
else
format.html { render :new }
format.json { render json: #identity.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /identities/1
# PATCH/PUT /identities/1.json
create alternative that I have also tried
def create
auth = request.env['omniauth.auth']
# Find an identity here
#identity = Identity.find_with_omniauth(auth)
if #identity.nil?
# If no identity was found, create a brand new one here
#identity = Identity.create_with_omniauth(auth)
end
if signed_in?
if #identity.user == current_user
# User is signed in so they are trying to link an identity with their
# account. But we found the identity and the user associated with it
# is the current user. So the identity is already associated with
# this user. So let's display an error message.
redirect_to root_url, notice: "Already linked that account!"
else
# The identity is not associated with the current_user so lets
# associate the identity
#identity.user = current_user
#identity.save
redirect_to root_url, notice: "Successfully linked that account!"
end
else
if #identity.user.present?
# The identity we found had a user associated with it so let's
# just log them in here
self.current_user = #identity.user
redirect_to root_url, notice: "Signed in!"
else
# No user associated with the identity so we need to create a new one
redirect_to new_registration_path, notice: "Please finish registering"
end
end
end
def update
respond_to do |format|
if #identity.update(identity_params)
format.html { redirect_to #identity, notice: 'Identity was successfully updated.' }
format.json { render :show, status: :ok, location: #identity }
else
format.html { render :edit }
format.json { render json: #identity.errors, status: :unprocessable_entity }
end
end
end
# DELETE /identities/1
# DELETE /identities/1.json
def destroy
#identity.destroy
respond_to do |format|
format.html { redirect_to identities_url, notice: 'Identity was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_identity
#identity = Identity.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def identity_params
params[:identity]
end
end
registrations controller
class Users::RegistrationsController < Devise::RegistrationsController
before_action :configure_permitted_parameters, if: :devise_controller?
def create
super do |resource|
UserOrganisationMapperService.call(resource)
end
end
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:email, :password, :first_name, :last_name) }
end
private
end
omniauth callbacks controller
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
#sourcey tutorial ------------------
def self.provides_callback_for(provider)
class_eval %Q{
def #{provider}
#user = User.find_for_oauth(env["omniauth.auth"], current_user)
if #user.persisted?
sign_in_and_redirect #user, event: :authentication
else
session["devise.#{provider}_data"] = env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
}
end
[:twitter, :facebook, :linkedin, :google_oauth2].each do |provider|
provides_callback_for provider
end
end
users/finish sign up view
<div class="container-fluid">
<div class="row">
<div class="col-xs-8 col-xs-offset-2">
<h1 class="formheader">Complete your registration</h1>
<%= form_for(current_user, :as => 'user', :url => finish_signup_path(current_user), :html => { role: 'form'}) do |f| %>
<% if #show_errors && current_user.errors.any? %>
<div id="error_explanation">
<% current_user.errors.full_messages.each do |msg| %>
<%= msg %><br>
<% end %>
</div>
<% end %>
<div class="form-group">
<!-- f.label :false -->
<div class="controls">
<% if current_user.first_name.blank? %>
<%= f.text_field :first_name, :value => '', class: 'form-control input-lg', placeholder: 'First name' %>
<p class="help-block">Hi there, what is your first name?.</p>
<% end %>
<% if current_user.last_name.blank? %>
<%= f.text_field :last_name, :value => '', class: 'form-control input-lg', placeholder: 'Last name (surname)' %>
<p class="help-block">Add your last name</p>
<% end %>
<% if !current_user.email_verified? %>
<%= f.text_field :email, :value => '', class: 'form-control input-lg', placeholder: 'Example: email#me.com -- use your primary work or university address' %>
<p class="help-block">Please confirm your email address. No spam.</p>
<% end %>
</div>
</div>
<div class="actions">
<%= f.submit 'Continue', :class => 'btn btn-primary' %>
</div>
<% end %>
</div>
</div>
</div>
users/authentications view
<div class="container-fluid">
<div class="row">
<div class="col-xs-8 col-xs-offset-2">
<div class="table-responsive" style="margin-left:30px; margin-top:15px">
<table class="table table-bordered">
<tr>
<td><i class="fa fa-facebook"></i></td>
<td>
<% if #user.identities.map(&:provider).include?('facebook') %>
<span class="glyphicon glyphicon-ok"</span>
<% else %>
<%= link_to icon('Connect Facebook', id: 'facebookauth'), user_omniauth_authorize_path(:facebook) %>
<% end %>
</td>
</tr>
<tr>
<td><i class="fa fa-google"></i></td>
<td>
<% if #user.identities.map(&:provider).include?('googleauth') %>
<span class="glyphicon glyphicon-ok"</span>
<% else %>
<%= link_to icon('Connect Google', id: 'googleauth'), user_omniauth_authorize_path(:google_oauth2) %>
<% end %>
</td>
</tr>
<tr>
<td><i class="fa fa-linkedin"></i></td>
<td>
<% if #user.identities.map(&:provider).include?('linkedin') %>
<span class="glyphicon glyphicon-ok"</span>
<% else %>
<%= link_to icon('Connect Linkedin', id: 'linkedinauth'), user_omniauth_authorize_path(:linkedin) %>
<% end %>
</td>
</tr>
<tr>
<td><i class="fa fa-twitter"></i></td>
<td>
<% if #user.identities.map(&:provider).include?('twitter') %>
å <span class="glyphicon glyphicon-ok"</span>
<% else %>
<%= link_to icon('Connect Twitter', id: 'twitterauth'), user_omniauth_authorize_path(:twitter) %>
<% end %>
</td>
</tr>
<tr>
<td>Password</td>
<td>
<% if #user.encrypted_password.present? %>
<span class="glyphicon glyphicon-ok"</span>
<% else %>
<%= form_for(current_user, :as => 'user', :html => { role: 'form'}) do |f| %>
<% if #show_errors && current_user.errors.any? %>
<div id="error_explanation">
<% current_user.errors.full_messages.each do |msg| %>
<%= msg %><br>
<% end %>
</div>
<div class="form-group">
<div class="controls">
<%= f.input :password, hint: ("#{#minimum_password_length} characters minimum" if #validatable), :input_html => { class: 'estimate-password'} %>
</div>
</div>
<% end %>
<div class="actions">
<%= f.submit 'Continue', :class => 'btn btn-primary' %>
</div>
<% end %>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
routes
devise_for :users, #class_name: 'FormUser',
:controllers => {
:registrations => "users/registrations",
# :omniauth_callbacks => "users/authentications"
:omniauth_callbacks => 'users/omniauth_callbacks'
}
# PER SOURCEY TUTORIAL ----------
match '/users/:id/finish_signup' => 'users#finish_signup', via: [:get, :patch], :as => :finish_signup
None of this works. I don't know how to plug it in. I'm not sure whether I'm supposed to include the attributes stored in my identities table in the permitted params in the controller??
The attributes are:
t.integer "user_id"
t.string "provider"
t.string "accesstoken"
t.string "refreshtoken"
t.string "uid"
t.string "name"
t.string "email"
t.string "nickname"
t.string "image"
t.string "phone"
t.string "urls"
I have got this working so a user can authenticate with one method only. I don't know how to get this working. I've tried every resource I can find to figure this out but I'm stuck.
I have this all working with each individual social plugin and email, but what I don't have is the ability to add identities to an existing user (in a current session) so that the next time they login they can use any acceptable identity.
Can anyone help?
Without being able to see all of your code, I just created a shell app that runs with multiple providers. I just followed the steps from the tutorial you mentioned at sourcey. Here is the link to my repo.
You should be able to just clone it and run it by entering your app's keys and secret tokens from facebook, twitter, and linkedin in the devise.rb initializer. To get this to work locally you will need to make sure that the callback url on twitter is set to http://127.0.0.1:3000/.
If you wanted to give a user the option to add their own omniauth account (identity) instead of having it done automatically through app authorization you could just make a form for the user to enter the numeric uid and create the identity your self in the controller or back end like this:
new_identity = Identity.new
new_identity.user_id = "current user's id"
new_identity.provider = "facebook"
new_identity.uid = "0123456789"
new_identity.save!
The user would have to get their numeric uid from the site and enter it themselves.

How do I use set up Twilio in my application to automatically send a text to a number?

I have a Ruby on Rails application where users create a "Reminder" and once it is created and saved to the database (POSTGRESQL), the number which the User inputed on making the reminder should be texted a message (by creating a message using Twilio's REST API). My Twilio credentials are correct and my environment variables are set properly in the application.
The reminder is evidently being created (seen after rails s in the console) but I am not receiving an SMS at all, and I am wondering if it has to do with the syntax in my reminder.rb:
class Reminder < ActiveRecord::Base
validates :title, presence: true
validates :phone_number, presence: true
validates :time, presence: true
belongs_to :user
after_create :send_text_message
##SEND_TEXT_MESSAGE_TIME = 5.minutes
def send_text_message
#twilio_phone_number = ENV['TWILIO_PHONE_NUMBER']
#twilio_client = Twilio::REST::Client.new ENV['TWILIO_ACCOUNT_SID'], ENV['TWILIO_AUTH_TOKEN']
# %k: 24hr time, %M: minute, %p: AM/PM, %b: e.g. "Jan", %d: zero padded day of the month e.g. "01"
numba = "+1"+self.phone_number
time_str = ((self.time).localtime).strftime("%k:%M%p on %b. %d")
body = "Hi. Just a reminder that #{self.title} is coming up at #{time_str}."
message = #twilio_client.account.sms.messages.create(
:from => #twilio_phone_number,
:to => numba,
:body => body,
)
puts message
end
def when_to_run
time - ##SEND_TEXT_MESSAGE_TIME
end
handle_asynchronously :send_text_message, :run_at => Proc.new { |i| i.when_to_run }
end
The form that the user submits is here:
<%= simple_form_for [#user,#reminder] do |f| %>
<% if #reminder.errors.any? %>
<div id="error_expl" class="panel panel-danger">
<div class="panel-heading">
<h3 class="panel-title"><%= pluralize(#reminder.errors.count, "error") %> prohibited this reminder from being saved:</h3>
</div>
<div class="panel-body">
<ul>
<% #reminder.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
</div>
<% end %>
<%= f.input :title , class: "form-control"%>
<%= f.input :phone_number, class: "form-control", input_html: { value: current_user.phone_number } %>
<%= f.input :time, class: "form-control" %>
<%= f.submit nil, :class => "btn btn-primary" %>
<%= link_to 'Cancel', user_reminders_path(current_user), class: "btn btn-danger" %>
<% end %>
and finally, my reminders_controller.rb:
class RemindersController < ApplicationController
def index
#user = User.find_by_id params[:user_id]
#reminders = #user.reminders
if #reminders.length == 0
flash[:notice] = 'You have no reminders scheduled. Create one now to get started.'
end
end
def new
#user = User.find_by_id params[:user_id]
#reminder = #user.reminders.new
end
def create
# Time.zone = reminder_params[:time_zone]
#user = current_user
#reminder = #user.reminders.build reminder_params
respond_to do |format|
if #reminder.save
flash[:success] = 'Reminder was created successfully!'
format.html {
redirect_to user_reminders_path
}
else
flash.now[:notice] = 'Reminder could not be created. Please try again.'
format.html {
render :new
}
format.json {
render json: #reminder.errors.full_messages
}
end
end
end
def show
#reminder = Reminder.find(params[:id])
end
def edit
#reminder = Reminder.find(params[:id])
end
def update
#reminder = Reminder.find(params[:id])
#reminder.user_id = current_user.id
respond_to do |format|
if #reminder.update_attributes(reminder_params)
flash[:success] = 'Reminder has been successfully updated.'
format.html {
redirect_to user_reminders_path(current_user)
}
format.json {
render json: #reminder
}
else
flash[:error] = 'Reminder could not be updated.'
format.html {
redirect_to edit_reminder_path
}
format.json {
render json: #reminder.errors.full_messages
}
end
end
end
def destroy
#reminder = Reminder.find(params[:id])
#reminder.destroy
respond_to do |format|
format.html { redirect_to user_reminders_path(current_user.id), notice: 'reminder was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def reminder_params
params.require(:reminder).permit(:title, :time, :phone_number)
end
end
Am I missing something? I am new to rails and am more used to the MEAN stack, so I am thinking it is maybe my use of variables and the self keyword that are messing things up for me. I am looking for advice on anything I can do or fix to make the Message resource from Twilio's REST API send a text to a number that I input.
Twilio developer evangelist here.
#ajporterfield makes a couple of good suggestions, make sure you're using live, not test, credentials and try making this work without delaying the method.
I have a couple of other questions. Are you running the delayed job process to process the jobs?
Have you upgraded your account? If you are still using a free Twilio account you will need to make sure that all the numbers you send messages to are verified in your account. Once you upgrade your account you can send messages to any number.
Finally, one suggestion: you are currently calling #twilio_client.account.sms.messages.create. The sms.messages endpoint in the API is actually deprecated right now so it is better to just call messages. Also, as a convenience, if you are using the main account, you don't need to call account either. So you can actually shorten that line to: #twilio_client.messages.create and it will work better and look tidier.
Let me know if this helps at all.

How to allow non-user to submit data and save it to new user?

The below pic is from the home page of my website. I'm trying to encourage nil user to create a goal right from the start.
<%= simple_form_for(Goal.new) do |f| %>
<%= f.text_field :name %>
<%= f.text_field :deadline %>
<%= f.submit %>
<% end %>
Upon clicking submit, the nil user should become a guest user and the goal should be created:
def create
#goal = current_user.goals.build(goal_params)
if #goal.conceal == true
#goal.save
redirect_to #goal, notice: 'Goal was successfully created'
elsif #goal.guest == true
#goal.save
redirect_to signup_url, notice: 'Sign up to see your goal!'
elsif
#goal.save
track_activity #goal
redirect_to #goal, notice: 'Goal was successfully created'
else
flash.now[:danger] = 'Required Field: "Enter Goal"'
render 'new'
end
end
And then the guest user should be redirected to the signup page:
<%= form_for(#user) do |f| %>
<%= f.text_field :name %>
<%= f.email_field :email %>
<%= f.password_field :password %>
<%= f.submit %>
<% end %>
Once signed in the data entered as a guest user should have been saved and applied to the new account.
The guest user should then expire in a week:
lib/goals/guests.rake
namespace :guests do
desc "Remove guest accounts more than a week old."
goal :cleanup => :environment do
User.where(guest: :true).where("created_at < ?", 1.week.ago).destroy_all
end
end
user.rb
class User < ActiveRecord::Base
has_many :goals
has_secure_password
def self.new_guest
new { |u| u.guest = true }
end
def move_to(user)
goals.update_all(user_id: user.id)
end
end
migration
rails g migration add_guest_to_users guest:boolean
user_controller
def create
#user = params[:user] ? User.new(params[:user]) : User.new_guest
if #user.save
current_user.move_to(#user) if current_user && current_user.guest?
session[:user_id] = #user.id
redirect_to root_url
else
render "new"
end
end
So what is my question actually?
Is my train of thought correct here or is there another way I should be trying to achieve my aims?
How can we make nil user a guest user upon clicking submit? I got much of my inspiration from this post, but his aims aren't quite the same as mine. He wants to give the user free access to the web app. I just want the non-user to be able to submit a goal as the first part of the signup process.
You can save the user with out validation
def create
#user = params[:user] ? User.new(params[:user]) : User.new_guest
if #user.save validate: false
....
end
end
This way the user does not return error when you know the data is just not right

Ruby on Rails Form Submission Error and Success Messages

I'm creating a landing page and I have two forms on my root page (trying to create a landing page). Very new to ruby on rails so forgive me because I'm going to explain this terribly.
The landing page controller (landing_controller) looks like this:
class LandingController < ApplicationController
def index
#email = Email.new
#design = Design.new
end
end
The emails_controller (emails_controller) looks like this:
class EmailsController < ApplicationController
protect_from_forgery with: :exception
def new
#email = Email.new
end
def create
#email = Email.new(params[email_params])
respond_to do |format|
if #email.save
format.html { redirect_to(root_path, :notice => 'Thank You For Subscribing!') }
format.json { render json: Email.create(email_params) }
else
format.html { redirect_to(root_path)}
format.json { render :json => #email.errors, :status => :unprocessable_entity }
end
end
end
private
def email_params
params.require(:email).permit(:username, :email)
end
end
and the designs controller (designs_controller) looks pretty much the same as emails_controller.
Then I have some validation in the email.rb model:
class Email < ActiveRecord::Base
validates :username, :presence => true
validates :email, :presence => true
end
And again the design.rb looks pretty much the same.
The form I have on the landing page (root_path) index looks like this:
<%= form_for #email, url: emails_path, html: {class: "email-form"} do |f| %>
<% if #email.errors.any? %>
<h3>Error</h3>
<ul>
<% #email.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
<% end %>
<h2>Receive Notifications</h2>
<%= f.text_field :username, :class => 'email-box', :placeholder => "First Name", :autocomplete => :off %>
<%= f.text_field :email , :class => 'email-box', :placeholder => "Email", :autocomplete => :off %>
<%= f.submit "Subscribe", :class => 'email-submit' %>
<p class="info">- We'll update ​you ​when ​we launch our new website</p>
<% end %>
When I submit the form breaking the validation I get no errors and if I submit the form following the validation rules I don't know if it creates a new entry in the database. If anyone can help I'd be very appreciative.
you need to render the landing controller index action rather than redirecting to it. because on redirection, it does #email = Email.new and all the errors are gone for email. try this as create action in your emails controller
def create
#email = Email.new(email_params)
respond_to do |format|
if #email.save
format.html { redirect_to root_path, notice: 'Thank You For Subscribing!' }
format.json { render json: Email.create(email_params) }
else
#design = Design.new
format.html { render "landing/index" }
format.json { render :json => #email.errors, :status => :unprocessable_entity }
end
end
end
for success or errors messages, put this in your application.html.erb
<% if flash[:error].present? %>
<p class='flash-error'><%= flash[:error] %></p>
<% end %>
<% if flash[:notice].present? %>
<p class='flash-notice'><%= flash[:notice] %></p>
<% end %>

Empty argument - form for

I want to implement a reset password functionality so I have followed this railscast, I receive the mail with the link to redirect to an edit password page but I get an error here.
View
<h1>Reset Password</h1>
<%= form_for #user, :url => password_reset_path(params[:id]) do |f| %>
<% if #user.errors.any? %>
<div class="error_messages">
<h2>Form is invalid</h2>
<ul>
<% for message in #user.errors.full_messages %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :password %>
<%= f.password_field :password %>
</div>
<div class="field">
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation %>
</div>
<div class="actions"><%= f.submit "Update Password" %></div>
<% end %>
The error is :First argument in form cannot contain nil or be empty
I'm assuming that #user is empty, I'm new on RoR and I don't know why I get this error
Password Controller
class PasswordResetsController < ApplicationController
def new
render :layout => false
end
def create
user = User.find_by_email(params[:email])
user.send_password_reset if user
redirect_to :connect, :notice => "An E-mail has been send"
end
def edit
render :layout => false
#user = User.find_by_password_reset_token!(params[:id])
end
def update
#user = User.find_by_password_reset_token!(params[:id])
if #user.password_reset_sent_at < 2.hours.ago
redirect_to new_password_reset_path, :alert => "Password ↵
reset has expired."
elsif #user.update_attributes(params[:user])
redirect_to root_url, :notice => "Password has been reset."
else
render :edit
end
end
end
Change your def edit to
def edit
#user = User.find_by_password_reset_token!(params[:id])
render :layout => false
end
you have to add
#user = User.new
to your new method.
you have also another error for your create method. there is no user creation.
class PasswordResetsController < ApplicationController
def new
#user = User.new
render :layout => false
end
def create
#user = User.new user_params
if #user.save
# your code to render success
else
# your code to render error
end
end
private
def user_params
params.require(:user).permit(:email) # add more
end
end
This is the answer to '#user.update_attributes(params[:user])' with forbidden attributes error.
Rails 4 has new feature known as strong parameters.
Change your password controller to:
class PasswordResetsController < ApplicationController
def new
render :layout => false
end
def create
user = User.find_by_email(params[:email])
user.send_password_reset if user
redirect_to :connect, :notice => "An E-mail has been send"
end
def edit
#user = User.find_by_password_reset_token!(params[:id])
render :layout => false
end
def update
#user = User.find_by_password_reset_token!(params[:id])
if #user.password_reset_sent_at < 2.hours.ago
redirect_to new_password_reset_path, :alert => "Password ↵
reset has expired."
elsif #user.update_attributes(user_params)
redirect_to root_url, :notice => "Password has been reset."
else
render :edit
end
end
private
def user_params
params.require(:user).permit(:name, :email_id, :password)
end
end

Resources