When is the validator of email uniqueness trigerred? - validation

I have a question about this test from the Michael Hartl tutorial :
Model:
class User < ActiveRecord::Base
.
.
.
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
uniqueness: true
end
Test:
require 'spec_helper'
describe User do
before do
#user = User.new(name: "Example User", email: "user#example.com")
end
.
.
.
describe "when email address is already taken" do
before do
user_with_same_email = #user.dup
user_with_same_email.email = #user.email.upcase
user_with_same_email.save
end
it { should_not be_valid }
end
end
My understanding of the uniqueness validator for the email is that it cannot be added twice in the database. But in this test, the User is just instantiated with a new, not a create.
So here is what I think happens:
#user = User.new (just in memory)
...
user_with_same_email = #user.dup we have two users in memory
...
user_with_same_email.save we are inserting the first user in the db, so it should be valid, and yet the test it { should_not be_valid } passes.
What am I getting wrong ?

What really happens:
In before:
#user = User.new (just in memory)
In the describe:
user_with_same_email = #user.dup we have two users in memory
user_with_same_email.save we are inserting the first user in the db, so it should be valid, and it is ! But that's not what's being tested here
In the it:
should_not be_valid calls .valid? on #user, and since we've just inserted an user with the same email, #user is not valid. And so the test passes.

Related

PG::UndefinedTable: ERROR: relation "cities" does not exist LINE 8: WHERE a.attrelid = '"cities"'::regclass ^

