Rails 4 Active Record Login Session Failing - ruby

I'm fairly new to Ruby on Rails so apologize if this is a simple problem, but after weeks of searching for a solution, I felt it might just be easier to ask.
I'm working on a Rails 4 site where I want to have active_record based authentication. I modeled the sign up and login process after this example: http://railscasts.com/episodes/250-authentication-from-scratch?view=asciicast
This example works fine if I use cookies for my session storage, but breaks somewhere in the background when I switched it to active_record. When I try to login, it just takes me back to the main page with no flash message and nothing in my current_user (though I have done a test where I render new on the login page instead of redirect and it can find my user info, but as soon as I navigate away, I lose the session)
The cookie session won't work due to file size limitations, but we're open to other options. I've set up the initializers to point to active_records and I've added it to the gemfile, but I can't seem to figure out where it's breaking. Am I missing an insert step somewhere to add it to the db?
Another possible clue is that my protect_from_forgery line gives me a "Can't verify CSRF token authenticity" but the session still fails if I comment out that line.
I apologize if this is a very simple fix, but like I mentioned, I've been searching for a solution for a while now.
Below is the main code running it. If you'd like to see any more of the code, just let me know.
application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :null_session
helper_method :current_user
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
end
session_controller.rb
class SessionsController < ApplicationController
def new
end
def create
user = User.authenticate(params[:email], params[:password])
if user
session[:user_id] = user.id
redirect_to root_url, :notice => "Logged in! #{User.find(session[:user_id]).email}"
else
flash.now.alert = "Invalid email or password"
render "new"
end
end
def destroy
session[:user_id] = nil
flash[:notice] = "Logged out!"
redirect_to root_url #, :notice => "Logged out!"
end
end
views/sessions/new.html.erb
<h1>Log in</h1>
<%= form_tag sessions_path do %>
<p>
<%= label_tag :email %><br />
<%= text_field_tag :email, params[:email] %>
</p>
<p>
<%= label_tag :password %><br />
<%= password_field_tag :password %>
<%= hidden_field_tag('authenticity_token', form_authenticity_token.to_s)%>
</p>
<p class="button"><%= submit_tag "Log in" %></p>
<% end %>
models/user.rb
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation
attr_accessor :password
before_save :encrypt_password
validates_confirmation_of :password
validates_presence_of :password, :on => :create
validates_presence_of :email, :on => :create, :message => "Can't be blank"
validates_uniqueness_of :email
def self.authenticate(email, password)
user = find_by_email(email)
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
user
else
nil
end
end
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
end

