Why is before_create not being called here? - ruby-on-rails

Similar question, but user was not actually creating an ActiveRecord object.
Following Michael Hartl's tutorial, and I've hit a wall. There is a method call in before_save that doesn't seem to be working. I have double checked that my code aligns with his and is syntactically valid (although I often overlook simple syntax errors).
class User < ActiveRecord::Base
before_create { :create_remember_token } <------ here
before_save { self.email = email.downcase }
validates :name, presence: true, length: { maximum: 30 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.?]+\.[a-z]{2,4}\z/i
validates :email, presence: true,
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password # automatically checks presence of password and confirmation
validates :password, length: { minimum: 6 }
def User.new_remember_token
SecureRandom.urlsafe_base64
end
def User.encrypt(token)
Digest::SHA1.hexdigest(token.to_s)
end
private
def create_remember_token
self.remember_token = User.encrypt(User.new_remember_token)
end
end
Here's a console session to show that the db schema includes a remember_token, the class methods indeed work, yet something is wrong with the create callback.
2.0.0p247 :001 > User
=> User(id: integer, name: string, email: string, created_at: datetime, updated_at: datetime, password_digest: string, remember_token: string)
2.0.0p247 :002 > User.create(name:"Confused", email:"stack#overflow.com", password:"password", password_confirmation:"password")
(0.1ms) begin transaction
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER('stack#overflow.com') LIMIT 1
Binary data inserted for `string` type on column `password_digest`
SQL (3.9ms) INSERT INTO "users" ("created_at", "email", "name", "password_digest", "updated_at") VALUES (?, ?, ?, ?, ?) [["created_at", Wed, 09 Oct 2013 22:50:41 UTC +00:00], ["email", "stack#overflow.com"], ["name", "Confused"], ["password_digest", "$2a$10$Pca7ZokAVlca/floQRY1KesM1SFoSfiUxWjEJ8xHyA4NJueK4GVbG"], ["updated_at", Wed, 09 Oct 2013 22:50:41 UTC +00:00]]
(101.5ms) commit transaction
=> #<User id: 9, name: "Confused", email: "stack#overflow.com", created_at: "2013-10-09 22:50:41", updated_at: "2013-10-09 22:50:41", password_digest: "$2a$10$Pca7ZokAVlca/floQRY1KesM1SFoSfiUxWjEJ8xHyA4N...", remember_token: nil>
2.0.0p247 :003 >
2.0.0p247 :004 > token = User.new_remember_token
=> "md1vrpB2PH1VNMWaliuT6g"
2.0.0p247 :005 > User.encrypt(token)
=> "522d45ee2771c2cb36ffe6536b316ad004e5038b"
2.0.0p247 :006 >
2.0.0p247 :006 > User._create_callbacks.select { |cb| cb.kind.eql?(:before) }.collect(&:filter).include?(:create_remember_token)
=> false

Just remove brackets, try:
before_create :create_remember_token
If you pass a block it is evaluated, if you pass symbol a method with given name is called.

Related

Datetime fails to save but without error

