Tableless validation in Rails 4.2 - ruby-on-rails

I am trying to validate a form, that is not bound to any real Model, so I did the following:
A model
class FormRequest
include ActiveModel::Model
attr_accessor :email
validates :email, presence: true, email: true
end
and a method in a controller
# The form is shown
def index
#form_request = FormRequest.new
end
But when I load the page I get this error:
Unknown validator: 'EmailValidator'
What did I miss?

You don't have the email: true validator.
To validate an email you can use regular expression:
VALID_EMAIL = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL }

I found a simple solution for my problem with the following gem
gem 'email_validator'
It seems like (amazingly) Rails 4 does not include an email validator.

Related

AngularJS and Rails live validation

In my Ruby on Rails application i've set up a user registration / login system that have some validations in the class ( for register a User).
class User < ActiveRecord::Base
# Put the email downcase before save the User
before_save {self.email = email.downcase}
# Confirmation of the password before save
validates_confirmation_of :password
has_secure_password
validates :first_name, :last_name,
presence: true,
format:{ without: /\s/ }
validates :email, uniqueness: true,
format:{
with: /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
}
def to_s
"#{first_name} #{last_name}"
end
end
In this application i also use AngularJS and the question is, can i use AngularJS for the live validation of the user during the registration?
If you want to validate live your fields you'll have to use AngularJS validators. Those ruby validators will be called when you'll submit the form.

How can I update particular fields of model with validation in Ruby on Rails?

There is an AcviteRecord Model named User like this:
class User < ActiveRecord::Base
validates :name, :presence => true
validates :email, :presence => true, :uniqueness => true
validates :plain_password, :presence => true, :confirmation => true
validates :plain_password_confirmation, :presence => true
#...other codes
end
It requires that the update of name and email and the update of password are separated.
When only update name and password, using update or update_attributes will cause password validation which is not needed. But using update_attribute will save name and email without validation.
Are there any ways to update particular fields of model with validation without causing the other fields' validation?
Give it a try, might help
class User < ActiveRecord::Base
validates :name, presence: true
validates :email, presence: true, :uniqueness => true
validates :plain_password, length: { in: 4..255, allow_nil: true }, confirmation: true
validates :plain_password_confirmation, presence: true, if: -> (user){ user.plain_password.present? }
# ......
# ......
end
Apart from this you should reconsider about saving plain_password ;)
You can adjust your validations to only run on create. Requiring confirmation ensures changes on edit are applied.
validates :plain_password,
confirmation: true,
presence: {
on: :create },
length: {
minimum: 8,
allow_blank: true }
validates :plain_password_confirmation,
presence: {
on: :create }
I am assuming you are hashing your passwords, so this would accompany code similar to:
attr_accessor :plain_password
before_save :prepare_password
def encrypted_password( bcrypt_computational_cost = Rails.env.test? ? 1 : 10)
BCrypt::Password.create plain_password, cost: bcrypt_computational_cost
end
private #===========================================================================================================
# Sets this users password hash to the encrypted password, if the password is not blank.
def prepare_password
self.password_hash = encrypted_password if plain_password.present?
end
A better way to handle this is to not include the fields in the rest of the edit form, but rather provide a link to "Change my password". This link would direct to a new form (perhaps in a modal window) which will require the new password, confirmation of the new password, and the old password, to prevent account hijacking.
In your case you can use has_secure_password The password presence is only validated on creation.

How to modify input parameters (which then get saved) on new user creation in rails?

I think this is a simple problem. So far I've ran
rails generate scaffold User username:string email:string password:string
to make a new scaffold for the User model. The following is my user.rb:
class User < ActiveRecord::Base
validates :username, presence: true, length: { in: 2..50 }, uniqueness: true
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: true
validates :password, presence: true, length: { in: 4..50}
#self.password = 'abcd' #I can't even change the parameter to something hard-coded!
end
I've written a few tests and that works great. My next step is to put the password parameter through a hashfunction (which I want to write myself for educational purposes) and save this newly modified string instead of the original string. I don't seem to understand how to do this? Do I create a method in user.rb which gets called from the users_controllers.rb under the create method?
I would like to test this by doing rails console --sandbox and writing some tests, too.
You can use the before_save callback
# user.rb model
before_save :hash_password
def hash_password
self.password = some_hash_function(self.password)
end
You have to be careful with this method not to hash the password multiple times. That is you must always hash the clear password and not hash the hashed version. That's why I would do it like this and call the field password_digest and only hash the password if the password attribute is set.
# user.rb model
attr_accessor :password
before_save :hash_password
def hash_password
self.password_digest = some_hash_function(self.password) unless self.password.blank?
end
http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html