Of course I would stumble upon a fix the next day after I post, but I wanted to share what I found in case others have the same problem. I started recreating the whole project from the example I shared to see if I was missing a step. When I ran that example, it started giving me a null exception, saying it was trying to create a session with no data. I took a look at the active_record github site and found this link in the "issues" section
https://github.com/rails/activerecord-session_store/issues/6
That fixed the null ref I was getting and when I plugged it into my main site it appears to have fixed the problem (or at least it doesn't log me out when I change pages). Not sure how it actually fixed it, but I'll take what I can get.
initializers/session_store.rb
ActiveRecord::SessionStore::Session.attr_accessible :data, :session_id

Related

NoMethodError in Users#unsubscribe

I am working on implementing unsubscribe link to my rails mailer. Unfortunately, my code breaks with this:
NoMethodError in Users#unsubscribe - undefined method `unsubscribe_hash' for nil:NilClass
which points to /app/views/users/unsubscribe.html.erb line #3
<h4>Unsubscribe from Mysite Emails</h4>
<p>By unsubscribing, you will no longer receive email...</p>
<%= simple_form_for(#user, unsubscribe_path(id: #user.unsubscribe_hash)) do |f| %>
<%= f.hidden_field(:subscription, value: false) %>
<%= f.submit 'Unsubscribe' %>
<%= link_to 'Cancel', root_url %>
<% end %>
my user_controller is as shown below
class UsersController < ApplicationController
protect_from_forgery
def new
#user = User.new
end
def create
#user = User.new(secure_params)
if #user.save
flash[:notice] = "Thanks! You have subscribed #{#user.email} for Jobs Alert."
else
flash[:notice] = 'Error Subscribing! Kindly check your email and try again.'
end
redirect_to root_path
end
def unsubscribe
user = User.find_by_unsubscribe_hash(params[:unsubscribe_hash])
#user = User.find_by_unsubscribe_hash(user)
end
def update
#user = User.find(params[:id])
if #user.update(secure_params)
flash[:notice] = 'Subscription Cancelled'
redirect_to root_url
else
flash[:alert] = 'There was a problem'
render :unsubscribe
end
end
private
def secure_params
params.require(:user).permit(:email, :subscription)
end
end
Route.rb
resources :users, only: [:new, :create]
get 'users/:unsubscribe_hash/unsubscribe' => 'users#unsubscribe', as: :unsubscribe
patch 'users/update'
user.rb
class User < ActiveRecord::Base
before_create :add_unsubscribe_hash, :add_true_to_users_table
validates :email, :uniqueness => true
validates_presence_of :email
validates_format_of :email, :with => /\A[-a-z0-9_+\.]+\#([-a-z0-9]+\.)+[a-z0-9]{2,4}\z/i
private
def add_unsubscribe_hash
self.unsubscribe_hash = SecureRandom.hex
end
def add_true_to_users_table
self.subscription = true
end
end
unsubscribe link in the email which calls unsubscribe action
# app/views/job_notifier/send_post_email.html.erb
...
<%= link_to "Unsubscribe", unsubscribe_url(id: #unsubscribe) %>.
diagrammatic view of the error
Its seems that I am missing something, do I need to define something in my users_controller? I have never in my life being able to solve NoMethodError or I don't understand what it's all about.
You get that error because #user (set in UsersController#unsubscribe) is nil. That is what the "for nil:NilClass" in undefined methodunsubscribe_hash' for nil:NilClass` is referring to.
This method doesn't seem correct:
def unsubscribe
user = User.find_by_unsubscribe_hash(params[:unsubscribe_hash])
#user = User.find_by_unsubscribe_hash(user)
end
You are looking up a user by unsubscribe_hash and assigning it to user, and then looking up user by unsubscribe_hash again but passing in user as the value to find_by_unsubscribe_hash.
I believe something like this is more as intended:
def unsubscribe
#user = User.find_by_unsubscribe_hash(params[:unsubscribe_hash])
end
Whenever you see any error messages in ruby of the format:
undefined method 'method_name' for nil:NilClass
you are being told you are trying to call something on a nil object and so the focus of your attention should be on why is that object nil. You are also told in your log where the error occurs - in your case it refers to the line, which refers to #user in #user.unsubscribe_hash in your form declaration.
So #user is nil and in this case it's nil because your controller responsible for rendering the form isn't setting #user:
user = User.find_by_unsubscribe_hash(params[:unsubscribe_hash])
#user = User.find_by_unsubscribe_hash(user)
Now quite why you are attempting to find the user and then pass that user into the second line to find #user is beyond me, but anyway the real issue is that you have no user that matches params[:unsubscribe_hash]
So the issue is related to whatever is invoking your unsubscribe action ... you have neglected to add that to your question so I cannot help with that but that is where your focus start.

how to resolve NoMethodError in ContactsController#index in ROR-4

Please help me to resolve this error and reedit my all pages. Actually i am new to Ruby on Rails and i am using rails version-4 and ruby version-1.9.3.I want to show one form including select options and selected value saved in DB. My errors and code snippets explained below.
Error:
undefined method `email_providers=' for #<Class:0x4e68df0>
Extracted source (around line #2):
1 class Contact < ActiveRecord::Base
2 self.email_providers = %w[Gmail Yahoo MSN]
3 validates :email_provider, :inclusion => email_providers
4 end
views/contacts/index.html.erb
<%= form_for #contact,:url => {:action => "create"} do |f|%>
<%= f.text_field:gmail %>
<%= f.select :email_provider, options_for_select(Contact.email_providers, #contact.email_provider) %>
<%= f.submit "Submit"%>
<% end %>
controller/contacts_controller.rb
class ContactsController < ApplicationController
def index
#contact=Contact.new
end
def create
end
end
models/contact.rb
class Contact < ActiveRecord::Base
self.email_providers = %w[Gmail Yahoo MSN]
validates :email_provider, :inclusion => email_providers
end
migrate/20141222061313_create_contacts.rb
class CreateContacts < ActiveRecord::Migration
def change
create_table :contacts do |t|
t.string :gmail
t.string :yahoo
t.string :msn
t.timestamps
end
end
end
I want to show the 3 content(gmail,yahoo,msn) in option drop down list and while it will be selected and clicked on submit button it will be saved in DB.Please help me to edit the code.Thanks in advance..
Change
self.email_providers = %w[Gmail Yahoo MSN]
validates :email_provider, :inclusion => email_providers
in your Contact model class to:
EMAIL_PROVIDERS = %w{Gmail Yahoo MSN}
validates :email_provider, inclusion: {in: EMAIL_PROVIDERS}
and the error should be fixed.
As you can guess, your Contact class doesn't have a self.email_providers= method. So trying to assign a value to it through this method will crash. What I've done is created a constant that can be easily accessed within the class through EMAIL_PROVIDERS and outside the class through Contact::EMAIL_PROVIDERS

has_many and belongs_ association rails 4

I am while leraning how to use association in rails 4 application
I have a user having many opinions and I want to add user opinion in the book show page
This is how i proceed:
my user.rb
class User < ActiveRecord::Base
has_one :panier
has_many :opinions
end
opinion.rb
class Opinion < ActiveRecord::Base
belongs_to :user
end
views/books/show.html.erb
<h2>Votre opinion nous intéresse:</h2>
<%= form_for([#user, #user.opinions.build]) do |f| %>
<p>
<%= f.label :body, 'votre opinion' %><br>
<%= f.text_area :body %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
opinion_controller.rb
class OpinionsController < ApplicationController
def create
#user = current_user
#opinion= #user.opinion.create(opinion_params)
end
private
def opinion_params
params.require(:opinion).permit(:body)
end
end
and in books_controllers this is my show method:
def show
#user= current_user
#books= Book.all
end
my routes:
get 'books/show' => 'books#show' , as: :books_list
resources :users do
resources :opinions
end
what I got as error:
undefined method `opinions' for nil:NilClass
in this line of code:
Most probably #user.opinions in your form causing this issue. check whether current_user returning object or not.
Also in your create method there is typo(#user.opinion), it should be #user.opinions.
Use accept nested attributes for same.

Rails3 association and accepts_nested_attributes_for failing when validation of user_id on

I am following the Rails Tutorial doing a little project for myself and then try to progress.
Well I am facing a problem for which I found the solution, but I'll really appreciate any thoughts and opinions because to make it work I have to disable a validation in an association.
The context is as follow : the application (big name for what it does actually...) has users to track their weight. I would like that when a user sign up, he/she enters a first measure on the go.
So here are the simplified models:
User.rb
class User < ActiveRecord::Base
attr_accessible :email, :name, :password, :password_confirmation, :measures_attributes
has_secure_password
has_many :measures, dependent: :destroy
accepts_nested_attributes_for :measures
# here goes validations before_save, etc. taken from the Rails Tutorial
end
Measure.rb
class Measure < ActiveRecord::Base
attr_accessible :weight
belongs_to :user
# This is kind where the problem is...
# If I deactivate the validation for user_id everyhing goes fine
validates :user_id, presence: true
validates :weight, presence: true, numericality: { greater_than: 0 }
default_scope order: 'measures.created_at ASC'
end
Here is the Users controller
class UsersController < ApplicationController
# GET /users/new
def new
#user = User.new
#user.measures.build
end
# POST /users
def create
#user = User.new(params[:user])
if #user.save
sign_in #user
flash[:success] = "Hi #{#user.name}. Welcome !"
redirect_to #user
else
render :new
end
end
end
Here is the form partial for the user :
<%= form_for(user) do |user_form| %>
<%= render 'shared/error_messages', object: user_form.object %>
<%= render 'users/fields', user_builder: user_form %>
<%= user_form.fields_for :measures do |measure_fields| %>
<%= render 'measures/fields', measure_builder: measure_fields, full: true %>
<% end %>
<%= user_form.submit submit_text, class: 'btn btn-large btn-primary' %>
<% end %>
And even when I fill the form correctly, I get this error :
* Measures user can't be blank
The only way I found to make it work is to get rid of the :user_id validation for presence in the Measure model. I want to emphasize that when this validation is off, the user is saved, the measure is saved and correctly associated with the newly created user.
Am I doing something wrong ? Is the :user_id presence validation in the Measure model really useful (it is in the Rails Tutorial and it makes perfectly sense for me) ? If yes why is the Measure validation failing when it is on ?
Thanks a lot in advance.
You could try validates :user instead of user_id. Then it might work out that the two are associated in memory even though the User hasn't been saved yet.
I think I may have found an error in your code that would cause the error you're seeing.
In your new controller you specifically create an association:
#user.measures.build
In your create controller, you create a new element based on the fields that were passed in.
#user = User.new(params[:user])
There are some cases where the associated element will be dropped. I can't remember if this happens by default if it doesn't validate or if there was an option you need to set to make it drop.
I think you may need to add
#user.measure.build if #user.measures.empty?
in your create controller else clause before render :new.
I don't know if you're actually hitting this problem, but it would explain why the association to your User wasn't set.

How to save to Database with associations in rails protecting mass assignment

After trying for few hours I can not save to the database.
The context is this:
I have two types of users, one for that I only need very basic information [Username, email, password] and another kind of user for who I need a lot of information [age, gender, city and so on]
I did not use STI becouse of the vast quantity of Null values there would be in the table.
So I created this three modes in which a user has a profile (profiles table) or not depending of its type [1 or 2], and a field of this profile is the city this user is living in, that relates to another table in the DB, the cities table
class User < ActiveRecord::Base
has_one :profile
has_one :city, through: :profile
end
class Profile < ActiveRecord::Base
belongs_to :user
belongs_to :city
[...a bunch of fields here]
end
class City < ActiveRecord::Base
has_many :profiles
has_many :users, through: :profiles
end
When I play with them in the rails console everything goes OK:
usr = User.new(name: "roxy", email: "roxy#example.me", password: "roxanna", password_confirmation: "roxanna", utype: 1)
cty = City.new(name: "Bucaramanga")
prf = Profile.new (rname: "Rosa Juliana Diaz del Castillo"...)
prf.city = cty
usr.profile = prf
usr.valid?
=> true
usr.save
=> true
but when I try to save in the app (View an Model)
<%= f.label :city, "En que ciudad te encuentras?"%>
<%= select_tag :city, options_from_collection_for_select(City.all, 'id', "name"),{:prompt => 'Selecciona tu ciudad'}%>
def new
#profile = Profile.new
end
def create
#profile = params[:profile]
#city= City.find_by_id(params[:city].to_i)
#profile.city = #city
end
I get this error:
undefined method `city=' for #<ActiveSupport::HashWithIndifferentAccess:0xa556fe0>
Can someone please help me?
UPDATE
As David suggested I created the Profile object in the first line of the create method, so my controller now look like this:
def create
#profile = Profile.new(params[:profile])
#city= City.find_by_id(params[:city].to_i)
#profile.city = #city
#usr = current_user
if #usr.profile.exists? #profile
#usr.errors.add(:profile, "is already assigned to this user") # or something to that effect
render :new
else
#usr.profile << #profile
redirect_to root_path
end
end
But I'm getting this error now
undefined method `exists?' for nil:NilClass
current_user returns the #current_user
def current_user
#current_user ||= User.find_by_remember_token(cookies[:remember_token])
end
Could you tell me please, what am I doing wrong?
I want to write this to all of you who are beginning as well as I am and are stuck in this step.
I had to create a new project and play with it to realize what I was doing wrong. I figured out that I was validating a last time field I added to the Profiles table and had
# education :string(255) not null
but I had not added it yet to the form so the error launched is:
Failed to save the new associated so_profile.
Now, you know if you got this error, go check your schema and look for NOT_NULL fields you might be missing in the form, also you can comment out all your model validations and after it's working uncomment'em to be sure.
So, my Final Models:
class User < ActiveRecord::Base
has_one :profile
has_one :city, through: :profile
attr_accessible :email, :name
end
class Profile < ActiveRecord::Base
belongs_to :user
belongs_to :city
attr_accessible :age, :fcolor, :gender
end
class City < ActiveRecord::Base
has_many :profiles
has_many :users, through: :profiles
attr_accessible :name
end
My controllers:
class ProfilesController < ApplicationController
def new
#user = User.find_by_id(params[:id])
#profile = Profile.new
end
def create
#profile = Profile.new(params[:profile])
city = City.find_by_id(params[:city])
#profile.city = city
#user = User.find_by_id(params[:userid])
#user.profile = #profile
if #user.save
flash[:success] = "Guardado"
redirect_to profile_path(id: #user.id)
end
end
def show
#user = User.find(params[:id])
end
end
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
flash[:success] = "Registrado!"
redirect_to new_profile_path(id: #user.id)
else
flash[:error] = "No Registrado :("
redirect_to new
end
end
def show
#user = User.find_by_id(params[:id])
end
end
In a real app you have to use Cookies or something else to keep the session alive and therefore the user_token from where you get the user_id, but it works to play with associations.
The views:
profiles/new.html.erb
<%= #user.name %>
<%= form_for #profile, url: {action: :create, userid: #user.id } do |f| %>
<%= f.label :age, "Edad" %>
<%= f.text_field :age%> <br />
<%= label :city, "Ciudad"%>
<%= select_tag :city, options_from_collection_for_select(City.all, 'id', 'name')%>
<%= f.submit %>
<% end %>
profiles/show.html.erb
Hello <%= #user.name %><br />
Tu edad es: <%= #user.profile.age %><br />
Vives en <%= #user.profile.city.name%>
users/new.html.erb
<%= form_for #user do |f|%>
<%= f.label :name, "Nombre"%>
<%= f.text_field :name, size: 20, placeholder: "Escribe tu nombre aqui" %><br />
<%= f.label :email, "Email"%>
<%= f.text_field :email, size: 20, placeholder: "Escribe tu email aqui" %><br />
<%= f.submit "Sign me up!"%>
users/show.html.erb
Name: <%= #user.name %><br />
Email: <%= #user.email %>
And that's it!
Cheers.
Learn to read the error messages. The problem is that #profile is a hash because you didnt't actually create a new Profile object on the first line of the create method.
I think that the correct is
#so_profile.City
not
#so_profile.city
Because the class name is City

Resources