I try saving a row that has an attribute named class_datetime (which is a DateTime) and it silently fails. Here's an example:
I create a new LittleClassSession, like so:
irb(main):010:0> l = LittleClassSession.new
=> #<LittleClassSession id: nil, length: nil, class_datetime: nil, created_at: nil, updated_at: nil, little_class_id: nil, location_id: nil, session_series_token: nil>
The class_datetime is a DateTime:
irb(main):011:0> LittleClassSession.columns_hash["class_datetime"].type
=> :datetime
I set the attribute:
irb(main):012:0> l.class_datetime = DateTime.now
=> 2015-08-13 22:02:09 -0400
And I save (no validations yet):
irb(main):013:0> l.save
(0.1ms) begin transaction
SQL (0.4ms) INSERT INTO "little_class_sessions" ("class_datetime", "created_at", "updated_at") VALUES (?, ?, ?) [["class_datetime", "2015-08-13 22:02:09.976030"], ["created_at", "2015-08-13 22:03:01.255911"], ["updated_at", "2015-08-13 22:03:01.255911"]]
(2.6ms) commit transaction
=> true
Looks like it worked. Let's see what I saved:
irb(main):014:0> l
=> #<LittleClassSession id: 728, length: nil, class_datetime: "2015-08-14 02:02:09", created_at: "2015-08-14 02:03:01", updated_at: "2015-08-14 02:03:01", little_class_id: nil, location_id: nil, session_series_token: nil>
Let's see what's in the table:
irb(main):015:0> LittleClassSession.last
LittleClassSession Load (0.2ms) SELECT "little_class_sessions".* FROM "little_class_sessions" ORDER BY "little_class_sessions"."id" DESC LIMIT 1
=> #<LittleClassSession id: 728, length: nil, class_datetime: nil, created_at: nil, updated_at: nil, little_class_id: nil, location_id: nil, session_series_token: nil>
My LittleClassSession model looks like:
class LittleClassSession < ActiveRecord::Base
belongs_to :little_class
belongs_to :location
end
What could be causing this? Recently I changed the default timezone in application.rb, but I don't see why that might be the reason.
Your :date_time field is DateTime object.
Try this:
l.class_datetime = DateTime.now
Rather than:
l.class_datetime = Time.now
The issue went away once I removed these two lines from application.rb:
config.time_zone = 'Eastern Time (US & Canada)'
config.active_record.default_timezone = 'Eastern Time (US & Canada)'
This evidently caused some chaos.

Rails: Migration default value cannot be overwritten by create methods

I have an migration for new table:
class CreateContentCategories < ActiveRecord::Migration
def change
create_table :content_categories do |t|
t.string :title, default: '', null: false
t.timestamps null: false
end
end
end
When I try to create or find_or_create_by new records with options:
def self.assign_titles_to_app(titles, app)
# somewhere inside ( class scope )
title = 'movies'
content_category = find_by_title(title) || create(title: title)
puts "title: #{title} category: #{content_category.inspect}"
active record doesn't use my title
title: movies category: #<ContentCategory id: 1050, title: "", created_at: "2015-06-25 15:42:57", updated_at: "2015-06-25 15:42:57">
the same result for find_or_create_by:
title = 'movies'
content_category = find_or_create_by(title: title)
title: movies category: #<ContentCategory id: 1062, title: "", created_at: "2015-06-25 15:45:25", updated_at: "2015-06-25 15:45:25">
Documentation said:
:default - The column's default value. Use nil for NULL.
What is going wrong? how to fix it?
Info:
rails 4.2.1
activerecord 4.2.1
ruby 2.2.2
Update: I forgot about attr_accessible:
class ContentCategory < ActiveRecord::Base
# attr_accessible :title <- I forgot to add this line, oh
end
There's nothing wrong with your code, maybe you need to restart your app (and spring) or maybe the problem is in code that you haven't shown. Just to demonstrate, I added your exact code to a vanilla 4.2 app and:
[9] pry(main)> ContentCategory.assign_titles_to_app 1, 2
ContentCategory Load (0.2ms) SELECT "content_categories".* FROM "content_categories" WHERE "content_categories"."title" = ? LIMIT 1 [["title", "movies"]]
(0.1ms) begin transaction
SQL (0.7ms) INSERT INTO "content_categories" ("title", "created_at", "updated_at") VALUES (?, ?, ?) [["title", "movies"], ["created_at", "2015-06-25 16:31:59.192793"], ["updated_at", "2015-06-25 16:31:59.192793"]]
(0.5ms) commit transaction
title: movies category: #<ContentCategory id: 1, title: "movies", created_at: "2015-06-25 16:31:59", updated_at: "2015-06-25 16:31:59">
=> nil

undefined method with has_many association

