Using Rails I'm trying to get an error message like "The song field can't be empty" on save. Doing the following:
validates_presence_of :song_rep_xyz, :message => "can't be empty"
... only displays "Song Rep XYW can't be empty", which is not good because the title of the field is not user friendly. How can I change the title of the field itself ? I could change the actual name of the field in the database, but I have multiple "song" fields and I do need to have specific field names.
I don't want to hack around rails' validation process and I feel there should be a way of fixing that.
Now, the accepted way to set the humanized names and custom error messages is to use locales.
# config/locales/en.yml
en:
activerecord:
attributes:
user:
email: "E-mail address"
errors:
models:
user:
attributes:
email:
blank: "is required"
Now the humanized name and the presence validation message for the "email" attribute have been changed.
Validation messages can be set for a specific model+attribute, model, attribute, or globally.
In your model:
validates_presence_of :address1, message: 'Put some address please'
In your view
<% m.errors.each do |attr, msg| %>
<%= msg %>
<% end %>
If you do instead
<%= attr %> <%= msg %>
you get this error message with the attribute name
address1 Put some address please
if you want to get the error message for one single attribute
<%= #model.errors[:address1] %>
Try this.
class User < ActiveRecord::Base
validate do |user|
user.errors.add_to_base("Country can't be blank") if user.country_iso.blank?
end
end
I found this here.
Update for Rails 3 to 6:
validate do |user|
user.errors.add(:base, "Country can't be blank") if user.country_iso.blank?
end
Here is another way to do it.
What you do is define a human_attribute_name method on the model class. The method is passed the column name as a string and returns the string to use in validation messages.
class User < ActiveRecord::Base
HUMANIZED_ATTRIBUTES = {
:email => "E-mail address"
}
def self.human_attribute_name(attr)
HUMANIZED_ATTRIBUTES[attr.to_sym] || super
end
end
The above code is from here
Yes, there's a way to do this without the plugin!
But it is not as clean and elegant as using the mentioned plugin. Here it is.
Assuming it's Rails 3 (I don't know if it's different in previous versions),
keep this in your model:
validates_presence_of :song_rep_xyz, :message => "can't be empty"
and in the view, instead of leaving
#instance.errors.full_messages
as it would be when we use the scaffold generator, put:
#instance.errors.first[1]
And you will get just the message you specified in the model, without the attribute name.
Explanation:
#returns an hash of messages, one element foreach field error, in this particular case would be just one element in the hash:
#instance.errors # => {:song_rep_xyz=>"can't be empty"}
#this returns the first element of the hash as an array like [:key,"value"]
#instance.errors.first # => [:song_rep_xyz, "can't be empty"]
#by doing the following, you are telling ruby to take just the second element of that array, which is the message.
#instance.errors.first[1]
So far we are just displaying only one message, always for the first error. If you wanna display all errors you can loop in the hash and show the values.
Hope that helped.
Rails3 Code with fully localized messages:
In the model user.rb define the validation
validates :email, :presence => true
In config/locales/en.yml
en:
activerecord:
models:
user: "Customer"
attributes:
user:
email: "Email address"
errors:
models:
user:
attributes:
email:
blank: "cannot be empty"
In the custom validation method use:
errors.add(:base, "Custom error message")
as add_to_base has been deprecated.
errors.add_to_base("Custom error message")
Related to the accepted answer and another answer down the list:
I'm confirming that nanamkim's fork of custom-err-msg works with Rails 5, and with the locale setup.
You just need to start the locale message with a caret and it shouldn't display the attribute name in the message.
A model defined as:
class Item < ApplicationRecord
validates :name, presence: true
end
with the following en.yml:
en:
activerecord:
errors:
models:
item:
attributes:
name:
blank: "^You can't create an item without a name."
item.errors.full_messages will display:
You can't create an item without a name
instead of the usual Name You can't create an item without a name
One solution might be to change the i18n default error format:
en:
errors:
format: "%{message}"
Default is format: %{attribute} %{message}
I recommend installing the custom_error_message gem (or as a plugin) originally written by David Easley
It lets you do stuff like:
validates_presence_of :non_friendly_field_name, :message => "^Friendly field name is blank"
Here is another way:
If you use this template:
<% if #thing.errors.any? %>
<ul>
<% #thing.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
<% end %>
You can write you own custom message like this:
class Thing < ActiveRecord::Base
validate :custom_validation_method_with_message
def custom_validation_method_with_message
if some_model_attribute.blank?
errors.add(:_, "My custom message")
end
end
This way, because of the underscore, the full message becomes " My custom message", but the extra space in the beginning is unnoticeable. If you really don't want that extra space at the beginning just add the .lstrip method.
<% if #thing.errors.any? %>
<ul>
<% #thing.errors.full_messages.each do |message| %>
<li><%= message.lstrip %></li>
<% end %>
</ul>
<% end %>
The String.lstrip method will get rid of the extra space created by ':_' and will leave any other error messages unchanged.
Or even better, use the first word of your custom message as the key:
def custom_validation_method_with_message
if some_model_attribute.blank?
errors.add(:my, "custom message")
end
end
Now the full message will be "My custom message" with no extra space.
If you want the full message to start with a word capitalized like "URL can't be blank" it cannot be done. Instead try adding some other word as the key:
def custom_validation_method_with_message
if some_model_attribute.blank?
errors.add(:the, "URL can't be blank")
end
end
Now the full message will be "The URL can't be blank"
Just do it the normal way:
validates_presence_of :email, :message => "Email is required."
But display it like this instead
<% if #user.errors.any? %>
<% #user.errors.messages.each do |message| %>
<div class="message"><%= message.last.last.html_safe %></div>
<% end %>
<% end %>
Returns
"Email is required."
The localization method is definitely the "proper" way to do this, but if you're doing a little, non-global project and want to just get going fast - this is definitely easier than file hopping.
I like it for the ability to put the field name somewhere other than the beginning of the string:
validates_uniqueness_of :email, :message => "There is already an account with that email."
Here is my code that can be useful for you in case you still need it:
My model:
validates :director, acceptance: {message: "^Please confirm that you are a director of the company."}, on: :create, if: :is_director?
Then I have created a helper to show messages:
module ErrorHelper
def error_messages!
return "" unless error_messages?
messages = resource.errors.full_messages.map { |msg|
if msg.present? && !msg.index("^").nil?
content_tag(:p, msg.slice((msg.index("^")+1)..-1))
else
content_tag(:p, msg)
end
}.join
html = <<-HTML
<div class="general-error alert show">
#{messages}
</div>
HTML
html.html_safe
end
def error_messages?
!resource.errors.empty?
end
end
If you want to list them all in a nice list but without using the cruddy non human friendly name, you can do this...
object.errors.each do |attr,message|
puts "<li>"+message+"</li>"
end
In your view
object.errors.each do |attr,msg|
if msg.is_a? String
if attr == :base
content_tag :li, msg
elsif msg[0] == "^"
content_tag :li, msg[1..-1]
else
content_tag :li, "#{object.class.human_attribute_name(attr)} #{msg}"
end
end
end
When you want to override the error message without the attribute name, simply prepend the message with ^ like so:
validates :last_name,
uniqueness: {
scope: [:first_name, :course_id, :user_id],
case_sensitive: false,
message: "^This student has already been registered."
}
I tried following, worked for me :)
1 job.rb
class Job < ApplicationRecord
validates :description, presence: true
validates :title,
:presence => true,
:length => { :minimum => 5, :message => "Must be at least 5 characters"}
end
2 jobs_controller.rb
def create
#job = Job.create(job_params)
if #job.valid?
redirect_to jobs_path
else
render new_job_path
end
end
3 _form.html.erb
<%= form_for #job do |f| %>
<% if #job.errors.any? %>
<h2>Errors</h2>
<ul>
<% #job.errors.full_messages.each do |message|%>
<li><%= message %></li>
<% end %>
</ul>
<% end %>
<div>
<%= f.label :title %>
<%= f.text_field :title %>
</div>
<div>
<%= f.label :description %>
<%= f.text_area :description, size: '60x6' %>
</div>
<div>
<%= f.submit %>
</div>
<% end %>
A unique approach I haven't seen anyone mention!
The only way I was able to get all the customisation I wanted was to use an after_validation callback to allow me to manipulate the error message.
Allow the validation message to be created as normal, you don't need to try and change it in the validation helper.
create an after_validation callback that will replace that validation message in the back-end before it gets to the view.
In the after_validation method you can do anything you want with the validation message, just like a normal string! You can even use dynamic values and insert them into the validation message.
#this could be any validation
validates_presence_of :song_rep_xyz, :message => "whatever you want - who cares - we will replace you later"
after_validation :replace_validation_message
def replace_validation_message
custom_value = #any value you would like
errors.messages[:name_of_the_attribute] = ["^This is the replacement message where
you can now add your own dynamic values!!! #{custom_value}"]
end
The after_validation method will have far greater scope than the built in rails validation helper, so you will be able to access the object you are validating like you are trying to do with object.file_name. Which does not work in the validation helper where you are trying to call it.
Note: we use the ^ to get rid of the attribute name at the beginning of the validation as #Rystraum pointed out referencing this gem
graywh's answer is the best if it actually locales different in displaying the field name. In the case of a dynamic field name (based on other fields to display), I would do something like this
<% object.errors.each do |attr, msg| %>
<li>
<% case attr.to_sym %>
<% when :song_rep_xyz %>
<%= #display error how you want here %>
<% else %>
<%= object.errors.full_message(attr, msg) %>
<% end %>
</li>
<% end %>
the full_message method on the else is what rails use inside of full_messages method, so it will give out the normal Rails errors for other cases (Rails 3.2 and up)
Upgraded #Federico answer to be universal for all field errors.
In your controller.rb:
flash[:alert] = #model.errors.messages.values
# [["field1_err1", "field1_err2"], ["field2_err1"], ["field3_err1"]]
messages method "returns a Hash of attributes with an array of their error messages" as in rails docs.
Then, to display those errors in a form:
<% flash.each do |type, type_arr| %>
<% type_arr.each do |msg| %>
<ul>
<li>
<%= msg.to_sentence %>
</li>
</ul>
<% end %>
<% end %>
I'm new on rails. I work on a project, and I try to add physical address to my users. I want that the address can be help-complete with Google map for later exploitation. I find the jt-rails-address which look like perfect for my project. But I can't implement it. I need complete address (street, zip code, city & country).
add_address_to_users.rb :
class AddAddressToUsers < ActiveRecord::Migration[5.1]
def change
add_column :users, :address, :address
end
end
form edit.html.erb :
<div class="col-md-6 col-md-offset-3">
<%= form_for (#user), :html => { :multipart => true } do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :phone, "Téléphone :" %>
<%= f.phone_field :phone, class: 'form-control' %>
<%= f.label :address, "Addresse :" %>
<div class="jt-address-autocomplete">
<!-- This field is used to search the address on Google Maps -->
<%= f.text_field :address, class: 'jt-address-search' %>
<!-- All fields are hidden because the javascript will set their value automatically -->
<% for attr in JT::Rails::Address.fields %>
<%= f.hidden_field "address_#{street}", class: "jt-address-field-#{street}" %>
<% end %>
<% for attr in JT::Rails::Address.fields %>
<%= f.hidden_field "address_#{zip_code}", class: "jt-address-field-#{zip_code}" %>
<% end %>
<% for attr in JT::Rails::Address.fields %>
<%= f.hidden_field "address_#{city}", class: "jt-address-field-#{city}" %>
<% end %>
<% for attr in JT::Rails::Address.fields %>
<%= f.hidden_field "address_#{country}", class: "jt-address-field-#{country}" %>
<% end %>
</div>
<%= f.submit "Enregistrer les changements", class: "btn btn-primary" %>
<% end %>
</div>
application.js :
// This function is call when Google Maps is loaded
window.googleMapInitialize = function(){
// Simple usage
$('.jt-address-autocomplete').jt_address();
};
I have already put :address in my user_params in the users controller and has_address :address in the user model
I also have put my Google Api.
My actual error:
undefined local variable or method `street' for #<#Class:0x00007fe91ec7f768:0x00007fe91dfaefc0>
Thanks in advance for your help.
No more error but a bug, maybe because of Google Map, I don't know how
to resolve it, I can't write the address and there is an error message:
problem
Here the code of the inspector:
First, you should add fields like in documentation or scope them using select, now you have code that will create duplicated hidden fields.
You should check from rails console that method address is available on User instance of model.` Maybe you forgot to run migration or didn't restart server after installing this gem as this is js gem and will need restart.
Hope this answers your questions. Add comment to this answer if problem is still there.
class Person < ActiveRecord::Base
before_save {self.email = email.downcase}
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, length: { maximum: 6}
end
I am really new to Ruby. I was learning from Michael Hartl's tutorial. When creating signup form (tutorial 7) I got stuck in displaying error messages ( like if we leave any field blank there should be notifications in red right?).I added error_messages.html.erb file. Rendered it in form. Still there are no messages.
What I guessed is, I am using :user in my form creation to save the user. where as it should be #user. So that it can create user?
but when I did so, It gave me anonymous error of user_path. I searched for that display of error messages on stack but was not able to find.
Kindly help me with it. I am stuck since very long.
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(:person) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :email %>
<%= f.email_field :email %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation %>
<%= f.submit "Create my account", class: "btn btn-primary" %>
<% end %>
</div>
</div>
<% if #person.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger">
the form contains <%= #person.errors.count %> errors.
</div>
<ul>
<%= #person.errors.full_messages.each do |msg| %>
<li> <%= msg %> </li>
<% end %>
</ul>
</div>
<% end %>
class PersonsController < ApplicationController
def show
#person = Person.find(params[:id])
end
def new
#person = Person.new
end
def create
#person = Person.new(user_params)
if #person.save
else
render new
end
end
private
def user_params
params.require(:person).permit(:name, :email, :password, :password_confirmation)
end
end
Rails.application.routes.draw do
get 'persons/show'
root 'staticpages#home'
get 'help' => 'staticpages#help'
get 'about' => 'staticpages#about'
get 'signUp' => 'persons#new'
resource :person
resources :persons
end
// error when i use #user in form. Instead of :user
NoMethodError in PersonsController#new
undefined method `users_path' for #<#<Class:0x007fdb68c0ee78>:0x007fdb68c0e428>
Extracted source (around line #220):
if options.empty?
recipient.send(method, *args)
else
recipient.send(method, *args, options)
end
Based on the code I have seen in the GitHub repo, the major problem I identified was with the naming. There was a large level of conflict in the name person in that its plural is people and not persons.
Rails has its way of pluralizing model names so User has a UsersController and Person has PeopleController and so forth.
Following are the changes required to fix the problems with sign up:
You need to change your controller name from PersonsController to PeopleController (both class name and file name).
In the PeopleController, the strong parameters method Person_params needs to change to lowercase (person_params). Otherwise, Rails would treat it as a constant name and start hunting for it and wouldn't find it.
You'll have to update the persons folder name in the app/views folder to people for the same reason specified above.
In your routes.rb file, there are two changes required. Change resource :person to resources :people and get 'signUp' should match to 'people#new' instead of 'persons#new' (now you know why).
Create a new file in app/views/people called create.html.erb. Once a person is created, Rails automatically renders this file and will throw an exception if it does not find it.
In the file app/views/people/new.html.erb, change the variable passed to form_for from :person to #person. #person is an instance variable initialized when that page is visited (see the new action in the PeopleController).
I'm not sure if this change will be needed, but if you face any errors after making the above changes, add gem 'therubyracer' to your Gemfile and run bundle install. This will install the rubyracer gem which I believe will be required for this version of Rails.
You should really familiarize yourself with the fundamentals of the framework and the Ruby language. It'll help you identify and debug these issues yourself. But I figured since you're new to it, this lengthy answer might help get rid of your frustration :).
You have used the User model in multiple places. The actual model is called Person. Switch to this model name in all the controllers and the model class.
And I'd also like to make a suggestion. When studying the tutorial, please do not blindly copy and paste snippets. Understand what their purpose is and write them in your files on your own.
I have a form that I've created to capture simple contact information from a user:
views/whitepapers/index.html.erb
<%= form_tag({action: "download"}, id: "whitepaper-form-#{w.id}") do %>
<%= label_tag 'name' %>
<%= text_field_tag "contact[name]", nil, class: "form-control" %>
<br/>
<%= label_tag 'email' %>
<%= email_field_tag "contact[email]", nil, class: "form-control" %>
<br/>
<%= label_tag 'phone' %>
<%= text_field_tag "contact[phone]", nil, class: "form-control" %>
<%= hidden_field_tag 'id', w.id %>
<%= hidden_field_tag 'whitepaper-name', w.title %>
<%= submit_tag "Download Now", class: "btn btn-success", id: "whitepaper-# {w.id}-submit" %>
<% end %>
Now, Once the user clicks the "Download" button, the file downloads, so I have that part taken care of. Now I'd like to email the form data without saving anything to the DB.
I've created the mailer: mailers/whitepaper_download_mailer.rb
class WhitepaperDownloadMailer < ApplicationMailer
def email_lead(contact)
#contact = contact
mail to: "admin#example.co", subject: "A Whitepaper Download!"
end
end
And I've started working on implementing in the controller, but all the examples I've run across have to do with data including the model. This is what I have so far, but it's not working in my controller:
controllers/whitepapers.rb
def download
#whitepaper = Whitepaper.find(params[:id])
#contact.name = params[:contact_name]
#contact.email = params[:contact_email]
#contact.phone = params[:contact_phone]
#contact.whitepaper_name = params[:whitepaper_name]
file_path = File.join(Rails.root, "public", #whitepaper.whitepaper_url)
send_file file_path
WhitepaperDownloadMailer.email_lead(#contact).deliver_now
end
models/whitepaper.rb
class Whitepaper < ActiveRecord::Base
mount_uploader :whitepaper, WhitepaperUploader
validates :title, presence: true
validates :abstract, presence: true
validates :whitepaper, presence: true
end
Obviously, I know this isn't going to work since I'm passing #contact to the mailer, but pulling form params into a structure (i.e. #contact.name). Should I be passing each of the parameter variables into the mailer:
WhitepaperDownloadMailer.email_lead(#contact.name, #contact.email, #contact.phone).deliver_now
Or is there some other way that I haven't found yet to make this mailer work?
I figured this out with help from #kevinthompson and Openstruct. So, directly from the form, in my controller controllers/whitepapers.rb:
def contact
#whitepaper = Whitepaper.find(params[:contact][:whitepaper_id])
file_path = File.join(Rails.root, "public", #whitepaper.whitepaper_url)
send_file file_path
if request.post?
#contact = OpenStruct.new(params[:contact])
WhitepaperDownloadMailer.email_lead(#contact).deliver_now
end
end
I also ended up changing the form_tag action in the view to coincide:
<%= form_tag({action: "contact"}, id: "whitepaper-form-#{w.id}") do %>
I am very new to Rails and I am facing a somehow weird problem, and my googling didn't helped me so far...
I have implemented a classical CRUD ressource following the Getting Started with Rails guide and I am blocking on the "update" part:
http://guides.rubyonrails.org/getting_started.html#updating-articles
This is part of my model "Devwork":
class Devwork < ActiveRecord::Base
validates :short_title, presence: true, uniqueness: true
validates :title_fr, presence: true, allow_blank: false
translates :title, :summary, :description
globalize_accessors
end
I am using the Globalize gem to persist localized data, and Globalize-accessor for the helpers.
Here is the controller's update action:
class DevworksController < ApplicationController
def update
#devwork = Devwork.find(params[:id])
if #devwork.update(devwork_params)
redirect_to #devwork
else
render :edit
end
end
private
def devwork_params
params.require(:devwork)
.permit!
end
end
And part of the form:
<%= form_for #devwork do |f| %>
<p>
<%= f.label :short_title %>
<%= f.text_field :short_title %>
</p>
<p>
<%= f.label :title_fr %>
<%= f.text_field :title_fr %>
<%= f.label :title_en %>
<%= f.text_field :title_en %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
short_title and title_fr are mandatory, while there exist another field title_en which is not. I want the update form to be shown again if the update fails (typically because of empty title_fr).
But it doesn't work. The update never fails (never entering render :edit) even if title_fr is empty. In fact, the update does nothing if one of the field is empty, it only updates non-empty fields.
I surely missed something somewhere, but I can't figure it out... perhaps a missuses of Globalize ?
Thanks for your help !