I use 'nokogiri' among others to check the schema from some uploaded xml's. And i will print out all errors which occurs:
xsd.validate(doc).each do |error|
flash[:error] = error.message
end
If I do so, I see only the last added error, if more than one exists.
I find also find a similar question, about this problem rails-easy-way-to-add-more-than-one-flashnotice-at-a-time but the accepted solution dosen't work for me.
Thanks
change the method to
flash[:error] = xsd.validate(doc).map(&:message).to_sentence
UPDATE
Using br tags to separate each error
flash[:error] = xsd.validate(doc).map(&:message).join('<br>').html_safe
I find also find a similar question, about this problem
rails-easy-way-to-add-more-than-one-flashnotice-at-a-time but the
accepted solution dosen't work for me.
In what way doesn't it work for you?
You can add your own flash types like flash[:errors] and write your own helper methods for convenience.
def my_flash(type, message)
flash[type] ||= []
flash[type] += Array.wrap(message)
end
Then you can use an array or a string as the message, making it easy to pass multiple in, like so.
my_flash :errors, "name cannot be blank"
my_flash :errors, ["age must be greater than 17", "phone number is invalid"]
p flash[:errors]
#=> ["name cannot be blank", "age must be greater than 17", "phone number is invalid"]
Related
I have code in my Rails app that allows me to export a CSV file. It works fine unless there is a record that has a field with no value in it. In that case it fails. As an example, the specific failure I'm getting is saying something liek "No Method Error" and it specifically references "address_line_1" because there are some users with no address_line_1. That is just one example though. Really all fields should be protected against potential blanks. Here is the code:
def download_kids_csv
#csv_headers = ['First',
'Last',
'Child First',
'Child Last',
'Parent Email',
'School',
'Class',
'Address',
'City',
'State',
'Zip',
'Parent Phone']
#kid_data = []
#school = School.find(params[:school_id])
#school.classrooms.each do |classroom|
classroom.kids.includes(:users).each do |kid|
kid.users.each do |parent|
#kid_data << {
first: parent.first_name,
last: parent.last_name,
child_first: kid.first_name,
child_last: kid.last_name,
parent_email: parent.email,
school: #school.name,
class: classroom.classroom_name,
address: parent.addresses.first.address_line_1,
city: parent.addresses.first.city,
state: parent.addresses.first.state,
zip: parent.addresses.first.zip_code,
parent_phone: parent.phones.first.phone_number
}
end
end
end
respond_to do |format|
format.csv do
headers['Content-Disposition'] = "attachment; filename=\"#{#school.name.downcase.gsub(' ', '-')}-data.csv\""
headers['Content-Type'] ||= 'text/csv'
end
end
end
Ok so the problem you are get is because you are calling method on a nil value.
So for example when you do:
kid.first_name
and kid is nil you are doing this
nil.first_name
nil does not implement the first_name method so it throws an error. WHat you could do to circumvent this (its kinda ugly) is this
kid.try(:first_name)
This will prevent you form getting those method missing errors
For those long chains you can do the following
parent.try(:addresses).try(:first).try(:zip_code)
This should save you a lot of headache, but the root cause of your issue is data integrity you would not have to do all of this if you ensured that your data was not blank. I do however understand in the real world it easier said than done. I could give you a lecture about The Law of Demeter and how you should not be running across object to access their attributes, and how thats a code smell of bad organization of data, but its a spread sheet and sometimes you just need the data. Good luck!
To build off of the earlier answer, you can also utilize the so-called lonely operator &. if you're on Ruby 2.3.
An example would look something like this: kid&.first_name.
If you're not on that version of ruby yet, there's a good gem that can help you out in this situation that's a little bit more robust than .try.
Using that gem your code would look like kid.andand.first_name. It might be overkill in this case but the difference here is that it will only perform the first_name method call if kid is not nil. For your longer chains, parent.address.first.zip_code, this would mean that the function chain would exit immediately if parent was nil instead of calling all of the different attributes with try.
Is it possible to use unless or another conditional?
unless parent.addresses.first.address_line_1.blank?
address: parent.addresses.first.address_line_1,
end
or
if parent.addresses.first.address_line_1 != nil
address: parent.addresses.first.address_line_1,
else
address: nil || "address is empty"
end
If I don’t have row with id=params[:id] how can i check it, since
when I write
def show
#post=Post.find(params[:id])
if #post.nil?
#post={
title:"No such post",
post:"No such post"
}
end
end
I get error.
From the fine manual:
find(*args)
Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]). If no record can be found for all of the listed ids, then RecordNotFound will be raised.
So if find can't find anything with the id you're looking for, it raises an ActiveRecord::RecordNotFound exception rather than return nil like you want it to. That exception ends up being handled deep inside Rails and gets converted to a 404.
You could trap that exception yourself:
def show
#post = Post.find(params[:id])
rescue ActiveRecord::RecordNotFound
#post = {
title: "No such post",
post: "No such post"
}
end
Note that you'd only trap the specific exception you're expecting to see, a bare rescue is almost always a mistake because it can hide bugs.
You could also use find_by:
find_by(*args)
[...]
If no record is found, returns nil.
like this:
def show
#post = Post.find_by(:id => params[:id])
if #post.nil?
#post = {
title: "No such post",
post: "No such post"
}
end
end
Exceptions are meant for handling errors and other exceptional conditions, they're not meant to be used for normal flow control. I'd probably use find_by for this sort of thing; it seems that you're expecting the occasional missing record so it a missing record isn't really an error or an unexpected condition.
show controller is expected to show existing elements only. When an element (Post instance) does not exist, find throws an exception. As #Michal suggested in the comments, usually non-existing entities are being handled with 404 response, or like.
For the time, being, though, you might cheat Rails with:
#post = Post.find(params[:id]) rescue {
title: "No such post",
post: "No such post"
}
This is not a production solution, of course, but it might help during learning phase.
I have the following code:
def register_learner
#event = Event.find(params[:event_id])
#registation = EventRegistration.new first_name: params[:first_name], last_name: params[:last_name], email: params[:email], event_id: params[:event_id]
if !#registation.valid?
#registation.errors.full_messages.delete("Event has already been taken"
flash[:notice] = #registation.errors.full_messages.to_sentence
redirect_to(event_path(#event))
else
#registation.save
end
end
Note the line #registation.errors.full_messages.delete("Event has already been taken") where I am trying to delete this particular message from the full_messages array, however it does not work. The next line is the flash message, and the message "Event has already been taken" is still being displayed.
Here is a sanity check via the console...
2.1.5 :001 > errors = ["Event has already been taken", "Last name can't be blank"]
=> ["Event has already been taken", "Last name can't be blank"]
2.1.5 :002 > errors.delete "Event has already been taken"
=> "Event has already been taken"
2.1.5 :003 > errors
=> ["Last name can't be blank"]
What am I missing?
This is because full_messages is a method, which gerneates a new array every time you call it. To do what you want:
errors = #registation.errors.full_messages
errors.delete("Event has already been taken")
flash[:notice] = errors.to_sentence
That answers the question, now there is a matter of - why do you need to do this? There might be a better way.
In general relying on strings is usually a bad idea, imagine that in a half a year you will need to change an error message for this validation. Can you be 100% sure you will remember to change it here as well? If not, you have a bug.
Well, because Rails re-creates #full_messages using errors every time you call the method:
def full_messages(options = {})
#errors.values.inject([]) do |full_messages, errors|
full_messages + errors.map { |error| error.full_message }
end
end
Source
You can use #select to skip that message:
#registation.errors.full_messages.select{|x| x != "Event has already been taken"}.to_sentence
Also, you can use #delete on #errors.messages (not full_messages), because they are exposed attribute of the underlying object (not a copy, as with full_messages):
#registration.errors.messages[:event].delete('has already been taken')
My source is at: https://gist.github.com/f01685376a02a577a9cb
the method in question:
def is_valid_email?(address)
User.find_by_email(address)
end
I think the solution is to change the value for User.find_by_email(address) to something like User.find_by_email(next_approver_email) = (address) but I know that doesn't work.
Next_approver_email is what we are tying to check against the user.email db column
any ideas?
You need to pass the email into is_valid_email? at the end of line 5
def validate_each(approval, attribute, value)
approval.errors[attribute] << "must be a valid e-mail address in our system" unless is_valid_email?(value)
end
I have a seeds.rb file and for some reason, this doesn't work:
#doesn't work
u=User.new
u['email']=h['email']
u['password']=h['password']
puts u['email']
puts u['password']
if u.save
puts "that saved"
else
puts "that did not save"
end
but this does:
#does work
User.create({:email => h['email'], :password => h['password']})
Is there any reason one works and one doesn't? From rails console, the first does work? Would there be any differences in validations? I run 'rake db:seed' so would think validations would be in effect in both.
thx
edit #1
sorry for lack of info. each one errors with "password can't be blank" but it then echoes out the password. Hmm...
Find out what's happening by looking at the validation errors:
else
puts "that did not save because " + u.errors.join(' ')
end
Edit: Now that you've added that validation errors contain 'password cant be blank' and it still fails to save, it becomes clear that User.create() does a number of things which User.save() omits.
What's missing is the generation of your password hash - hence the blank password error.
Try it this way:
u = User.new
u.email = h['email']
u.password = h['password']
puts u.inspect
if u.save!
puts "that saved"
else
puts "that did not save"
end
I believe the approach of accessing the attribute on the u object as u['password'] is steering you wrong.
Also, doing a u.inspect prints the entire state of the model and that should provide a cleaner way of seeing what's happening. Finally, adding a bang ! at the end of the save will cause it to 'fail fast' and give you the error immediately.