Error self.user.update_attribute undefined method nil:Class - ruby-on-rails

UPDATE POST I'm noob in RoR and i start the test. I have an application and I try to use test with rspec and capybara, I want create user and test the login. But when i do my test i have some error with my models users, because in my app I create user and i call an after_create :create_order
I modify my factories.rb but i have an error in my update_attributes
See my model user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
has_many :ratings
has_many :rated_sounds, :through => :ratings, :source => :sounds
has_many :sounds ,:dependent => :destroy
has_many :orders ,:dependent => :destroy
has_many :song_ups ,:dependent => :destroy
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:registerable, :confirmable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me,
:nom, :prenom, :societe, :tel, :cgu, :sign_in_count, :trans_simu,
:trans_limit, :project, :vat_number, :adress, :zip_code, :city, :tutorial
after_create :create_order
def create_order
order = Order.create(user_id: self.id,files_left: 3)
order.subscription = Subscription.where(category: 'test').first || Subscription.create(category: 'test')
self.update_attribute(:trans_limit, 1)
#Ancien Order
# Order.create(user_id: self.id, subscription_id: Subscription.where(category: 'test').first.id, files_left: 3)
# self.update_attribute(:trans_limit, 1)
end
def test?
self.orders.trial.present? && self.orders.count==1
end
def unlimited?
!self.test? && self.orders.current.where(time_left: -1).any?
end
def allow_send?
!self.finish_order? && self.sounds.in_progress.count < self.trans_limit.to_i
end
def finish_order?
self.orders.current.empty?
end
end
For create my user in my test I use FactoryGirl. And i write this :
require 'factory_girl'
FactoryGirl.define do
factory :user do
sequence(:email) {|n| "email#{n}#factory.com" }
password "foobar"
password_confirmation "foobar"
societe "RspecTest"
prenom "John"
nom "Doe"
tel "0101010101"
confirmed_at Time.now
association :order
end
factory :order do
association :subscription
end
factory :subscription do
end
end
And one of my test is :
scenario "User login right" do
visit new_user_session_path
current_path.should == "/users/sign_in"
page.html.should include('<h2>Se connecter</h2>')
user = FactoryGirl.create(:user)
fill_in "Email", :with => user.email
fill_in "Mot de passe", :with => user.password
check "user_remember_me"
click_button "Connexion"
page.should have_content('Mon compte')
current_path.should == root_path
end
My order.rb
class Order < ActiveRecord::Base
attr_accessible :nb_files, :user_id, :period, :time_left, :subscription_id, :promo_id, :promo_end_date, :max_time_file, :files_left, :ended_at
belongs_to :user
belongs_to :subscription
scope :current, where('files_left != ? AND time_left != ? AND (ended_at >= ? OR ended_at IS ?)', 0, 0, Time.now, nil)
before_create :init_value
def self.trial
self.where(subscription_id: Subscription.where(category: 'test').first.id).first
end
def init_value
self.time_left = self.subscription.trans_seconds
self.max_time_file = self.subscription.max_time_file
if self.subscription.category != 'test'
self.user.update_attribute(:trans_limit, 1)
Order.where(user_id: self.user_id, subscription_id: Subscription.where(category: 'test')).destroy_all
else
self.files_left = 3
end
end
end
My error :
Failure/Error: user = FactoryGirl.create(:user)
NoMethodError:
undefined method `trans_seconds' for nil:NilClass
# ./app/models/order.rb:13:in `init_value'
# ./app/models/user.rb:21:in `create_order'
I hope you can help me. Thank's

You don't have Subscription with category 'test' in your database. Solution depend on how you want to handle this kind of error.
If you expect this subscription always to be in your database, use db:seed rake task for prepopulating your db. (try googling it to find out how to do this)
If you don't want to assign any subscription if given doesn't exist try:
def create_order
order = Order.create(user_id: self.id,files_left: 3)
order.subscription = Subscription.where(category: 'test').first
self.update_attribute(:trans_limit, 1)
end
And finally, if you want to create such a subscription if it doesn't exist:
def create_order
order = Order.create(user_id: self.id,files_left: 3)
order.subscription = Subscription.find_or_create_by_category('test')
self.update_attribute(:trans_limit, 1)
end

