> (irb):5:in `<main>': undefined method `name=' for <...> (NoMethodError)
> Did you mean? name
This error message is a bit roundabout. I assume the implication is that `name` is the getter and `name=` is the setter. And so, immutable objects don't have the setter method? But definitely could be worded much better than NoMethodError.
Disclaimer: I'm more of a Python/JS/TS person, and haven't used Ruby much.
This is how Ruby works — you don't directly access an object's instance variables outside of the object, you call getter methods (commonly created with attr_reader) or setter methods (commonly created with attr_writer, or attr_accessor). In this case, there is no setter. This is the same error you'd get if you tried to set a read-only attribute anywhere else.
And it's worth noting that, unlike with Python, this isn't Ruby internals letting an exception bubble up from somewhere; rather, `foo.bar = baz` is literally syntactic sugar for `foo.bar=(baz)`.
(All Ruby operators work this way: something like `foo + bar` is short for `foo.+(bar)` for example.)
Yeah. Non-Ruby devs are probably thinking, "what is the point of rules if the rules can be sidestepped so easily?"
But IMO/IME Ruby strikes a good balance here. You can break the rules but it really sticks out like a sore thumb. We avoid these backdoors like the plague.
How so? That's a day 1 experience for Ruby. Typos and calls for the methods on the wrong objects will always get you a NoMethodError. Anybody past their first Ruby tutorial will be familiar with this exception.
This is the kind of problem you encounter once, scratch your head, look it up on stack overflow, understand why it is, and then commit this little quirk to memory and it never really troubles you again.
(Or more realistically, your IDE underlines your error in red and you never even see the error message)
I like it because it encourages the newcomer to understand that the `=` "operator" in `foo.bar = 123` is actually just a part of a method named `bar=`.
Though, to be honest, rather than an "instructive" error message... I sort of wish the language syntax itself forbade that space and simply forced you to write `foo.bar= 123` instead of `foo.bar = 123`.
I wonder if Matz himself has ever second guessed that choice he made in the 90s? =)
> I sort of wish the language syntax itself forbade that space
This syntactic sugar is definitely a bit weird, but it seems necessary in the "everything is an object" design, and to simultaneously meet Ruby's prime directive of maximizing developer happiness.
Can you imagine the howling from the haters if `x = 1` was a syntax error?
This is a pretty basic facet of Ruby. You do not need extensive experience to be familiar with Ruby's approach to getters and setters. This will be surprising to people whose expectations come from other languages, but I doubt it would surprise any Ruby developer.
Agreed. The second chapter of Programming Ruby, "Classes, Objects, and Variables", shows you how to manually implement 'attributes' before introducing you to the attr_* methods. This is fundamental stuff.
Fair enough. In this case, since it subclasses/implements Immutable, it might be nice to call that out specifically in the error. You could immediately avoid thinking it's a typo or programmer error, but a deliberate feature.
I'm also not a ruby person but I'd think it would not be too hard to say "method 'name=' does not exist. Value objects do not provide setter methods by default." Or similar. It would almost be better if it didn't make the "did you mean?" suggestion which confuses the issue.
IMO the root issue is the language syntax allows you to write `foo.bar = 123` instead of `foo.bar= 123` which kind of obscures the fact that `bar=` is a method.
If you've never written Ruby I 100% agree that the NoMethodError is perhaps confusing. However as others have noted this is just a really simple core concept you learn right away! This is your standard "oops, I called something that doesn't exist" message and contains everything you need to see where you went wrong.
99% of the time that message is all you need; for the other 1% you can use a debugger to pause things and see e.g. what methods `foo` does support.
To be honest I did understand what was wrong from the error message on first read, despite not knowing ruby (but probably aided by experience in other languages with similar patterns). I think what the distant grandparent was noticing is valid though: the error message is accurately but tangential to the reason for the error. Sure, you can figure it out. Probably you can figure it out without needing to be a great master of the language. But to criticize somebody for wondering whether an error message could be more informative/relevant, when that is clearly possible, I find galling.
> (irb):5:in `<main>': undefined method `name=' for <...> (NoMethodError)
> Did you mean? name
This error message is a bit roundabout. I assume the implication is that `name` is the getter and `name=` is the setter. And so, immutable objects don't have the setter method? But definitely could be worded much better than NoMethodError.
Disclaimer: I'm more of a Python/JS/TS person, and haven't used Ruby much.