I'm building a simple JSON API using the rails-api gem.
models/user.rb:
class User < ActiveRecord::Base
has_secure_password
attr_accessible :email, :password, :password_confirmation
validates :email, presence: true, uniqueness: { case_sensitive: false }, format: { with: /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i }
validates :password, presence: { on: :create }, length: { minimum: 6 }
end
When I try to sign up without a password this is the JSON output I get:
{
"errors": {
"password_digest": [
"can't be blank"
],
"password": [
"can't be blank",
"is too short (minimum is 6 characters)"
]
}
}
Is it possible to hide the error message for password_digest? I'm returning the #user object with respond_with in the controller.
I've tried the following but no luck (it just duplicates the error "can't be blank"):
validates :password_digest, presence: false
#freemanoid: I tried your code, and it didn't work. But it gave me some hints. Thanks! This is what worked for me:
models/user.rb
after_validation { self.errors.messages.delete(:password_digest) }
You can manually delete this message in json handler in User model. Smth like:
class User < ActiveRecord::Base
def as_json(options = {})
self.errors.messages.delete('password_digest')
super(options)
end
end
Related
Is there a way to validate email addresses such as 'myemail#mydomain.com' ?
So I should check if user has #mydomain.com extension on signup? How would regex code would look like?
This is what I am using currently, would like to add user.type = special if validation passes #mydomain.com if else user.type = normal
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
You can add model level validation on email attribute
validates :email, format: { with: /#mydomain\.com\z/i }
However the above regex doesn't put any restrictions on part of email before #
Edit:
You will have to add a before_create action which does check for #mydomain.com
before_create :set_user_type
def set_user_type
if /#mydomain\.com\z/i.match(email)
self.type = :special
else
self.type = :normal
end
end
The following User test passes with no problem, the user is valid:
user_test.rb:
require 'test_helper'
class UserTest < ActiveSupport::TestCase
def setup
#user = User.new(name: "Example User", email: "user#example.com", callsign: "example",
password: "foobar", password_confirmation: "foobar")
end
test "user should be valid" do
assert #user.valid?
end
end
User model:
class User < ActiveRecord::Base
attr_accessor :remember_token, :activation_token, :reset_token
has_many :personas, dependent: :destroy
has_secure_password
before_save do
email.downcase!
callsign.downcase!
end
before_create :create_activation_digest
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 }
VALID_CALLSIGN_REGEX = /\A[a-z\d\-.\_]+\z/i
validates :callsign, presence: true,
length: { maximum: 20 },
format: { with: VALID_CALLSIGN_REGEX },
uniqueness: { case_sensitive: false }
validates :password, length: { minimum: 6 }, allow_blank: true
def to_param
callsign
end
.
.
end
However, when I set up exactly the same user in the persona_test, the validation fails. (The persona validation fails too, each User has_many personas)
persona_test.rb:
require 'test_helper'
class PersonaTest < ActiveSupport::TestCase
def setup
#user = User.new(name: "Example User", email: "user#example.com", callsign: "example",
password: "foobar", password_confirmation: "foobar")
#persona = #user.personas.build(name: "Bazman", callsign: "Baz")
end
test "user should be valid" do
assert #user.valid?
end
test "persona should be valid" do
assert #persona.valid?
end
end
Persona model:
class Persona < ActiveRecord::Base
belongs_to :user
before_save do
self.callsign.downcase!
set_persona_id
end
validates :name, presence: true,
length: { maximum: 50 }
VALID_CALLSIGN_REGEX = /\A[a-z\d\-.\_]+\z/i
validates :callsign, presence: true,
length: { maximum: 20 },
format: { with: VALID_CALLSIGN_REGEX },
uniqueness: { case_sensitive: false }
validates :user_id, presence: true
validates :persona_id, presence: true
def to_param
callsign
end
.
.
end
Failed test output:
FAIL["test_user_should_be_valid", PersonaTest, 0.754914]
test_user_should_be_valid#PersonaTest (0.75s)
Failed assertion, no message given.
test/models/persona_test.rb:18:in `block in <class:PersonaTest>'
FAIL["test_persona_should_be_valid", PersonaTest, 0.893247]
test_persona_should_be_valid#PersonaTest (0.89s)
Failed assertion, no message given.
test/models/persona_test.rb:22:in `block in <class:PersonaTest>'
I don't understand why the User validation in persona_test.rb is failing when the setup user is identical to the one in user_test.rb. Are you not allowed to test Users in a Personas test? If so, how do I successfully test personas? Each persona belongs_to a user, so I have to create a user in order to create a persona.
EDIT:
persona_test.rb:
require 'test_helper'
class PersonaTest < ActiveSupport::TestCase
def setup
#user = User.new(name: "Example User", email: "user#example.com", callsign: "example",
password: "foobar", password_confirmation: "foobar")#, activated: true)
#persona = #user.personas.build(name: "Bazman", callsign: "Baz")
#persona.user = #user
#persona.persona_id = 1
end
test "user should be valid" do
assert #user.valid?, #user.errors.full_messages
end
test "persona should be valid" do
assert #persona.valid?, #persona.errors.full_messages
end
end
With the updated persona test above, I get the error message 'User can't be blank'. Why is
#persona.user = #user
not working?
The reason for the failed assertion is that some validations in Persona won't pass:
validates :user_id, presence: true
validates :persona_id, presence: true
The validations are run before saving them to the database. For new records, user_id and persona_id will still be nil.
Because Persona is invalid, the User will be invalid in the other test as well.
In your persona model you have:
validates :user_id, presence: true
validates :persona_id, presence: true
But it doesn't look like a user_id is being set. Try setting it with #persona.user = #user in your test.
Additionally, as a tool for debugging, you can print #persona.errors.full_messages in your test to see where exactly it is not validating.
E.g. assert #persona.valid?, #persona.errors.full_messages
Hope that helps.
EDIT: as per the comments below, the line should actually be #persona.user_id = #user.id. Another way you could achieve the same effect is to actually save the records to the database. So in your setup function, you would use create instead of build. This would, however, be slower.
I'm trying to update a user object through a form. However, I don't want to allow usernames to be changed. I don't have the field present on the form. When I submit the form, it gives me an error that I cannot leave the username confirmation blank. Here's my controller:
def update
#partner = CommunityPartner.find(params[:id])
if #partner.update_attributes(allowed_update_params)
else
render('edit')
end
end
def allowed_create_params
params.require(:community_partner).permit(:name, :username, :display_email,
:username_confirmation,
:contact_method, :password,
:password_confirmation,
:phone_number, :address,
:description, :tags_string)
end
def allowed_update_params
params.permit!(:name) if params[:name]
params.permit!(:display_email) if params[:display_email]
params.permit!(:contact_method) if params[:contact_method]
params.permit!(:phone_number) if params[:phone_number]
params.permit!(:address) if params[:address]
params.permit!(:description) if params[:description]
params.permit!(:tags_string) if params[:tags_string]
end
How can I update only the params I'm allowing in the update action without touching the others?
EDIT: validation methods
validates(:name, presence: { on: :create })
validates(:username, presence: { on: :create }, confirmation: { on: :create }, uniqueness: true)
validates(:contact_method, presence: { on: :create })
validates(:username_confirmation, presence: { on: :create })
validates(:display_email, format: { with: VALID_EMAIL })
validates(:address, presence: { on: :create })
validates(:phone_number, presence: { on: :create })
validates(:description, presence: { on: :create })
Assuming that you have a validation on username_confirmation in the CommunityPartner model.
Use on: :create option on that validation. So, that validation would only be checked at the time of CommunityPartner record creation and not while updating(where you don't pass username_confirmation).
For example:
class CommunityPartner < ActiveRecord::Base
validates :username_confirmation, presence: true, on: :create
end
You can skip validation using update_all helper instead of update_attributes.
I have a model User with usual attributes email, email_confirmation, password, password_confirmation. I use "has_secure_password" so the real attributes in the database are email and password_digest. However I would like to limit password length without spaces to 6 characters.
Here is my model :
class User < ActiveRecord::Base
before_validation :auto_strip_confirmation
validates :email, presence: true,
length: { maximum: MAX_SIZE_DEFAULT_INPUT_TEXT },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false },
confirmation: true
validates :email_confirmation, presence: true
has_secure_password
validates :password, length: { minimum: MIN_SIZE_USER_PASSWORD,
maximum: MAX_SIZE_USER_PASSWORD }
private
def auto_strip_confirmation
self.password.strip!
self.password_confirmation.strip!
end
end
But I get this in console :
> user.password = user.password_confirmation = "a "
=> "a "
> user.valid?
User Exists (0.8ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER('user#example.com') LIMIT 1
=> true
Thanks for your help.
After reload, my code actually works in console : user.valid? => false (thanks surendran). But my initial problem was in my tests : I thought I could not set virtual attributes because of the error message "undefined method `strip!' for nil:NilClass". But I forgot I test if my user is valid when password is nil, nearly like this :
before { user.password = nil) }
it { should_not be_valid }
before_validation comes before this test so he tries to strip a nil object.
Following the Michael Hartl rails tutorial. Struggling to get the remember token tests to pass, specifically, the test for non-blankness of the remember token.
The code for the User class is below
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
has_secure_password
before_save { |user| user.email = email.downcase }
before_save { :create_remember_token }
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_sensitive: false}
validates :password, presence: true, length: { minimum: 6 }
validates :password_confirmation, presence: true
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
and the test I can't get to pass:
subject { #user }
...
describe "remember token" do
before { #user.save }
its (:remember_token) { should_not be_blank }
end
and the error message I get is:
.....................F
Failures:
1) User remember token remember_token
Failure/Error: its (:remember_token) { should_not be_blank }
expected blank? to return false, got true
# ./spec/models/user_spec.rb:120:in `block (3 levels) in <top (required)>'
Finished in 0.68878 seconds
22 examples, 1 failure
Failed examples:
rspec ./spec/models/user_spec.rb:120 # User remember token remember_token
I don't know if this is relevant but sublime_text isn't doing anything with the self keyword (ie its not highlighting it in a different colour).
I'm using Ruby 1.9.3
The issue is that you're writing before_save { :create_remember_token } when you should have before_save :create_remember_token. The { } is a block. Same as when you do
do
#This is some code
end
That is also a block of code.
That's why your first before_save works, because you're giving the block a piece of code to execute. In the second before_save you're just giving it the name of the method to execute which has the block of code.
Tl:dr:
Change
before_save { :create_remember_token }
to
before_save :create_remember_token
and you should be good to go.