1

I frequently need to see the error messages associated with various database ROLLBACKs along the lines of that which is seen here:

project = Project.find 118
project.assign_attributes(featured: true)
project.valid?
project.errors.full_messages

Simply appending .errors.full_messages is not too laborious, however, when we haven't instantiated some object (in this case project) then it becomes more tedious because an object has to be instantiated, which involves changing code (just to see an error message).

Example:

User.create(name: "john", email: "john@gmail.com", .... etc)

would have to be refactored to something like

user = User.new(name: "john", email: "john@gmail.com", .... etc)
user.save

# then type

user.errors.full_messages

all of this to simply see an error message.

Question

Is there some way of making error messages always display (in full) without any need to specifically request them with something like .errors.full_messages? (in both the rails console and server)

I will try anything - global settings, gems, hacks - whatever it takes

stevec
  • 41,291
  • 27
  • 223
  • 311
  • 1
    Wanting to see errors "when we haven't instantiated some object" -> could you elaborate on that a bit? Maybe include the code you'd like to have, the output when valid, and invalid? – Jake Worth Sep 12 '20 at 16:15
  • @JakeWorth tbh, the code I have is `User.create(name: "john", email: "john@gmail.com", .... etc)` and it errors, and I prefer to have to change to `user = User.new(name: "john", email: "john@gmail.com", .... etc)` in order to simply see error messages – stevec Sep 12 '20 at 16:17
  • @JakeWorth it's in `seeds.rb`. I have fixed the specific error now, by refactoring in the way described above, but my question is just around how I can avoid having to refactor code just to see an error message, that is, always show error messages by default (just have them plonked into the console without having to ask for them via `.errors..` – stevec Sep 12 '20 at 16:19

1 Answers1

1

Use a bang (!).

On a model that requires many things (including a title), create fails and triggers your rollback:

> Post.create(title: nil)
   (0.1ms)  BEGIN
  Post Exists (0.2ms)  SELECT  1 AS one FROM "posts" WHERE "posts"."slug" IS NULL LIMIT $1  [["LIMIT", 1]]
   (0.1ms)  ROLLBACK
=> #<Post:0x007fa44c934fc0
 id: nil,
 developer_id: nil,
 body: nil,
 created_at: nil,
 updated_at: nil,
 channel_id: nil,
 title: nil,
 slug: nil,
 likes: 1,
 tweeted: false,
 published_at: nil,
 max_likes: 1>

With the bang, the creation fails fast and an RecordInvalid error is raised:

> Post.create!(title: nil)
   (0.1ms)  BEGIN
  Post Exists (0.2ms)  SELECT  1 AS one FROM "posts" WHERE "posts"."slug" IS NULL LIMIT $1  [["LIMIT", 1]]
   (0.1ms)  ROLLBACK
ActiveRecord::RecordInvalid: Validation failed: Body can't be blank, Channel can't be blank, Developer can't be blank, Title can't be blank
from /Users/dev/.asdf/installs/ruby/2.3.3/lib/ruby/gems/2.3.0/gems/activerecord-5.0.1/lib/active_record/validations.rb:78:in `raise_validation_error'

To build on the OP, one could use update_attributes and update_attributes! to produce the two behaviors:

> Post.first.update_attributes(title: nil)
  Post Load (0.2ms)  SELECT  "posts".* FROM "posts" ORDER BY "posts"."id" ASC LIMIT $1  [["LIMIT", 1]]
   (0.1ms)  BEGIN
  Developer Load (0.2ms)  SELECT  "developers".* FROM "developers" WHERE "developers"."id" = $1 LIMIT $2  [["id", 4], ["LIMIT", 1]]
  Post Exists (0.2ms)  SELECT  1 AS one FROM "posts" WHERE "posts"."slug" = $1 AND ("posts"."id" != $2) LIMIT $3  [["slug", "81e668bc4e"], ["id", 1], ["LIMIT", 1]]
   (0.1ms)  ROLLBACK
=> false

> Post.first.update_attributes!(title: nil)
  Post Load (0.2ms)  SELECT  "posts".* FROM "posts" ORDER BY "posts"."id" ASC LIMIT $1  [["LIMIT", 1]]
   (0.1ms)  BEGIN
  Developer Load (0.2ms)  SELECT  "developers".* FROM "developers" WHERE "developers"."id" = $1 LIMIT $2  [["id", 4], ["LIMIT", 1]]
  Post Exists (0.2ms)  SELECT  1 AS one FROM "posts" WHERE "posts"."slug" = $1 AND ("posts"."id" != $2) LIMIT $3  [["slug", "81e668bc4e"], ["id", 1], ["LIMIT", 1]]
   (0.1ms)  ROLLBACK
ActiveRecord::RecordInvalid: Validation failed: Title can't be blank
from /Users/dev/.asdf/installs/ruby/2.3.3/lib/ruby/gems/2.3.0/gems/activerecord-5.0.1/lib/active_record/validations.rb:78:in `raise_validation_error'

What is !?

! in Ruby often means the method will modify the object it is called on. However, ActiveRecord has a different convention; ! methods "are stricter in that they raise the exception."

create docs validation docs

Jake Worth
  • 5,490
  • 1
  • 25
  • 35
  • Thanks! When is it safe / not safe to simply append a `!` ? (e.g. I believe `!` has another meaning in other contexts?) Would it be true to say that we can use the bang on database operations to get the desired outcome above (error messages printed)? – stevec Sep 12 '20 at 16:27
  • `!` in Ruby often means the method will modify the object it is called on; see: https://stackoverflow.com/a/612196/2112512 – Jake Worth Sep 12 '20 at 16:32
  • 1
    ActiveRecord has a different convention; bang methods "are stricter in that they raise the exception" https://guides.rubyonrails.org/active_record_basics.html#validations – Jake Worth Sep 12 '20 at 16:33
  • 1
    @stevec I'd say in an ActiveRecord context, `!` is safe. I would generally prefer to fail fast with an exception. – Jake Worth Sep 12 '20 at 16:35
  • 1
    I guess it's down to personal taste, but to me seems there is never any reason to *not* want the error message. Hiding it seems like a strange default. I guess it declutters the console, but that's a small benefit I think. I wish there was a global setting `always.show.errors = true`. But I think using `!` for all ActiveRecord work is a good solution – stevec Sep 12 '20 at 16:36
  • @stevec "Hiding it seems like a strange default" - nobody is hiding anything. `#create` return the record. `u = User.create; u.errors.full_messages` – Sergio Tulentsev Sep 12 '20 at 18:58
  • @SergioTulentsev I deem any message not explicitly printed to console as hidden. I guess it's no longer hidden when you know where to look. But I'd argue things as important as error messages ideally shouldn't require any special knowledge around where to look, they ought to be printed to console by default, but hidden if some option(s) are set by the user. – stevec Sep 12 '20 at 19:01
  • 1
    @stevec: I see. That's where we disagree. Also, it was more of a counterpoint to your "you'd have to rewrite this code to this form" - no, you don't. You can keep using `create`. Also, I'd wage that your desired behaviour can be implemented with a 5 LOC patch/mixin. But if failing with an exception is also acceptable in your situation, then no need to involve patching. – Sergio Tulentsev Sep 12 '20 at 19:06
  • @SergioTulentsev the patch/mixin would be very handy. The general problem for me is I would like to configure ruby/rails to always print error messages (no exceptions, ever). As in, so I could be 100% confident that all error messages had been printed to console/server logs 100% of the time. That would be very useful – stevec Sep 12 '20 at 19:11