How to bypass validations when creating a rails object? - ruby-on-rails

I have following rake task to populate my dev database with dummy data. I get an error that my weight and reps can't be blank in the make_contest block which is weird since these fields are not available in the Contest model.
I think it has to do with my "has_many :contests, through: :submissions" relation? How do I fix this?
Error
ActiveRecord::RecordInvalid: Validation failed: Reps can't be blank, Weight can't be blank
/home/christoph/Documenten/kratos/lib/tasks/populate.rake:62:in `block (2 levels) in make_contests'
/home/christoph/Documenten/kratos/lib/tasks/populate.rake:62:in `block in make_contests'
/home/christoph/Documenten/kratos/lib/tasks/populate.rake:58:in `times'
/home/christoph/Documenten/kratos/lib/tasks/populate.rake:58:in `make_contests'
/home/christoph/Documenten/kratos/lib/tasks/populate.rake:15:in `block (2 levels) in <top (required)>'
Tasks: TOP => db:pcontests
Does anybody knows what could be the reason?
rake task
def make_users
User.create!(
firstname: "Christoph",
name: "Geypen",
username: "chris88",
bio: "He is the best!",
email: "test#test.be",
password: "testtest",
password_confirmation: "testtest"
)
15.times do |n|
firstname = Faker::Name.first_name
name = Faker::Name.last_name
username = Faker::Internet.user_name
bio = Faker::Lorem.sentence
email = "example-#{n+1}#railstutorial.org"
password = "longpassword"
User.create!(
firstname: firstname,
name: name,
username: username,
bio: bio,
email: email,
password: password,
password_confirmation: password,
data_source: "rake_populate"
)
end
end
def make_contests
users = User.all.limit(5)
10.times do
name = Faker::Team.creature
description = Faker::Lorem.sentence
users.each { |user| user.contests.create!(
name: name,
description: description,
admin_id: user.id,
ctype_id: 1,
data_source: "rake_populate"
) }
end
end
def make_submissions
users = User.all.limit(15)
contests = Contest.where(data_source: "rake_populate")
contests.each do |contest|
users.each do |user|
contest.submissions.create!(
reps: rand(1..20),
weight: rand(100..350),
user_id: user.id,
contest_id: contest.id,
data_source: "rake_populate"
)
end
end
end
model validations
contest
class Contest < ActiveRecord::Base
has_many :submissions
has_many :users, through: :submissions
belongs_to :admin, class_name: 'User', foreign_key: 'admin_id'
belongs_to :ctype
validates_presence_of :name, :admin_id, :ctype_id
submission
class Submission < ActiveRecord::Base
belongs_to :user
belongs_to :contest
validates_presence_of :user_id, :reps, :weight
validates_uniqueness_of :tonnage, scope: [:user_id, :contest_id]
before_validation :calculate_tonnage
# model helpers
def calculate_tonnage
self.tonnage = self.weight * self.reps if weight && reps
end
user
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
devise :omniauthable, omniauth_providers: [:facebook]
has_many :submissions
has_many :contests, through: :submissions
has_one :contest
My create action in the controller looks as following. I am just trying to recreate this using the rake task.
def create
#contest = current_user.contests.new(contest_params)
#contest.admin_id = current_user.id
#contest.save
redirect_to contest_submissions_path(#contest)
end

I fixed it by replacing the create with new and saving afterwards. The following question is WHY does that work and create does not?
def make_contests
users = User.all.limit(5)
10.times do
name = Faker::Team.creature
description = Faker::Lorem.sentence
users.each do |user|
x = user.contests.new(
name: name,
description: description,
admin_id: user.id,
ctype_id: 1,
data_source: "rake_populate")
x.save
end
end
end
UPDATE
aannnnddd problem solved
So create instantiates the new object, validates it, and then saves it to the database. And new only creates the local object but does not attempt to validate or save it to the DB.
https://stackoverflow.com/a/2472416/2785289

Related

Error with `check_validity!'

