I am using the railstutorial.org book.
I tried updating the user attributes as written in Chapter 7 of the book, but the email became nil. I have tried updating to no avail. This produces a NoMethodError in UsersController#show: undefined method `downcase' for nil:NilClass.
Here is the show.html.erb
<% provide(:title, #user.name) %>
<h1>
<%= gravatar_for #user %>
<%= #user.name %>
</h1>
Users helper
module UsersHelper
# Returns the Gravatar of the given user.
def gravatar_for(user)
gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}"
image_tag(gravatar_url, alt: user.name, class: "gravatar")
end
end
User Controller
class UsersController < ApplicationController
def new
end
def show
#user = User.find(params[:id])
end
end
User model
class User < ActiveRecord::Base
before_save { email.downcase! }
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true,
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, presence: true, length: { minimum: 6 }
end
Please I need help to fix this. Thanks.
I'm also following this tutorial, so I don't pretend to be a Rails expert.
That said, I just went back and reviewed the tutorial material surrounding the first introduction of the
before_save { email.downcase! }
syntax, and I see this at the end of Chapter 6 (listing 6.42).
I'm pretty sure this isn't working for you because your UsersController is missing a definition of the "New" method:
def new
#user = User.new
end
I'm wiling to bet that your #user object is Nil because you haven't create an instance of it yet. BTW, at this point in the tutorial, you should have also defined a Create method in UsersController.
EDIT: If your problems are limited to what is happening in the Rails console, I agree with the comments that you need to provide a complete transcript of your console session in order for folks to provide a complete answer.
Here's an example Rails console session from within my Rails Tutorial project:
Invoke the console and make it aware of my User model:
$rails console
Loading development environment (Rails 4.2.2)
require './app/models/user'
=> true
Create a User instance named "spong"
**spong = User.new**
=> <User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil, password_digest: nil, remember_digest: nil, admin: nil, activation_digest: nil, activated: false, activated_at: nil>
(Note: My User model has more attributes because I am toward the end of the Tutorial.)
Populate values for name and e-mail:
spong.name = "Yo Dawg!"
=> "Yo Dawg!"
spong.email = "YoDaWG#dawg.COM"
=> "YoDaWG#dawg.COM"
Note that my initial e-mail address is mixed case.
Invoke the downcase method:
spong.email.downcase
=> "yodawg#dawg.com"
This is working for me in the console. Now let's try the update_attributes method:
spong.update_attributes(name: "The Dude", email: "dude#AbideS.org")
This is straight out of the tutorial, but it doesn't work for me, because at this point in my journey, I have implemented features that prevent this kind of update:
(6.5ms) begin transaction
User Exists (0.5ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER('dude#AbideS.org') LIMIT 1
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER('dude#AbideS.org') LIMIT 1
(0.1ms) rollback transaction
=> false
As Hartl says:
Note that if any of the validations fail, such as when a password is required to save a record (as implemented in Section 6.3), the call to update_attributes will fail.
So let me try the singular version of this command:
spong.update_attribute( :email, "dude#AbideS.org")
(3.7ms) begin transaction
SQL (4.0ms) INSERT INTO "users" ("name", "email", "created_at", "updated_at", "activation_digest") VALUES (?, ?, ?, ?, ?) [["name", "The Dude"], ["email", "dude#abides.org"], ... ]
(1.2ms) commit transaction
==> true
spong.email
=> "dude#abides.org"
Not that the e-mail address is already converted to lower case in the INSERT command--exactly as expected, thanks to that
before_save { email.downcase! }
we have in our User model.
But what's with all the DB activity? This is because update_attributes updates a single attribute and saves the record without going through the normal validation procedure (which is why I am able to do this). While research this, I found this excellent discussion about update_attribute and update_attributes. Great stuff!
OK, so what happens if we try to call update_attribute when the (existing) e-mail address is blank? Let's see:
newUser = User.new
=> #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil, password_digest: nil, remember_digest: nil, admin: nil, activation_digest: nil, activated: false, activated_at: nil>
Everything in newUser is nil. Let's try to update the e-mail address:
newUser.update_attribute(:email, "cOnFuSed#MixecCase.com")**
(1.2ms) begin transaction
SQL (3.9ms) INSERT INTO "users" ("email", "created_at", "updated_at", "activation_digest") VALUES (?, ?, ?, ?) [["email", "confused#mixeccase.com"], ...]
(0.9ms) commit transaction
=> true
Again, because of the behavior of update_attribute/update_attributes, my database is updated; somewhat counterintuitively, a record is inserted during this "update" process, but this is because I had not yet saved this (or the first) record to the DB.
I hope all this helps. At a minimum, I have demonstrated that this DOES work via the console--even with previously 'nil' values (and I learned a ton while doing the research to attempt an answer).
Related
I have a User table for my app which contains the list of all users. This table has a Boolean field named active.
I have this code to fetch the user:
existing_user = User.where("LOWER(email) = ?", auth_hash['info']['email'].downcase)
And this is what I get when I do an existing_user.inspect:
User Load (1.9ms) SELECT "users".* FROM "users" WHERE (LOWER(email) = 'biswanath.chandramouli#gmail.com')
#<ActiveRecord::Relation [#<User id: 4, name: "Biswanath Chandramouli", email: "Biswanath.Chandramouli#gmail.com", last_updated_by: "", admin: true, active: true, last_updated_on: nil, created_at: "2018-10-30 08:14:59", updated_at: "2018-10-30 08:14:59"
As you can see, existing_user has the property active available as shown above.
But this code fails:
if(!existing_user.active?)
The above call throws this error:
undefined method `active?' for #<User::ActiveRecord_Relation:0x00007f0a58b2c500> Did you mean? acts_like?
When existing_user.inspect shows active: true, why does the above call existing_user.active fail? Pls help!
I think you should use if(!existing_user.first.active?). This will work in your case. Where clause returns you an array, not an object. In your case, existing_user is an array not an object.
This answer is off-topic but could save you a lot:
Every time you call this existing_user = User.where("LOWER(email) = ?", auth_hash['info']['email'].downcase), it's going to downcase all the emails in the table and look for the correct one.
I would suggest to downcase the email before saving the user and add an index on it;
before_save { self.email = self.email.downcase }
and then get the user:
user = User.where(email: auth_hash['info']['email'].downcase).first
Try this method and you'll see a big difference in the data retrieval (which is now 1.9ms)
Half of the time I'll be able to make my tests run. The other half they fail because of a uniqueness violation, the source of which I am unable to locate. Right now I am in the latter half. My error is this:
ItemTest#test_valid_setup:
ActiveRecord::RecordNotUnique: PG::UniqueViolation: ERROR: duplicate key value violates unique constraint "index_blobs_on_user_id_and_item_id"
DETAIL: Key (user_id, item_id)=(1, 1) already exists.
: INSERT INTO "blobs" ("user_id", "item_id", "amount", "active", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id"
test/models/item_test.rb:15:in `setup'
I have a factories.rb:
FactoryBot.define do
factory :user, aliases: [:owner] do
email "test#test.com"
username "test"
end
factory :item do
owner
image { loads file etc... }
price 100
...
end
factory :blob, aliases: [:wanted, :collateral] do
user
item
amount 0
active true
end
end
My item_test.rb
require 'test_helper'
require 'support/database_cleaner'
class ItemTest < Minitest::Test
def setup
DatabaseCleaner.start
#create users
#user1 = FactoryBot.create(:user, email: "a#pear.com", username: "barnyard")
#user2 = FactoryBot.create(:user, email: "bo#ichi.com", username: "ponygrl")
#user3 = FactoryBot.create(:user, email: "ho#ho.com", username: "hon")
#create items
#item1 = FactoryBot.create(:item, owner: #user1)
#item2 = FactoryBot.create(:item, owner: #user2, price: 101)
#item3 = FactoryBot.create(:item, owner: #user3, price: 102)
#create blobs
#blob1 = FactoryBot.create(:blob, user: #user1, item: #item1, amount: #item1.price, active: false)
#blob2 = FactoryBot.create(:blob, user: #user2, item: #item2, amount: #item2.price, active: false)
#blob3 = FactoryBot.create(:blob, user: #user3, item: #item3, amount: #item3.price, active: false)
end
def teardown
DatabaseCleaner.clean
end
end
And then an item.rb
class Item < ApplicationRecord
after_create :create_blobs
private
def create_blobs
blob = Blob.new(user_id: self.owner.id, item_id: self.id, amount: self.price)
blob.save
end
end
A little background: A User creates an Item which in turn creates a Blob in an after_create with an amount parameter set to the value of Item's price. I cannot find out how to run an after_create in minitest, so I mocked up the Blob data in setup to inherit from an attribute of Item.
I can see that the error comes from line 15 of item_test.rb, but I'm not understanding why. I'm creating the Users, then the Items, and then ERROR the Blobs. I understand the why (I have a db level uniqueness constraint on a combination of user and item) but not the how (because from what I see, I haven't created those Blobs - there's no after_create called on Item when they're created in test), and I suspect that it has to do with the way I'm writing this.
It seems natural to me to conclude that DatabaseCleaner.start and DatabaseCleaner.clean both start and clean up old test data when the test is run and concluded, but this is obviously not the case. I started using it specifically to avoid this problem, which I was having previously. So I db:drop db:create, and db:schema:load, but once again, I have the same issue. And if it's not that, it's a uniqueness violation on a username, an email, etc......long story short, what is going on with that error?
Sorry if this is so confusing.
Edit: If I uncomment the after_create and replace all method references to the blob object created through that callback with blobs created in my test setup, the tests pass. But I really don't like doing that.
Either uncomment the after_create and reference the test objects, or remove the test objects and reference each blob by writing a method that returns the blob owned_by user and item, so that you can write #item.blob, and have it return the relevant blob.
Suppose there are users records in the database. And we decided to add validation in model. Model:
class User < ActiveRecord::Base
validates_format_of :name, with: /\A[^\d]*\z/, allow_blank: true
before_validation :delete_digits_from_name
def delete_digits_from_name
self.name = name.gsub!(/\d/, '')
end
end
Scenario 1 in console:
User.create(name: 'Username 15')
User.last
=> #<User id: 14154, name: "Username"
And it's ok. But there are old record (created before adding validation) and.. scenario 2:
user = User.first
=> #<User id: 1, name: "Username 15"
user.save
=> true
user
=> #<User id: 1, name: "Username"
user.reload
=> #<User id: 1, name: "Username 15"
But why?? Why changes not saved?
The gsub! in delete_digits_from_name changes the name in place, so Rails thinks name is the same thing it loaded from the DB. It's the same object, even though you've changed its value. Rails does this to optimize away DB updates when no data has changed, and in-place editing confuses it.
Switching to self.name = self.name.gsub(/\d/, '') (no !) assigns a new String that Rails will recognize as dirty and needing saving.
You can also add name_will_change! after your gsub! to tell Rails the attribute needs saving.
I am trying to insert a new record into a SQLiteDB through my Rails console. Simply using User.create().
The row does not appear in my database.The message implies that a user exists, but calling User.first returns null and checking the database shows an empty table. What am I missing?
Input:
console > User.create(name: 'don', email: 'don#gmaill.com', password: 'test', password_confirmation: 'test')
Output:
(0.1ms) begin transaction
User Exists (0.1ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = 'don#gmaill.com' LIMIT 1
(0.0ms) rollback transaction
=> #<User id: nil, name: "don", email: "don#gmaill.com", created_at: nil, updated_at: nil, password_digest: "$2a$10$3EK3L932ryjrJPFIc4E0/uzavrpkWylDRzx4Wkdwzx8d...">
User Class:
class User < ActiveRecord::Base
before_save { self.email = email.downcase }
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sen,sitive: false }
has_secure_password
validates :password, length: { minimum: 6 }
end
It's not persisting the User (you can tell by the rollback statement). Looks like it's failing a uniqueness validation on the email. You can look at the errors by calling errors.full_messages on the User you tried to create, or by using create! instead of create
You are most likely validating uniqueness of email field. Hence ActiveRecord is issuing a select first to check if a record exist with the same email address, and finding one. Hence Create is not saving the new record to database.
But create returns the object anyway.
You can try this to verify this. Instead of create, try new followed by save!
console > u = User.new(name: 'don', email: 'don#gmaill.com', password: 'test', password_confirmation: 'test')
console > u.save!
To get rid of this error, first delete the record that exists in the DB
There is User model, and when user creates account, letter with link for confirmation is sent to his email adress. after_callback sets user's confirmed column to false.
(I don't sure about if more than one callbacks of one type is possible, there are two after callbacks)
User.rb:
after_create :confirmation
after_create :add_user_profile
...
def confirmation
if self.guest
self.confirmed=true
elsif self.authentication
self.confirmed=true
else
self.confirmed=false
begin
confirmation_token = SecureRandom.urlsafe_base64
end while User.exists?(:confirmation_token => confirmation_token)
self.confirmation_token=confirmation_token
self.confirmation_link_sent_at=Time.now.utc
UserMailer.send_confirmation_link(self).deliver
User.delay.delete_unconfirmed(self)
end
end
But when I output last user from console:
>> User.last
User Load (1.0ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT 1
#<User id: 21, email: "somddething#mail.com", password_digest: "$2a$10$LMtsumUaS5MaiRhz3.mZ2em7Fbt3.66pbpe4863zj3b0...", created_at: "2013-05-22 17:17:18", updated_at: "2013-05-22 17:17:18", guest: false, auth_token: "lbIJftEqgwyXYymjEVdhCQ", password_reset_token: nil, password_link_sent_at: nil, confirmed: nil, confirmation_token: nil, confirmation_link_sent_at: nil>
As you can see, confirmed is nil, also others related columns too. Why?
That's not working because your are setting the confirmed attribute to the users but you are not saving it again.
Depending on your needs, you can change the callback to before_create or save the user:
def confirmation
...
self.save
end
Hop this helps!