In create_order try using this:
Order.create!(user: self, subscription: Subscription.where(category: 'test').first, files_left: 3)
Use objects instead of plain ids.
On before_* methods
To make sure that validations pass, return true at the end of these methods. In your case, add return true at the end of the init_value method.

Related

Devise-invitable : Upon inviting a user, 2 emails are being sent, an invitation mail and a mail that says "Thank you for registering with us"

I am using Devise-invitable to send invitations to users using their email addresses. When I send and invitation, one of the mails that is (correctly) sent is one with the invitation link. However, another email gets sent which tells the invitee "Thank you for registering with us.".
I cannot find why this is happening.
Ruby version : 2.3.0p0 (2015-12-25 revision 53290) [x86_64-darwin15]
Rails version : 4.2.4
devise version : 3.5.10
devise_invitable version : 1.6.0
Code in invitations controller:
class InvitationsController < Devise::InvitationsController
def create
params[:user][:email].each do |email|
User.invite!({:email => email}, current_user) #If I comment out this line, both emails are not sent
end
redirect_to root_path
end
end
Code in User.rb
class User < ActiveRecord::Base
# after_create :send_welcome_email
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :invitable, :omniauthable, :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable
attr_accessor :user_profile_phone, :user_profile_city
# Relationships
has_many :identities
has_many :orders
belongs_to :role, :dependent => :destroy
belongs_to :userdatum, :dependent => :destroy
belongs_to :userprofile, :dependent => :destroy
has_many :comments
has_many :deliveries
has_many :conversations, :dependent => :destroy
# Filters
before_save :assign_role
after_create :send_welcome_mail
def send_welcome_mail
UserMailer.registration(self).deliver_now
end
def assign_role
self.role = Role.find_by name: "User" if self.role.nil?
end
def facebook
identities.where( :provider => "facebook" ).first
end
def facebook_client
#facebook_client ||= Facebook.client( access_token: facebook.accesstoken )
end
def google_oauth2
identities.where( :provider => "google_oauth2" ).first
end
def google_oauth2_client
if !#google_oauth2_client
#google_oauth2_client = Google::APIClient.new(:application_name => 'Test', :application_version => "1.0.0" )
#google_oauth2_client.authorization.update_token!({:access_token => google_oauth2.accesstoken, :refresh_token => google_oauth2.refreshtoken})
end
#google_oauth2_client
end
def admin?
self.role.name == "Admin"
end
def sytlist?
self.role.name == "Stylist"
end
def user?
self.role.name == "User"
end
end
You are sending both emails yourself.
User.invite! sends out an invitation email by default.
You need to remove the after_create :send_welcome_mail in user.rb. That's what is sending out the "welcome email".
Solution I used was to check for token here :
after_create :send_welcome_email
def send_welcome_mail
if self.invitation_token.nil?
UserMailer.registration(self).deliver_now
end
end
and add
def accept_invitation!
super
UserMailer.registration(self).deliver_now
end
Not sure if this is the best but it solved my problem.

Factory Girl associated object instantiation [using devise]