I'm getting this error when trying to migrate. I've looked into my user.rb but I don't see how validate_format_of is causing an error. Below is my user.rb and error log:
ArgumentError: Either :with or :without must be supplied (but not both)
/Users/admin/.rvm/gems/ruby-2.2.0/gems/activemodel-4.1.6/lib/active_model/validations/format.rb:17:in `check_validity!'
/Users/admin/.rvm/gems/ruby-2.2.0/gems/activemodel-4.1.6/lib/active_model/validator.rb:157:in `initialize'
/Users/admin/.rvm/gems/ruby-2.2.0/gems/activemodel-4.1.6/lib/active_model/validations/with.rb:89:in `new'
/Users/admin/.rvm/gems/ruby-2.2.0/gems/activemodel-4.1.6/lib/active_model/validations/with.rb:89:in `block in validates_with'
/Users/admin/.rvm/gems/ruby-2.2.0/gems/activemodel-4.1.6/lib/active_model/validations/with.rb:88:in `each'
/Users/admin/.rvm/gems/ruby-2.2.0/gems/activemodel-4.1.6/lib/active_model/validations/with.rb:88:in `validates_with'
/Users/admin/.rvm/gems/ruby-2.2.0/gems/activemodel-4.1.6/lib/active_model/validations/format.rb:109:in `validates_format_of'
/Users/admin/Documents/workspace/ruby_on_rails/zoan/app/models/user.rb:19:in `<class:User>'
/Users/admin/Documents/workspace/ruby_on_rails/zoan/app/models/user.rb:1:in `<top (required)>'
Model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
TEMP_EMAIL_PREFIX = 'main#gmail'
TEMP_EMAIL_REGEX = /\Amain#gmail/
attr_accessor :login
self.per_page = 20
extend FriendlyId
friendly_id :username, use: [:slugged, :finders]
devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable
validates :name, presence: true, length: { maximum: 100 }
validates :username, presence: true, length: { maximum: 20 }, :uniqueness => { case_sensitive: false }
validates_format_of :email, on: :update
has_many :tweets
has_many :relationships
has_many :friends, through: :relationships
has_many :inverse_relationships, class_name: "Relationship", foreign_key: "friend_id"
has_many :inverse_friends, through: :inverse_relationships, source: :user
has_many :favorites
has_many :votes
has_many :retweets, foreign_key: "retweeter_id"
mount_uploader :avatar, AvatarUploader
mount_uploader :cover, CoverUploader
# def self.find_for_database_authentication(warden_conditions)
# conditions = warden_conditions.dup
# if login = conditions.delete(:login)
# where(conditions).where(["lower(username) = :value OR lower(email) = :value", { :value => login.downcase }]).first
# else
# where(conditions).first
# end
# end
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
# Create the user if needed
if user.nil?
# 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
user = User.where(:email => email).first if email
# Create the user if it's a new registration
if user.nil?
user = User.new(
name: auth.extra.raw_info.name,
#username: auth.info.nickname || auth.uid,
email: email ? email : "#{TEMP_EMAIL_PREFIX}-#{auth.uid}-#{auth.provider}.com",
password: Devise.friendly_token[0,20]
)
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 && self.email !~ TEMP_EMAIL_REGEX
end
end
Check out the example on the docs: http://apidock.com/rails/ActiveModel/Validations/ClassMethods/validates_format_of
Basically you need to provide validates_format_of with a regular expression so it has something to compare your string with.
You can provide the regular expression by using the :with option.
In your example, to validate for an email upon updating, do this:
validates_format_of :email, :with => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :on => :update
That regular expression takes care of most email formats: http://rubular.com/r/YEPtKO3j5L
validates_format_of :email, on: :update
You have to specify how to check email. Use "with"

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

Error self.user.update_attribute undefined method nil:Class

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.

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) }

Rails: Populate nested models with rake and forgery

I want to populate my database with test data my user and profile models are seperate form each other with a 1-to-1 relationship. The script that I'm running creates the data but doesn't relate it together. how would I get it to related the data together?
app/models/user.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :profile
attr_accessible :email, :password, :password_confirmation, :remember_me, :profile_attributes
accepts_nested_attributes_for :profile
end
app/models/profile.rb
class Profile < ActiveRecord::Base
belongs_to :user
attr_accessible :first_name, :last_name
validates :first_name, presence: true
validates :last_name, presence: true
end
lib/tasks/sample_data.rb
namespace :db do
desc "Fill database with sample data"
task populate: :environment do
User.create!(email: "dufall#iinet.net.au",
password: "123qwe",
password_confirmation: "123qwe")
Profile.create!(first_name: "Aaron",
last_name: "Dufall")
99.times do |n|
first_name = Forgery::Name.first_name
Last_name = Forgery::Name.last_name
email = "example-#{n+1}#railstutorial.org"
password = "password"
User.create!(email: email,
password: password,
password_confirmation: password)
Profile.create!(first_name: first_name,
last_name: Last_name)
end
end
end
Try to use user.create_profile! instead of Profile.create!
namespace :db do
desc "Fill database with sample data"
task populate: :environment do
user = User.create!(email: "dufall#iinet.net.au",
password: "123qwe",
password_confirmation: "123qwe")
user.create_profile!(first_name: "Aaron",
last_name: "Dufall")
99.times do |n|
first_name = Forgery::Name.first_name
Last_name = Forgery::Name.last_name
email = "example-#{n+1}#railstutorial.org"
password = "password"
user = User.create!(email: email,
password: password,
password_confirmation: password)
user.create_profile!(first_name: first_name,
last_name: Last_name)
end
end
end

Resources