
“Did you mean?” Experience in Ruby - yuki2448
http://www.yukinishijima.net/2014/10/21/did-you-mean-experience-in-ruby.html
======
prairiedogg
I've been pairing with ruby / rails developers since 2010 and coming from
statically typed languages, it's unbelievable how much time gets spent playing
"guess the method name". Even in rich IDEs like RubyMine, the utter lack of
context in any given file in rails leaves programmers typing their best guess
of a method name, running the tests, rinse, repeat.

This solution, while creative and laudable, solves a problem that shouldn't
exist. It should either be solved by IDE/tooling in a dynamically typed
language or by the compiler in a statically typed language. Stop guessing and
let your tools do the hard work of remembering method names for you.

~~~
zerr
I have exactly this frustration as a person coming from static languages
(mostly C++) to web dev. Anybody can recommend something more comfortable? I
like the concept of Vaadin, but I'd prefer something non-Java.

~~~
loosescrews
Try Go. It is a lot like C and compiles to concurrent machine code. It is also
very easy to learn and use.

~~~
sirseal
Er, you mean to say "compiles to machine code." There is no such thing as
concurrent machine code... Now the Go language has some fantastic data
structures baked-into the syntax that provide a language with which to
construct concurrent programs.

------
derefr
A lot of people are recommending IDE-like tooling--but in truly dynamic
language (one with a "living image" with path-dependent monkey-patched
behavior that can't be replicated during static analysis, like Smalltalk--or,
sometimes, Ruby) there's a more idiomatic way.

In a dynamic language, if you're at all unsure of what code you need to write,
then you _don 't write it in your editor in the first place._ Instead, you
build the expression you need, interactively, at the REPL—and then, once it
works, you paste that _into_ your editor.

In dynamic languages, the "dead" code in modules is effectively a coagulation
of previously "live" REPL incantations; to trust code that was written "dead"
to work correctly "live" is madness (or just a recipe for really long
iterations involving unit tests.)

If you take this approach far enough, though, you do get a sort of
IDE—something that manages your expression experiments and the context
harnesses they need, and re-runs experiments when you edit their dependent
formulae. I am, of course, talking about "notebook" interfaces like IPython's.

~~~
vidarh
And with Ruby, especially since you mention Smalltalk, we have a tool that is
getting _closer_ to the kind of live introspection and modification that
Smalltalk is famous for: Pry [1].

Pry lets you call "binding.pry" anywhere in your program to dump you into a
shell within that context, with full access to local variables etc.. And tab-
completion and plenty of introspection features. I frequently find myself
triggering Pry in the middle of handling http requests if something doesn't
work, for example. Letting me inspect the environment, modify stuff, and when
I exit the request is completed.

It can also do things (with some limitations) like bring up an editor to where
the current method was defined, and let you edit and reload the code.

And you can attach to it remotely using Drb in case the app in question
doesn't run attached to a terminal.

At this point it's almost criminal to do Ruby development without Pry.