I have three models user (author), which is incorporating devise logic:
app/models/user.rb
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 :questions
has_many :answers
end
question:
app/models/question.rb
# Model for Question
class Question < ActiveRecord::Base
has_many :answers, dependent: :destroy
belongs_to :author, class_name: 'User', foreign_key: 'user_id'
validates :title, presence: true, length: { maximum: 100 }
validates :body, presence: true, length: { minimum: 10 }
validates :author, presence: true
end
and answer:
app/models/answer.rb
# Model for Answer
class Answer < ActiveRecord::Base
belongs_to :question
belongs_to :author, class_name: 'User', foreign_key: 'user_id'
validates :body, presence: true, length: { minimum: 10 }
validates :question_id, presence: true
validates :author, presence: true
end
and their factories:
spec/factories/users.rb
FactoryGirl.define do
sequence :email do |n|
"email-#{n}#example.com"
end
sequence :password do |n|
"testpassword#{n}"
end
factory :user, aliases: [:author] do
email
# tried sequence generator and fixed password - both have no impact on result
# password '1234567890'
# password_confirmation '1234567890'
password
end
end
spec/factories/answers.rb
FactoryGirl.define do
factory :answer do
body 'Answer Body'
author
question
end
factory :nil_answer, class: 'Answer' do
question
body nil
end
end
spec/factories/questions.rb
FactoryGirl.define do
factory :question do
title 'Question Title'
body 'Question Body'
author
factory :question_with_answers do
after(:create) do |question|
# changing create_list to create has no impact on result
# create_list(:answer, 2, question: question)
create(:answer, question: question)
end
end
end
end
test code:
spec/features/delete_answer_spec.rb
require 'rails_helper'
feature 'Delete answer', %q{
By some reason
As an authenticated user
I want to delete answer
} do
given(:question) { create(:question_with_answers) }
given(:user) { create(:user) }
given(:ans) { create(:answer) }
scenario 'Answer author password should not be nil' do
expect(question.answers.first.author.password).to_not be_nil
# question.author.password and ans.author.password return not nil
# I need password to do:
# visit new_user_session_path
# fill_in 'Email', with: user.email
# fill_in 'Password', with: user.password
# click_on 'Log in'
end
end
Can anyone explain why the following given statement:
given(:question) { create(:question_with_answers) }
creates question object that:
question.author.password #=> '1234567890'
but:
question.answers.first.author.password #=> nil
why method "create" instantiates author of question properly (field password is set), but "create_list" inside "after" callback creates author in answer with nil fields?
rails 4.2.5, ruby 2.3.0, devise 3.5.6, warden 1.2.6, factory_girls_rails 4.6.0 (4.5.0)
Devise (and most authentication libraries) encrypt the password and don't allow you to access passwords from models retrieved from the database. The password may be temporarily available through an in-memory reader method, but won't be available if you retrieve the record from the database.
If you do:
user = User.new(password: "example")
p user.password
I'm guessing you'll see "example".
But if you do:
user = User.first
p user.password
I bet you'll see nil (assuming you have user records in your database).
When you query an association proxy like question.answers.first.author, it's going to the database again to find the answer and author. That means you're using a different instance, which no longer has the password available.

Factory_girl create gives validation error

