2

I’m relatively new to Ruby, Sinatra, and DataMapper, but have a question about DataMapper validation errors.

I know you can see any errors that occur when attempting to save a new row to the database with DataMapper by doing something like the following:

user = User.new username: 'bradleygriffith', password: 'not_my_password'
if user.save
  #success!
else
  user.errors.each do |error|
    puts error
  end
end

What I would like to be able to do is determine on which property the error occurred. This way, for example, I might be able to place error messages next to the appropriate fields in my registration form. That is, I want to know that the registrant entered, say, an invalid username before displaying the error message so that I could place the message along-side the username field.

Is this possible?

matt
  • 78,533
  • 8
  • 163
  • 197
bradleygriffith
  • 948
  • 3
  • 11
  • 24

3 Answers3

2

The errors object is an instance of DataMapper::Validations::ValidationErrors which has an on method that will return an array containing all the validation error messages for the property you pass as a parameter, or nil if there are no errors. (It looks like those docs don't actually match the implementation).

user = User.new username: 'joe', :age => 40

if user.save
  #success!
else
  puts "Username: #{user.username} #{user.errors.on(:username)}"
  puts "Age: #{user.age} #{user.errors.on(:age)}"
end

produces (with suitable validations set up):

Username: joe ["Username must be between 4 and 20 characters long"]
Age: 40 
matt
  • 78,533
  • 8
  • 163
  • 197
1

I found that the following allows you to get the errors specific to a field:

user.errors.keys.each do |key|
  user.errors[key].each do |error|
    puts "#{key} => #{error}"
  end
end
1

All the items in the errors attribute of your model class are instances of DataMapper::Validation::Violation which have a property called attribute_name.

Check out line 60 of the datamapper source.

#...
def attribute_name
  if @attribute_name
    @attribute_name
  elsif rule
    rule.attribute_name
  end
end
#...

So that means you can do this

user = User.new username: 'bradleygriffith', password: 'not_my_password'
 if user.save
    #success!
 else
    user.errors.each do |error|
        #print the name of the invalid attribute
        puts error.attribute_name
    end
 end

The reason this may not have been apparent is because Violation's #to_s method is aliased to message and puts will call #to_s on any object passed to it, so when you call puts error it actually prints error.message.

jacobsimeon
  • 2,012
  • 1
  • 18
  • 20
  • 1
    Im getting: "NoMethodError at /join. undefined method 'attribute_name' for ["Username is already taken"]:Array" Any thoughts? – bradleygriffith Apr 02 '12 at 18:31
  • hmm, do this `puts error[0].class.name` to see what kind of object is in the array. It looks like a string, but it might be tricking us. Let me know if you figure this one out. – jacobsimeon Apr 02 '12 at 21:52