I am new to Sinatra and trying to create a post request which would add some data into "City" table in PGadmin. But, I am getting an error when trying to add it. The get request works fine, and posting static data on the post endpoint also works fine, but posting data doesn't I am not sure what's wrong since the migration and the model for city does exist. Here's the code:
city.rb
class City < UserDB
validates_presence_of :name, :pin
def as_json(options = {})
super({only: [:city, :pin]})
end
end
migration:
class CreateCity < ActiveRecord::Migration[6.0]
def change
create_table :city do |t|
t.string :name
t.integer :pin
end
end
end
module Users
module V1
class CityController< LocalAPI::ApplicationBase
before do
content_type 'application/json'
end
get '/' do
city=City.all
if city
"city exists"
else
"doesn't exist"
end
end
post '/add' do
params = JSON.parse request.body.read
#name= params['name']
#pin= params['pin']
#city=City.new(name:#name,pin:#pin)
if #city.save
return output(200, #city.as_json)
else
"cant create"
end
end
end
end
end
I am trying to post the below content through postman:
{
"name": "hamilton",
"pin":"222"
}
Please note that the route is configured and it does work when I visit the url. Also have ran db:migrate

Can't Use Validates Presence On Picture Rails 5.x

micropost.rb
class Micropost < ActiveRecord::Base
belongs_to :user
default_scope -> { order(created_at: :desc) }
mount_uploader :picture, PictureUploader
validates :user_id, presence: true
validates :tag1, presence: true, length: { maximum: 20 }
validates :tag2, presence: true, length: { maximum: 20 }
validates :tag3, presence: true, length: { maximum: 20 }
validates :picture, presence: true
validate :picture_size
validates :ispublic, inclusion: { in: [ true, false ] }
private
# Validates the size of an uploaded picture.
def picture_size
if picture.size > 5.megabytes
errors.add(:picture, "should be less than 5MB in size.")
end
end
end
microposts_controller.rb snippet:
def create
#micropost = current_user.microposts.build(micropost_params)
if #micropost.save!
flash[:success] = "Post created!"
redirect_to root_url
else
#feed_items = []
render 'static_pages/home'
end
end
def micropost_params
params.require(:micropost).permit(:tag1, :tag2, :tag3, :picture, :ispublic)
end
Test that is failing snippet:
tag1 = "This"
tag2 = "Is"
tag3 = "Sparta"
image = File.open("test/fixtures/p_avatar.png")
assert_difference 'Micropost.count', 1 do
post microposts_path, params: {micropost: { tag1: tag1, tag2: tag2, tag3: tag3, picture: "image",
ispublic: false }}
end
Test error:
Validation failed. Picture can't be blank.
I am using CarrierWave...based on other answers I was wondering if I can't use the presence: true validation with a picture, but I thought the #micropost.save would just store whatever as a string (varchar). For testing purposes, I just want to make sure a valid string is passed, but maybe that isn't even necessary if CarrierWave and picture_size validation is enough.
Found it. In the Rails spec, there is a function called fixture_file_upload() which does all the heavy lifting of creating the image object for me (some sort of upload to a cache it seems) and making the Model.save() method validate it and store the string in the db.
Thanks to this site it informed me a bit on image upload testing without going through FactoryBot: https://jeffkreeftmeijer.com/carrierwave-rails-test-fixtures/
Ultimately I decided I micropost doesn't require a picture (A la Twitter).

Rails 4+: where to store custom validation msgs used across models

I have custom validation messages that I use in several different models:
# models/user.rb
class Person < ActiveRecord::Base
validates :name, presence: { message: "This is a required question" }
end
# models/pet.rb
class Pet < ActiveRecord::Base
validates :name, presence: { message: "This is a required question" }
end
I'm a little familiar with the en.yml config, but that specifies messages per model:
en:
activerecord:
errors:
models:
person:
attributes:
name:
blank: "This is a required question"
pet:
attributes:
name:
blank: "This is a required question"
And that just seems very verbose and not developer-friendly.
My solution was to create an initializer file and store my custom validation messages in a hash there:
# config/initializers/custom_validation_messages.rb
CUSTOM_VALIDATION_MESSAGES = {
required: "This is a required question"
}
And then to use them as:
validates :name, presence: { message: CUSTOM_VALIDATION_MESSAGES[:required] }
My solution works, but I'm not sure if this is a correct usage of an initializer. Is there a more appropriate Rails convention for cleaning up those duplicate validation messages?
I prefer the following approach, so I could also use the translations on other places within my app (like a hint message for instance):
class Pet < ActiveRecord::Base
validates :name, presence: { message: I18n.t('common_errors.required_question' }
end
and
en:
common_errors:
required_question: "This is a required question"

How can i return some message if before_validation method is false by Sinatra

I want to return some message that tell the user their password is not match with the confirm password field.
This is my code
post '/signup' do
user = User.new(params[:user])
if user.create_user
"#{user.list}"
else
"password not match"
end
end
require 'bcrypt'
class User < ActiveRecord::Base
include BCrypt
# This is Sinatra! Remember to create a migration!
attr_accessor :password
validates :username, presence: true, uniqueness: true
validates :email, :format => { :with => /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i ,
:message => "Email wrong format" }
validates :email, uniqueness: true,presence: true
validates :encrypted_password, presence: true
validates :encrypted_password, length: { minimum: 6 }
before_validation :checking_confirm_password
def initialize(signup = {})
##signup = signup
super()
#username = signup["username"]
#email = signup["email"]
#password = signup["password"]
#confirm_password = signup["confirm_password"]
end
def create_user
p_p = Password.create(#password)
p_h ||= Password.new(p_p)
user_hash = {:username => #username,:email => #email, :encrypted_password => p_h}
return User.create(user_hash)
end
def list
User.all
end
private
def checking_confirm_password
if #password != #confirm_password
return false
end
end
end
So how can i specific the message that will send back to the user,
if their password is not match or the validation fail before create the dada?
Thanks!
The validations populate #user.errors with all validation errors with all validation errors by field, so you can easily return all validation errors at once like so:
post '/signup' do
user = User.new(params[:user])
if user.create_user
"#{user.list}"
else
user.errors.full_messages
end
end
Have a look at: http://edgeguides.rubyonrails.org/active_record_validations.html#working-with-validation-errors

Rails -- before_save not working?

I am following Michael Hartl's RoR tutorial, and it is covering the basics of password encryption. This is the User model as it currently stands:
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :name, :email,: password, :password_confirmation
email_regex = /^[A-Za-z0-9._+-]+#[A-Za-z0-9._-]+\.[A-Za-z0-9._-]+[A-Za-z]$/
#tests for valid email addresses.
validates :name, :presence => true,
:length => {:maximum => 50}
validates :email, :presence => true,
:format => {:with => email_regex},
:uniqueness => {:case_sensitive => false}
validates :password, :presence => true,
:length => {:maximum => 20, :minimum => 6},
:confirmation => true
before_save :encrypt_password
private
def encrypt_password
#encrypted_password = encrypt(password)
end
def encrypt(string)
string
end
end
(Obviously this isn't doing any encrypting because the encrypt method isn't really implemented but that's not my question)
I then wrote the following Spec (according to the tutorial):
require 'spec_helper'
describe User do
before(:each) do
#attr = { :name => "Example User", :email => "user#example.com",
:password => "abc123", :password_confirmation => "abc123"}
end
describe "password encryption" do
before(:each) do
#user = User.create!(#attr) # we are going to need a valid user in order
# for these tests to run.
end
it "should have an encrypted password attribute" do
#user.should respond_to(:encrypted_password)
end
it "should set the encrypted password upon user creation" do
#user.encrypted_password.should_not be_blank
end
end
end
The first of these tests passes, but since #user.encrypted_password is nil, the second test fails. But I don't understand why it's nil since the encrypt_password method should be being called by before_save. I know I must be missing something -- can someone please explain?
The encrypt_password method is incorrect, it should read:
def encrypt_password
self.encrypted_password = encrypt(password)
end
Note the use of self, which will properly set the attribute for the user object rather than creating an instance variable which is forgotten.
This is an old question and this is more of a comment but I don't have enough reputation to comment yet. Just wanted to link this question too as it goes into some solid detail about self.
Why isn't self always needed in ruby / rails / activerecord?

Resources