8 Recent changes in did_you_mean 1.1.0

7 months have already passed since the beginning of 2017, and for Rubyists, January comes on the heels of the release of a new version of Ruby. I suspect there are quite a few of you considering upgrading to the new version of Ruby for apps you are developing for work.

New version 1.1.0 of the did_you_mean gem was also released before Christmas, and the new version ships with Ruby 2.4.0. With that in mind, I would like to take a look back at the changes since version 1.0.0.

Prior to the version 1.1.0, I had also released versions 1.0.1 and 1.0.2 after 1.0.0, but these versions do not get installed unless you explicitly run gem update, so I will also include the changes in these versions.

Version 1.1.0 only runs on Ruby 2.4

The did_you_mean gem actively leverages new features added to Ruby core, and takes the approach of boosting the gem's precision. In Ruby 2.4, the new NoMethodError#private_call? method has been added and is used by the version 1.1.0 of the did_you_mean gem, so the gem does not run on Ruby versions earlier than Ruby 2.4. If you are still using Ruby 2.3, please use version 1.0.2.

The gem no longer suggests names called by the user

One of the frequently reported issues until now has been that the method name actually called ends up in the suggestions. A common example would be as follows:

Ruby 2.3 and did_you_mean 1.0.0:

bar; bar = 1
# => NameError: undefined local variable or method `bar' for ...
#    Did you mean?  bar

Although it is concerning that the return value of Kernel#local_variables contains variables that have not yet been initialized due to the specs of MRI, there have also been reports of the gem suggesting the same name as the name called in other cases. Accordingly, I made it so that these suggestions are not shown in any case.

Ruby 2.4 and did_you_mean 1.1.0:

bar; bar = 1
# => NameError: undefined local variable or method `bar' for ...

Suggestions are now made when a NameError occurs from a Struct

The #[] method is defined on Struct objects that behaves the same way as Hash#[]. However, when a key that is not a member of the Struct object is passed in, a NameError occurs (Hash simply returns nil). In version 1.1.0, candidates are now suggested when a NameError occurs from Struct#[].

Ruby 2.3 and did_you_mean 1.0.0:

Struct.new(:foo).new[:fooo]
# => NameError: no member 'fooo' in struct

Ruby 2.4 and did_you_mean 1.1.0:

Struct.new(:foo).new[:fooo]
# => NameError: no member 'fooo' in struct
#    Did you mean?  foo
#                   foo=

This change was proposed and implemented by Yuki Kurihara.

Suggestions are no longer given (for the most part) when a NoMethodError occurs from nil

While there have been frequent reports of an issue where weird candidates are suggested when the receiver is nil, I could not come up with a great way to solve this problem. There are two cases to consider when a NoMethodError occurs on nil: the case in which the developer is trying to call a real method on nil but made a typo, and the case in which the method name being called is correct but the receiver unwittingly became nil. suggestions should be made for the former case, but not for the latter. I could not, however, come up with a good way to distinguish between these two cases. That being said, this issue cannot just be ignored considering the frequency at which developers encounter this scenario, so as a temporary solution, I decided to remove the methods defined by default on nil from the suggestion candidates.

Ruby 2.3 and did_you_mean 1.0.0:

@users.map {|user| ... }
# => NoMethodError: undefined method `map' for nil:NilClass
#    Did you mean?  tap

Ruby 2.4 and did_you_mean 1.1.0:

@users.map {|user| ... }
# => NoMethodError: undefined method `map' for nil:NilClass

While for the time being this implementation solves the most frequently encountered problems, it does not mean that I am satisfied with it — it will almost surely create a situation where developers are expecting suggestions to be made but nothing comes up. If you have any good ways to get around this, I would love to hear your suggestions.

Private method names are now handled properly as candidates based on the circumstances

In version 1.0.0, there was a bug where private method names would be inappropriately suggested depending on the circumstances. For instance, consider the following example:

Ruby 2.3 and did_you_mean 1.0.0:

File.raed 'path/to/file.csv'
# => NoMethodError: undefined method `raed' for File:Class
#    Did you mean?  read
#                   rand

It is obvious in this case that you want to call the public method File#read, but the inaccessible private method, the Kernel#rand method, even ends up in the suggestions. This is because the names of all private methods were treated as candidates in any situation if arguments were passed in since there was no way to determine whether a call could access private methods in Ruby 2.3. In Ruby 2.4, we can now determine this with the newly added NoMethodError#private_call?, so the names of private methods can now be handled properly.

Ruby 2.4 and did_you_mean 1.1.0:

File.raed 'path/to/file.csv'
# => NoMethodError: undefined method `raed' for File:Class
#    Did you mean?  read

The spell checker can now be used as a public interface

Beginning from version 1.1.0, the spell checker used internally by the did_you_mean gem can now easily be reused.

Ruby 2.4 and did_you_mean 1.1.0:

DidYouMean::SpellChecker.new(dictionary: ['email', 'fail', 'eval']).correct('meail')
# => ['email']

Experimental features have been added

One of the secrets of the did_you_mean is that versions prior to 1.0.0 used to show a "Did you mean?" suggestion in more circumstances. However, in association with shipping as part of Ruby 2.3, features that seemed likely to affect performance, and features only available in Rails were deleted. I couldn't bear, however, losing these features that I was personally fond of, so from among the deleted features, I decided to package the ones that could be used solely with the features of Ruby core in the did_you_mean gem as experimental features. For the features only available in Rails, I have published the did_you_mean-activerecord gem separately. To activate the experimental features packaged in version 1.1.0 of the did_you_mean gem, all you have to do is add:

require 'did_you_mean/experimental'

If you add this, you activate the following 3 experimental features —

When you make a typo on instance variable names and a NoMethodError occurs:

require 'did_you_mean/experimental'

@full_name = "Yuki Nishijima"
@full_anme.split(" ")
# => NoMethodError: undefined method `split' for nil:NilClass
#    Did you mean?  @full_name

When you have a typo on a key when accessing a hash:

require 'did_you_mean/experimental'

hash = {foo: 1, bar: 2, baz: 3}
hash.fetch(:fooo)
# => KeyError: key not found: :fooo
#    Did you mean?  :foo

When there is a typo on the class initializer:

require 'did_you_mean/experimental'

class Person
  def intialize
    ...
  end
end
# => warning: intialize might be misspelled, perhaps you meant initialize?

Just as with the suggestions for Structs, the suggestions when accessing hashes were also proposed and implemented by Yuki Kurihara. Thank you.

Compatibility with JRuby

Following MRI 2.3.0, the did_you_mean gem is now installed and activated automatically on JRuby starting from version 9.1.3.0. Although there are some differences in JRuby, such as typos on local variable names not being corrected and the possibility that the experimental features mentioned above may not work perfectly, we have succeeded in providing more or less the same features as on MRI. Moving forward, I hope to increase the implementations on which the did_you_mean gem runs instead of just on MRI.

In providing support for JRuby, I was helped by the JRuby core team members Charles Oliver Nutter, Christian Meier, Thomas E Enebo. I would like to express my gratitude to them here.

I am looking forward to your feedback

There has been a variety of changes in the year since 1.0.0. I thought that there probably wasn't that many major changes, but after writing them out, there was actually a ton! You can get surprisingly far if you keep building up gradually over a year.

If you have any bugs to report or suggestions, please feel free to contact me on GitHub issues. In particular, I am sure the accuracy of the spell checker can still be improved significantly with some serious research, so I am looking for people who are interested in researching in technical school, college, or graduate school (even though you may think there is no one into that kind of thing...).

Show Comments

Get the latest posts delivered right to your inbox.