I'm trying to test a destroy action in my rails application. I use Factory_girl to create objects.
When creating a company object it's mandatory to add a user relation. But this is where I get a fail when I try to create a Company with Factory_girl.
user_spec.rb
describe User do
before(:each) { #user = User.new(email: 'user#example.com') }
subject { #user }
it { should respond_to(:email) }
it { should respond_to(:companies) }
it "#email returns a string" do
expect(#user.email).to match 'user#example.com'
end
describe "company associations" do
let!(:a_company) do
FactoryGirl.create(:company, user: #user)
end
it {should have_many :companies, :dependent => :destroy}
end
end
factory.rb
FactoryGirl.define do
factory :user do
confirmed_at Time.now
name "Test User"
email "test#example.com"
password "please123"
trait :admin do
role 'admin'
end
end
factory :company do
name "Total Inc."
user :user
end
end
model/user.rb
class User < ActiveRecord::Base
has_many :companies, dependent: :destroy
enum role: [:user, :vip, :admin]
after_initialize :set_default_role, :if => :new_record?
def set_default_role
self.role ||= :user
end
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :invitable, :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable
end
model/company.rb
class Company < ActiveRecord::Base
belongs_to :user
validates :user_id, presence: true
end
The error I get:
Failures:
1) User company associations
Failure/Error: FactoryGirl.create(:company, user: #user)
ActiveRecord::RecordInvalid:
Validation failed: User can't be blank
EDIT
I followed the advise from below and now I can create but get following error:
Failure/Error: it {should have_many :companies, :dependent => :destroy} expected #<User:0x007fc7b7ce08c0> to respond to has_many?
The first error, Validation failed: User can't be blank, is a result of not having saved the #user object. You can fix the error by saving the object before you call create:
let!(:a_company) do
#user.save
FactoryGirl.create(:company, user: #user)
end
The second error, Failure/Error: it {should have_many :companies, :dependent => :destroy} expected #<User:0x007fc7b7ce08c0> to respond to has_many? is a testing error - the production code works fine. To fix your test try one of the following options:
Use Shoulda
it {should have_many(:companies).dependent(:destroy)}
Use FactoryGirl
it 'Expects user to have many companies' do
expect{#user.companies}.to_not raise_error
end

Have user follow/take course - my method is wrong

I am getting an rspec error when I have a user try to take/follow a course. I believe there must be something wrong with my method because it returns nil in the rspec test. There is also an assignment model between the course and user models. What is wrong?
The Error
1) User taking course
Failure/Error: it { should be_taking_course(course)}
expected taking_course?(#<Course id: 1, title: "Ze Finance Course 1", description: "Description for course 1", typeof: nil, created_at: "2013-06-25 21:38:24", updated_at: "2013-06-25 21:38:24">) to return true, got nil
# ./spec/models/user_spec.rb:125:in `block (3 levels) in <top (required)>'
My User Model
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 :name, :email, :password, :password_confirmation, :remember_me
has_many :assignments, foreign_key: "user_id", dependent: :destroy
has_many :courses, through: :assignments
def taking_course?(course)
assignments.find_by_course_id(course.id)
end
def take_course!(course)
assignments.create!(course_id: course.id)
end
end
User spec
require 'spec_helper'
describe User do
before(:each) do
#attr = {
:name => "Example User",
:email => "user#example.com",
:password => "changeme",
:password_confirmation => "changeme"
}
end
describe "taking course" do
let(:course) {FactoryGirl.create(:course)}
before do
#user = User.new(#attr)
#user.save
#user.take_course!(course)
end
it { should be_taking_course(course)} //this is the problem test
end
end
it { should ... } is shorthand it { subject.should ... } but you are not setting up an #user rather than the subject. So RSpec will create an implicit subject which is just User.new.
You can probably fix your test by setting an explicit subject in much the same way you set up #user:
describe "taking course" do
let(:course) {FactoryGirl.create(:course)}
subject { User.create(#attr) }
before do
subject.take_course!(course)
end
it { should be_taking_course(course) }
end
If you don't want to do that then make your assertion more explicit:
it { #user.should be_taking_course(course) }

Factory_girl simple association between two models

FactoryGirl.define do
factory :agency do
name "Example Inc"
available_items "20"
recruiter # recruiter.id
end
factory :recruiter do
email 'example#example.com'
password 'please'
password_confirmation 'please'
# required if the Devise Confirmable module is used
# confirmed_at Time.now
end
end
agency.rb
class Agency < ActiveRecord::Base
belongs_to :recruiter
validates :name, :presence => true
end
recruiter.rb
class Recruiter < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me
attr_accessible :agency_attributes, :first_name
has_one :agency, :dependent => :destroy
accepts_nested_attributes_for :agency
validates :email, :presence => true
end
authentication_steps.rb
def create_user
#recruiter = FactoryGirl.create(:recruiter)
end
How can I replicate this Recruiter & Agency association using factory_girl?
I think you should remove recruiter from agency factory and add agency to requiter factory
FactoryGirl.define do
factory :agency do
name "Example Inc"
available_items "20"
factory :agency_without_recuiter do
recuiter_id = 1
end
factory :agency_with_recuiter do
recuiter
end
end
factory :recuiter do
email 'example#example.com'
password 'please'
password_confirmation 'please'
factory :recuiter_with_agency
agency
end
end
end
This should work from both sides
create(:agency).recuiter => nil
create(:agency_with_recuiter).recuiter => recuiter
create(:recuiter).agency => nil
create(:recuiter_with_agency).agency => agency
Hope it will be usefull. Good luck!
I think you have to replicate it in your test cases, not in FG itself.
before (:each) do
#recruiter = FactoryGirl.create(:recruiter)
#agency = FactoryGirl.create(:agency)
#agency.recruiter = #recruiter
end
Something like this.

Resources