[1] [http://pryrepl.org/](http://pryrepl.org/)

~~~
gcao
In pry is it possible to return something when I exit the REPL? Like this:

    
    
      def do_something
        return binding.pry
    
        do_something_but_does_not_work
      end
    
      result = do_something
      process result
    

It might be very convenient if I put 'return binging.pry' just before the
broken code. I can interactively fix the broken code and continue run outside
of current method.

~~~
vidarh
If you are explicit: "return" in a Pry session will exit pry and return the
value you pass to it, just as if you are in the method itself. So e.g. "return
42" would return 42 from do_something.

------
timr
Neat. In case you're wondering, it's implemented using a levenshtein distance
algorithm:

[https://www.omniref.com/ruby/gems/did_you_mean/0.6.0/symbols...](https://www.omniref.com/ruby/gems/did_you_mean/0.6.0/symbols/DidYouMean::MethodMatcher/similar_methods)

and it works by extending the NameError exception:

[https://www.omniref.com/ruby/gems/did_you_mean/0.6.0/files/l...](https://www.omniref.com/ruby/gems/did_you_mean/0.6.0/files/lib/did_you_mean/core_ext/name_error.rb)

~~~
lucaspiller
The close_enough gem takes this one step further and uses this to 'patch'
method_missing:

[http://decomplecting.org/blog/2013/03/01/code-typos-got-
you-...](http://decomplecting.org/blog/2013/03/01/code-typos-got-you-down-
stop-worrying-with-close-enough/)

------
WalterBright
Some years back I put this feature in both the Digital Mars C/C++ compiler and
the D language compiler. It's turned out to be popular and very useful.

Compiler error messages are steadily improving from mere statements of what is
wrong to suggestions for corrective action.

------
PaulJulius
Obviously this is a useful tool, and all the power to the author for finding
what looks to be an excellent solution, but this line bothers me:

>>> Sometimes I wasted hours and hours just because there is one character
difference. I hate it.

 _This shouldn 't happen._ Ever. This should not be a problem anymore. These
are the sort of errors that we can catch immediately and should be caught
immediately. From looking at the author's GitHub profile, it looks like he
uses Emacs, presumably without a plugin that would give him IDE like features.
I'm not going to tell him to go use a regular IDE, but it frustrates me that's
we can't have those sort of tools available everywhere. (As a vim user myself
I have high hopes for the neovim project and look forward to the day when it
can be embedded inside a general sort of IDE.)

~~~
vidarh
While you can catch _some_ of these, for a language like Ruby, it's an
incredibly hard problem to catch all of these errors immediately because the
methods that can be called on a given named entity _can not_ in the general
case be known by statically inspecting the source, and you can not in general
safely instantiate the application because even class definitions are
_executed_ and can have side effects, and the methods available can even vary
based on environmental factors, such as whether or not you get a database
connection, and what's in the database.

Consider that it is a common pattern for Ruby ORM's to either use
method_missing or dynamically define methods to correspond to the current (at
connection time) set of columns present in your database.

And "thanks" to the ability to monkey patch and redefining methods, even
determining if something "obviously" safe like 42.to_s is not.

There's no way for your editor to handle that unless you stand up a version of
your app with an instrumented language environment and lets the editor poke
around. Now _that_ is possible with tools like Pry, etc., but it takes a lot
more work to do safely (because your editor can't know if it can safely start
your app).

------
andrewchambers
The value of static typing is more and more apparent. Recently I've played
with ocaml and F# and it felt great compared to Java or C++.

~~~
jader201
... until you start trying to make your app testable.

(I say this as a developer of primarily statically typed languages.)

~~~
nbevans
Try using a good type-inferred statically-typed language. Hint: this rules out
Java, C# etc.

------
inglor
This sort of stuff should, at least in the 'easy' case be done at the editor
level. Doesn't ruby have linter tooling for this?

Still, props for the work.

~~~
ryanto
Ruby has a linters, but this sort of problem is hard to solve with a linter in
a dynamically typed language. It's even harder in a Rails environment since
Rails adds many methods to your classes at run time.

~~~
kyllo
Yeah, since it's a dynamically typed lang with inheritance and late binding,
doesn't performing static analysis on Ruby code to determine whether a method
is defined or not, basically require a solution to the halting problem?

I'm under the impression that you have to actually execute Ruby code to find
out with certainty where the methods it calls are defined.

~~~
vidarh
You can solve the "90%" problem statically. My Ruby-compiler-in-progress warns
of methods that have not been seen, and I may eventually add some (entirely
optional) pragma to allow hinting to reduce false positives (though getting
that non-intrusive will be a fun challenge).

But you are right, the general case does require a solution to the halting
problem, something which is trivial to demonstrate very explicitly:

    
    
        eval(STDIN.gets)
        42.will_i_halt?
    

Put that in a file, and run it. Press enter, and it halts. Or cut and paste
something like this, and it doesn't:

    
    
        class Fixnum; def will_i_halt?; loop {}; end; end
    

And consider that even "require" is a practical equivalent to "eval", so
trying to look for eval() calls and similar constructs and think you're safe
doesn't work, unless you have a guarantee that the interpreter load paths will
be the same when the code is run as what you think it'll be.

But in more pragmatic terms, this is a real issue because many Ruby ORMs for
example will add methods that depends on the current state of the databases
they connect to, so many typical Ruby applications _will_ actually validly use
method names that are not explicitly defined anywhere.

------
joshdance
Blows my mind that IDEs and environments are not better at this. This is
something that computers are good at, pattern recognition and scanning the
whole file. Of course the it wouldn't work every time, an could make
ridiculous suggestions, but I would love the computer to suggest something
every time there is an issue. Even if it is wrong 90% of the time, if those
guesses don't slow down the programmer the time saved would be huge.

~~~
lnanek2
Java IDEs are better at this. JavaScript...well I saw NetBeans do it once and
was amazed. Ruby...well it's impossible to do correctly as other posters have
mentioned.

------
jevin
I think this is an amazing gem. And I'm eager to use it on my Rails projects.

On a side note, am I the only one who feels that autocomplete tend to get in
the way when I'm coding?

~~~
ASneakyFox
The trick is to actually use it. If you auto complete then your coding should
be typing 2-3 characters, hit enter, 2-3 characters, enter and so on. It
should feel like you're entering hotkeys not typing.

Only real typing should be defining variables and methods.

Your coding speed can go way up depending on how good the autocomplete is.

------
modarts
Or just use some semblance of static typing and completely eliminate this
class of bug.

------
ChrisAntaki
> Sometimes I wasted hours and hours just becaue there is one charactor
> difference. I hate it.

Haha, humans are much more able to parse meaning, despite a character being
off here or there. Well, you've taken computers one step closer to humans. And
you've made programming with Ruby friendlier. Great job!

~~~
ndesaulniers
When I read that line you quoted, I wondered "how did the author ever finish
the gem, since it didn't exist yet to help them with their typos?"

------
ryan-allen
It's almost like... statically typed languages!

------
flowerpot
I see you used the Levenshtein algorithm to calculate the suggestions, very
cool idea. I've noticed lately that it is being used quite more often than in
the past. (may just be my perception)

------
general_failure
'Sometimes I wasted hours and hours just becaue there is one charactor
difference. I hate it.'

Not sure if the typos (Yeah, there are two) are intentional but I loved it :-)

~~~
baddox
> So what will happen when you misspell ActiveSupprt's
> Hash#with_indifferent_access?

Turned down the too-obvious ironic opportunity and went for somewhere else in
the sentence.

------
mikecmpbll
Not sure how helpful this is, when it says there's no method called xyz, it's
pretty obvious you called the wrong method, or you called it on the wrong
thing. Your first thing should be checking that you didn't call the wrong
method name.. which involves looking at the error which repeats the method
name that you tried to call, and the object you called it on.

Bizarre that this is a genuine hang-up for people.

------
stretchwithme
I use Rubymine, which visually indicates when a variable is unused or doesn't
exist.

It has tons of other features that save you time and hassle.

~~~
alexch
RubyMine FTW. It's amazing to me how completely Dwarf Fortress -- um, I mean
vim and tmux -- have conquered the Ruby dev world.

------
dankohn1
Could I also highly recommend making your project Rubocop-clean, and using
pronto to run Rubocop on your CI server and make comments on your commits in
Github. Rubocop warns against any methods you define that are not used at
least once.

------
brvs
I would love it if instead of quitting my program with an error, it just went
ahead and called the method it thinks I'm referring to. This would remove a
lot of unneeded friction from web development.

~~~
zenlikethat
You should implement a `yolo` gem to support this.

~~~
Widdershin
[https://github.com/markburns/fuzzy](https://github.com/markburns/fuzzy)

------
skatenerd
Who's gonna be the first person to hook this into method_missing()

------
bradgessler
I hope somebody forks this and creates a version that automatically corrects
the method for you at runtime. Why even show an error or throw an exception?

Bonus if the corrections are cached for performance.

~~~
allyant
What a horrible idea - what if the suggested method wasn't the intended one?

------
silveira
A python implementation of "Did you Mean?" by Peter Norvig
[http://norvig.com/spell-correct.html](http://norvig.com/spell-correct.html)

------
sleepingspider
Strange. As an ruby programmer, I never had such a need.

------
imacomputer2
"Sometimes I wasted hours and hours just becaue there is one charactor
difference." Oh thank God! I thought I was the only one.

------
RVuRnvbM2e
Wow. Talk about the wrong approach. When you're having to play trial-and-error
to get the right method, you definitely have a problem with your tools - not
the language!

I don't know about other editors, but vim has great autocomplete support for
ruby built right in. Because of this I don't often even type methods out in
full anymore.

And there's a great plugin for doco too:
[https://github.com/danchoi/ri.vim](https://github.com/danchoi/ri.vim)

------
annnnd
> Here is a good exmaple...

Looks like "Did you mean" could be a nice extension to browser textboxes too.
;)

------
mangecoeur
This is really smart, really the sort of thing you wonder why no one thought
of it before.

------
thesz
Glorious Haskell Compiler suggests names for typos too.

------
whizzkid
How about implementing this in a way that it starts guessing method names
while typing a method?

In this case, you would solve the error while typing it.

A simple dropdown with suggestion(s) would be great in Sublime Text.

------
KedarMhaswade
"Did you mean?" is great, but "Do you mean?" would be better. Achieving latter
in Ruby/JS/... certainly feels harder.

------
octref
>>> Sometimes I wasted hours and hours just becaue there is one charactor
difference. I hate it.

"because" misspelt.

~~~
ZoF
'Charactor' is also miss-spelt... It's intentional.

------
germs12
Seems like a bandaid approach to fixing a severed artery. The real problem is
the lack of desire to read documentation. This also has the problem of sending
someone down the wrong path when the wrong "solution" is suggested. Read more
documentation and pay attention while you're programming.

~~~
amckenna
I don't think this has to do with a lack of knowledge of documentation, it has
to do with simple spelling errors. Quite a number of times I type a method
name wrong and upon scanning the code don't see the missing, duplicated, or
mistyped letter. This would help a lot with that.