Custom user authentication on Rails 4

I'm trying to learn and use my own user model and authentication with Ruby on Rails 4.0, but most tutorials (if not all) seem to be outdated with this recent update. No method described on any of them works. I'm absolutely clueless, this is my User model:
class User
include Mongoid::Document
field :login, type: String
field :hash, type: String
field :salt, type: String
field :email, type: String
field :name, type: String
before_save :hash_password
validate :login, presence: true, uniqueness: true, length: { in: 4..24 }
validate :password, presence: true, confirmation: true, length: { in: 8..32 }
validate :email, presence: true
validate :name, presence: true
def hash_password
if password.present?
self.salt = BCrypt::Engine.generate_salt
self.hash = BCrypt::Engine.hash_secret(password, salt)
end
end
end
And the controller:
class UsersController < ApplicationController
def create
#user = User.new(user_params)
if #user.save
redirect_to #user
else
render 'new'
end
end
def new
#user = User.new
end
private
def user_params
params.require(:user).permit(:login, :password, :password_confirmation, :email, :name)
end
end
I then get an UnknownAttribute error when I save the user. What am I missing? What fields are wrong?
You need to add:
attr_accessor :password
Right now you are trying to use the password attribute but rails doesn't know anything about it. attr_accessor allows you to use the password attribute locally, but will not persist it to the database (which is good).
You can follow rail casts episode. I know its a bit old but i also used it a couple of months ago and for the rails 4 you can simply "permit" the attributes rather than using "attr_accessible" as rails 4 doesn't support attr_accessible.
I managed to make it work. I didn't figure out what was the matter with the "unknown attribute" error, but I changed the model to use the method has_secure_password, which automagically takes my :password and :password_confirmed parameters, bcrypts and save to :password_digest field.
class User
include Mongoid::Document
include ActiveModel::SecurePassword # important, imports has_secure_password
field :login, type: String
field :password_digest, type: String
has_secure_password
field :email, type: String
field :name, type: String
validate :login, presence: true, uniqueness: true, length: { in: 4..24 }
validate :password, presence: true, confirmation: true, length: { in: 8..32 }
validate :email, presence: true, uniqueness: true
validate :name, presence: true
end
This done, I got the following error:
can't activate bcrypt-ruby (~> 3.0.0), already activated bcrypt-ruby-3.1.2. Make sure all dependencies are added to Gemfile.
Even though bcrypt was correctly added to my Gemfile, apparently has_secure_password needs specifically version 3.0.x of the gem, so I forced it:
gem 'bcrypt-ruby', '~> 3.0.0'
This downloaded the version 3.0.1 (not 3.0.0) which worked as expected. I hope they fix this version incompatibility soon.
Thanks for all the answers. This project will be available open source when I finish :)

Rails - How to disallow specific inputs in model validations?

My User model has an attribute called :profile_name which is used in routing profile page url's - domain.com/:profile_name . In order to prevent collision with my other views I want to make sure a User can't choose something like "friends" or "feed" as their profile name. How can I set this in validations?
/models/user.rb (currently):
...
validates :email, presence: true, uniqueness: true
validates :profile_name, presence: true,
uniqueness: true,
format: {
with: /^[a-zA-Z0-9_-]+$/,
message: 'Must be formatted correctly.'
}
...
The exclusion validation helper:
validates :profile_name, presence: true,
...,
exclusion: {
in: ["friends", "feed"],
message: "Profile name %{value} is reserved."
}
Use a custom validation method. You'd probably want to separate out the forbidden list, but I kept this extra concise.
class User < ActiveRecord::Base
validates :profile_not_forbidden
protected
def profile_not_forbidden
if ['friends','feed'].include?(profile_name)
errors.add(:profile_name, 'Forbidden profile name.')
end
end
end

Resources