I have 2 models with a one-to-many association: User and Recipe. the User class has_many :recipes while the Recipe class belongs_to :user. I've already run the migration, reloaded the rails console, and checked to make sure that user_id is a column in the recipes table. Still, I get an undefined method error when I try to append a recipe to a user:
2.0.0-p598 :047 > user.recipes << Recipe.first
NoMethodError: undefined method `recipes' for #<User:0x00000004326fa0>
here is the migration code (I've already run rake db:migrate):
class AddUserIdToRecipes < ActiveRecord::Migration
def change
add_column :recipes, :user_id, :integer
end
end
Here is the User model code:
class User < ActiveRecord::Base
has_one :profile
has_many :recipes
end
Here is the Recipe model code:
class Recipe < ActiveRecord::Base
validates_presence_of :title, :body
belongs_to :user
def long_title
"#{title} - #{published_at}"
end
end
Why does recipes still show up as an undefined method?
Try this on your console:
irb(main):007:0> user = User.new first_name: 'John', last_name: 'Doe'
=> #<User id: nil, first_name: "John", last_name: "Doe", created_at: nil, updated_at: nil>
irb(main):008:0> user.save
(0.1ms) begin transaction
SQL (0.6ms) INSERT INTO "users" ("created_at", "first_name", "last_name", "updated_at") VALUES (?, ?, ?, ?) [["created_at", "2015-01-19 21:14:33.489371"], ["first_name", "John"], ["last_name", "Doe"], ["updated_at", "2015-01-19 21:14:33.489371"]]
(0.6ms) commit transaction
=> true
irb(main):009:0> r = Recipe.new name: 'oooohh awesome', description: 'my description goes here'
=> #<Recipe id: nil, name: "oooohh awesome", description: "my description goes here", created_at: nil, updated_at: nil, user_id: nil>
irb(main):010:0> r.save
(0.1ms) begin transaction
SQL (0.2ms) INSERT INTO "recipes" ("created_at", "description", "name", "updated_at") VALUES (?, ?, ?, ?) [["created_at", "2015-01-19 21:15:16.548090"], ["description", "my description goes here"], ["name", "oooohh awesome"], ["updated_at", "2015-01-19 21:15:16.548090"]]
(1.2ms) commit transaction
=> true
irb(main):011:0> user.recipes << Recipe.first
Recipe Load (0.2ms) SELECT "recipes".* FROM "recipes" ORDER BY "recipes"."id" ASC LIMIT 1
(0.0ms) begin transaction
SQL (0.2ms) UPDATE "recipes" SET "updated_at" = ?, "user_id" = ? WHERE "recipes"."id" = 1 [["updated_at", "2015-01-19 21:15:49.181586"], ["user_id", 1]]
(1.3ms) commit transaction
Recipe Load (0.2ms) SELECT "recipes".* FROM "recipes" WHERE "recipes"."user_id" = ? [["user_id", 1]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Recipe id: 1, name: "oooohh awesome", description: "sper long deskdk", created_at: "2015-01-19 21:10:24", updated_at: "2015-01-19 21:15:49", user_id: 1>]>
irb(main):012:0> user.save
(0.1ms) begin transaction
(0.0ms) commit transaction
=> true
irb(main):013:0> user.recipes
=> #<ActiveRecord::Associations::CollectionProxy [#<Recipe id: 1, name: "oooohh awesome", description: "sper long deskdk", created_at: "2015-01-19 21:10:24", updated_at: "2015-01-19 21:15:49", user_id: 1>]>
irb(main):014:0> user.recipes.first
=> #<Recipe id: 1, name: "oooohh awesome", description: "sper long deskdk", created_at: "2015-01-19 21:10:24", updated_at: "2015-01-19 21:15:49", user_id: 1>
irb(main):015:0>
you can see that Recipe.first has been inserted into user.recipes and its saved.
I made two models similar to yours, and have exactly the same setup as you. You can follow code above to write your controllers.

Rails model create and new returns nil in test environment

I have a model spec that is failing with "undefined method 'save' for nil:NilClass'." This occurs in the class method 'create_and_send_self_eval'. The method is creating a new Evaluation, but it always returns nil in the test environment. I've also tried using 'create', 'create!' and they also return nil. However, this only occurs in the test environment. In the development environment, it returns the correct object. I'm using rspec 3.1.5, rails 4.1.6, and ruby 2.1.2.
I've included the code for the class and my debug output. Any suggestions?
Evaluation.rb
class Evaluation < ActiveRecord::Base
has_one :evaluator
validates_uniqueness_of :access_key
validates_presence_of :participant_id
before_validation :set_access_key, on: :create
def send_invite
return true
end
def self.create_and_send_self_eval(participant)
evaluation = self.new do |e|
e.participant_id = participant.id
e.evaluator = participant
end
if evaluation.nil?
binding.pry
end
evaluation.save
end
private
def set_access_key
return if access_key.present?
begin
self.access_key = SecureRandom.hex(8)
end while self.class.exists?(access_key: self.access_key)
end
end
Debug output using pry in the test environment
[1] pry(Evaluation)> participant
=> #<Participant id: 167, first_name: "Puff", last_name: "Daddy", evaluation_url: nil, created_at: "2014-10-07 19:43:47", updated_at: "2014-10-07 19:43:47">
[2] pry(Evaluation)> Evaluation.new
=> nil
[3] pry(Evaluation)> Evaluation.create(participant_id: participant.id)
NoMethodError: undefined method `save' for nil:NilClass
from /Users/diyahm/.rvm/gems/ruby-2.1.2/gems/activerecord-4.1.6/lib/active_record/persistence.rb:34:in `create'
[4] pry(Evaluation)> Evaluation.create!(participant_id: participant.id)
NoMethodError: undefined method `save!' for nil:NilClass
from /Users/diyahm/.rvm/gems/ruby-2.1.2/gems/activerecord-4.1.6/lib/active_record/validations.rb:41:in `create!'
Debug output in rails console
2.1.2 :005 > p = Participant.last
SQL (0.9ms) SELECT "participants"."id" AS t0_r0, "participants"."first_name" AS t0_r1, "participants"."last_name" AS t0_r2, "participants"."evaluation_url" AS t0_r3, "participants"."created_at" AS t0_r4, "participants"."updated_at" AS t0_r5, "evaluators"."id" AS t1_r0, "evaluators"."email" AS t1_r1, "evaluators"."created_at" AS t1_r2, "evaluators"."updated_at" AS t1_r3, "evaluators"."actable_id" AS t1_r4, "evaluators"."actable_type" AS t1_r5, "evaluators"."evaluation_id" AS t1_r6 FROM "participants" LEFT OUTER JOIN "evaluators" ON "evaluators"."actable_id" = "participants"."id" AND "evaluators"."actable_type" = 'Participant' ORDER BY "participants"."id" DESC LIMIT 1
=> #<Participant id: 3, first_name: "Puff", last_name: "Daddy", evaluation_url: nil, created_at: "2014-10-06 06:32:40", updated_at: "2014-10-06 06:32:40">
2.1.2 :006 > Evaluation.new
=> #<Evaluation id: nil, participant_id: nil, access_key: nil, created_at: nil, updated_at: nil>
2.1.2 :007 > Evaluation.create(participant_id: p.id)
(0.2ms) BEGIN
Evaluation Exists (2.1ms) SELECT 1 AS one FROM "evaluations" WHERE "evaluations"."access_key" = 'c688b05ee4625c60' LIMIT 1
Evaluation Exists (0.3ms) SELECT 1 AS one FROM "evaluations" WHERE "evaluations"."access_key" = 'c688b05ee4625c60' LIMIT 1
SQL (1.7ms) INSERT INTO "evaluations" ("access_key", "created_at", "participant_id", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["access_key", "c688b05ee4625c60"], ["created_at", "2014-10-07 19:47:15.877706"], ["participant_id", 3], ["updated_at", "2014-10-07 19:47:15.877706"]]
(2.3ms) COMMIT
=> #<Evaluation id: 4, participant_id: 3, access_key: "c688b05ee4625c60", created_at: "2014-10-07 19:47:15", updated_at: "2014-10-07 19:47:15">
pry debug output at beginning of method
[1] pry(Evaluation)> self
=> Evaluation(id: integer, participant_id: integer, access_key: string, created_at: datetime, updated_at: datetime)
[2] pry(Evaluation)> self.class
=> Class
[3] pry(Evaluation)> self.connection
=> #<ActiveRecord::ConnectionAdapters::PostgreSQLAdapter:0x007f8158eb8ee0
[4] pry(Evaluation)> Evaluation
=> Evaluation(id: integer, participant_id: integer, access_key: string, created_at: datetime, updated_at: datetime)
[5] pry(Evaluation)> Evaluation.class
=> Class
[6] pry(Evaluation)> Evaluation.connection
=> #<ActiveRecord::ConnectionAdapters::PostgreSQLAdapter:0x007f8158eb8ee0
I didn't show the entire output for self.connection or Evaluation.connection. But connection is returning correctly.
The answer to this question had to do with how the tests were written. In my spec, I'm checking to see if "new" is called on Evaluation. Since, I'm using rspec-mocks, Evaluation is not actually being created. Fixed this by changing the test to test the output results.
Try doing this instead:
evaluation = self.new.tap do |e|
e.participant_id = participant.id
e.evaluator = participant
end
Using Object#tap should guarantee that you set evaluation to the object rather than to the return value of the block.

rails db column remember_token is not saved and always nil? [duplicate]

This question already has answers here:
".save" only inserts null values in database
(3 answers)
Closed 8 years ago.
Why am i unable to update a db column in rails?
Database has a column - 'remember_token' but update is not working on it ?
Result of DB migration
....
....
....
== 20140830041234 AddRememberTokenToUsers: migrating ==========================
-- add_column(:users, :remember_token, :string)
-> 0.0010s
-- add_index(:users, :remember_token)
-> 0.0000s
== 20140830041234 AddRememberTokenToUsers: migrated (0.0030s) =================
rails console - output !
remember_token is nil after a direct assignment
irb(main):063:0* user
=> #<User id: 1, name: "Test", email: "hb#c.co", created_at: "2014-09-01 22:52:02", updated_at: "2014-09-01 22:52:02", p
assword_digest: "$2a$10$/66wO2dBte/xCXqxk.UAo.v.7.XZjsBFA3AerAWkHp16...", remember_token: nil>
irb(main):064:0> User.digest('asdasd')
=> "85136c79cbf9fe36bb9d05d0639c70c265c18d37"
irb(main):065:0> user.remember_token = User.digest('asdasd')
=> "85136c79cbf9fe36bb9d05d0639c70c265c18d37"
irb(main):066:0>
irb(main):067:0* user
=> #<User id: 1, name: "Test", email: "hb#c.co", created_at: "2014-09-01 22:52:02", updated_at: "2014-09-01 22:52:02", p
assword_digest: "$2a$10$/66wO2dBte/xCXqxk.UAo.v.7.XZjsBFA3AerAWkHp16...", remember_token: nil>
irb(main):068:0>
remember_token is nil after update_attributes
irb(main):071:0> user.update_attributes(name: "Yahoo", remember_token: "will this get saved")
←[1m←[36m (0.0ms)←[0m ←[1mbegin transaction←[0m
←[1m←[35mUser Exists (1.0ms)←[0m SELECT 1 AS one FROM "users" WHERE (LOWER("users"."email") = LOWER('hb#c.co') AND "u
sers"."id" != 1) LIMIT 1
←[1m←[36mSQL (7.0ms)←[0m ←[1mUPDATE "users" SET "name" = ?, "updated_at" = ? WHERE "users"."id" = 1←[0m [["name", "Y
ahoo"], ["updated_at", Mon, 01 Sep 2014 23:12:59 UTC +00:00]]
←[1m←[35m (8.0ms)←[0m commit transaction
=> true
irb(main):072:0>
irb(main):073:0* user
=> #<User id: 1, name: "Yahoo", email: "hb#c.co", created_at: "2014-09-01 22:52:02", updated_at: "2014-09-01 23:12:59",
password_digest: "$2a$10$/66wO2dBte/xCXqxk.UAo.v.7.XZjsBFA3AerAWkHp16...", remember_token: nil>
Below is the model
class User < ActiveRecord::Base
attr_accessor :remember_token
before_save { self.email = email.downcase }
before_create :create_remember_token
def User.new_remember_token
SecureRandom.urlsafe_base64
end
def User.digest(token)
Digest::SHA1.hexdigest(token.to_s)
end
private
def create_remember_token
self.remember_token = User.digest(User.new_remember_token)
end
end
And just in case -- here is the controller
class UsersController < ApplicationController
def new
#user = User.new
end
def show
#user = User.find(params[:id])
end
def create
##user = User.new(params.require(:user).permit(:name, :email, :password, :password_confirmation)
#user = User.new(user_params)
if #user.save
# Handle a successful save.
flash[:success] = "Welcome #{#user.name} to the Sample App!"
redirect_to #user
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password,:password_confirmation, :remember_token)
end
end
You have to add :remember_token to attr_accessible in your User model.
attr_accessible :remember_token
it seems that the :remember_token attribute has not been picked up by rails. Try to restart the console and it might work.

Resources