# Method Auto Completion (#110)

**URL:** <https://www.ruby-forum.com/t/method-auto-completion-110/82358>\
**Category:** Ruby\
**Created:** [January 19, 2007, 4:29pm UTC](https://www.ruby-forum.com/t/method-auto-completion-110/82358 "2007-01-19T16:29:36Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [January 19, 2007, 4:29pm UTC](https://www.ruby-forum.com/t/method-auto-completion-110/82358/1 "2007-01-19T16:29:36Z")

</div>

The three rules of Ruby Q.:

1. Please do not post any solutions or spoiler discussion for this quiz  
until  
48 hours have passed from the time on this message.

2. Support Ruby Q. by submitting ideas as often as you can:

[http://www.rubyquiz.com/](http://www.rubyquiz.com/)

1. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem helps  
everyone  
on Ruby T. follow the discussion. Please reply to the original quiz  
message,  
if you can.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

by Robert D.

Command Line Interfaces very often support command abbreviations The  
purpose of  
this quiz is to automatically dispatch to methods on unambiguous  
abbreviations,  
ask the user for clarification in case of ambiguous abbreviations and  
raise a  
NoMethodError in case of an abbreviation that cannot be matched to any  
command.

Behavior of other methods defined in a class shall not be altered.

Be creative about the interface and about behavior. I have OTOH defined  
a small  
test suite that makes assumptions about the interface and behavior. But  
the test  
suite is only there for your convenience.

What is said below applies to the test suite and shall in no way inhibit  
any  
alternative ideas.

class Mine  
abbrev :step, :next, :stop  
abbrev :exit  
end

Mine.new.e # should resolve to exit  
Mine.new.st # should prompt the user  
Mine.new.a # should still raise a NoMethodError

Abbreviation targets themselves are not expanded.

class Nine  
abbrev :hash  
abbrev :has  
end

Nine.new.ha # =\> [:hash, :has]  
Nine.new.has # =\> NoMethodError

class Nine  
def has; 42; end  
end  
Nine.new.has # =\> 42

In order to allow for automated testing the test code shall not prompt  
the user  
in case of an ambiguous abbreviation but return an array containing all  
(and  
only all) possible completions as symbols. Note that the test suite sets  
the  
global variable $TESTING to a true value for your convenience.

[http://rubyquiz.com/test-abbrev.rb](http://rubyquiz.com/test-abbrev.rb)

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [January 21, 2007, 5:01pm UTC](https://www.ruby-forum.com/t/method-auto-completion-110/82358/2 "2007-01-21T17:01:16Z")

</div>

Last week I was kind of embarrassed that Bob chose to feature my late,  
poorly commented, somewhat opaque solution, but I certainly appreciate  
the inclusive spirit of Ruby Q… Here’s a more timely submission for  
the auto completion problem.

* * *

class Object  
def self.abbrev(\*args)  
module\_eval \<\<-EOS  
@@abbrevs ||= []  
@@abbrevs += args

```
def method_missing(m)
  # abbrev targets themselves are not supposed to be expanded
  raise NoMethodError if @@abbrevs.include?(m)

  # which abbrev targets could match m, and which of those

```

correspond to methods?  
matches = @@abbrevs.select do |sym|  
sym.to\_s.index(m.to\_s) == 0 && methods.include?(sym.to\_s)  
end

```
  case matches.size
    when 0
      raise NoMethodError
    when 1
      self.send(matches.first)
    else
      # multiple matches, pass them back to the user
      return matches if $TESTING
      puts matches.join(" ")
  end
end
EOS

```

## end end

This code passes the supplied tests, and I also added another test  
when an earlier version of my code was doing things it shouldn’t have:

* * *

# check for unintended effects on other classes

class Test3 \< Test::Unit::TestCase  
def setup  
@foo\_class = Class.new {  
abbrev :start  
def start; nil end  
}  
@bar\_class = Class.new {  
def start; nil end  
}  
end

## def test1 f = @foo\_class.new b = @bar\_class.new assert\_raise NoMethodError do b.send(:sta) end end end

I don’t feel very confident in the approach I’ve taken, so I’m eager  
to hear critique and see other solutions.

Krishna

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [January 21, 2007, 5:17pm UTC](https://www.ruby-forum.com/t/method-auto-completion-110/82358/3 "2007-01-21T17:17:18Z")

</div>

On Fri, 19 Jan 2007 22:45:05 +0900, Ruby Q. wrote:

> ask the user for clarification in case of ambiguous abbreviations and raise a
> 
> end  
> Nine.new.has # =\> 42
> 
> In order to allow for automated testing the test code shall not prompt the user  
> in case of an ambiguous abbreviation but return an array containing all (and  
> only all) possible completions as symbols. Note that the test suite sets the  
> global variable $TESTING to a true value for your convenience.
> 
> [http://rubyquiz.com/test-abbrev.rb](http://rubyquiz.com/test-abbrev.rb)

Returning an array when the answer is ambiguous is a very bad way to do  
this. Instead, I throw an exception (you can get the candidates from the  
#candidates attribute of the exception).

I also saw no reason to specifically name the methods that get  
abbreviated  
– rather, abbreviation works on all methods in the object and its super  
classes.

require ‘abbrev’

class AmbiguousExpansionError \< StandardError  
attr\_accessor :candidates  
def initialize(name,possible\_methods)  
super(“Ambiguous abbreviaton: #{name}\n”+  
“Candidates: #{possible\_methods.join(”, “)}”)  
@candidates=possible\_methods  
end  
end

module Abbreviator  
def method\_missing name,\*args  
abbrevs=methods.abbrev  
return send(abbrevs[name.to\_s],\*args) if abbrevs[name.to\_s]  
meths=abbrevs.reject{|key,value| key!~/^#{name}/}.values.uniq  
raise AmbiguousExpansionError.new(name, meths) if meths.length\>1  
return super(name,\*args)  
end  
end

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [January 21, 2007, 5:24pm UTC](https://www.ruby-forum.com/t/method-auto-completion-110/82358/4 "2007-01-21T17:24:18Z")

</div>

Here is my solution.  
It does not pass the supplied tests since it uses a different interface  
when  
there are multiple matches : It a raises a MultipleMethods exception  
with an  
array of the possible matches.

There is an additional abbrev method in Module which defines the methods  
to be  
abbreviated. If used with the single argument ‘true’ it enables  
abbreviations  
for all methods of the object.  
the ‘abbreviated\_methods’ private method of Object does the intersection  
of  
the defined object class’s abbreviated methods and the current actual  
methods  
of the object.  
Then the abbreviation are caught with method\_missing.

quiz110.rb :

class MultipleMethods \< NoMethodError  
attr\_reader :methods  
def initialize(methods)  
@methods = methods  
super("Multiple Choices : " + @methods.join(’ '))  
end  
end

class Module  
attr\_reader :abbreviated\_methods

private

# Defines a list of methods to be abbreviated.

# If auto = true, then all the methods are abbreviated

# 

# class Test

# abbrev # All the methods of Test will be abbreviated

# end

# 

# class Test2

# abbrev :first, :last # Only the methods first and last may be

abbreviated

# end

# 

# If multiple choices are possible, a MultipleMethods exception is

raised  
which contains a list of the matches.  
def abbrev(auto = true, \*args)  
if auto.respond\_to? :to\_sym  
@abbreviated\_methods ||= []  
@abbreviated\_methods += args.collect {|m| m.to\_sym }.unshift  
auto.to\_sym  
elsif args.empty?  
auto ? @abbreviated\_methods = [] : @abbreviated\_methods = nil  
else  
raise ArgumentError  
end  
end  
end

class Object  
alias :abbrev\_method\_missing :method\_missing  
def method\_missing(sym, \*args)  
found = abbreviated\_methods.select { |m| m.to\_s =~ /^#{sym}/ }  
if found.empty?  
abbrev\_method\_missing(sym, \*args)  
elsif found.size == 1  
send found.first, \*args  
else  
raise MultipleMethods.new(found)  
end  
end

private  
def abbreviated\_methods  
if self.class.abbreviated\_methods.nil?  
[]  
elsif self.class.abbreviated\_methods.empty?  
methods  
else  
self.class.abbreviated\_methods & methods.collect {|m| m.to\_sym}  
end  
end  
end

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [January 21, 2007, 6:34pm UTC](https://www.ruby-forum.com/t/method-auto-completion-110/82358/5 "2007-01-21T18:34:27Z")

</div>

> My Solution doesn’t pass all tests, since i think that when a method  
> :aa is defined it should offer completion from :a to :aa (even when  
> the method doesn’t exist in the first place but is defined later on)
> 
> [Parked at Loopia](http://pastie.caboo.se/34631)

[Parked at Loopia](http://pastie.caboo.se/34632) - forgot to remove the other stuff 😑 and  
somehow it didn’t set highlighting to ruby…

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [January 21, 2007, 6:32pm UTC](https://www.ruby-forum.com/t/method-auto-completion-110/82358/6 "2007-01-21T18:32:27Z")

</div>

On 1/19/07, Ruby Q. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> ask the user for clarification in case of ambiguous abbreviations and raise a
> 
> ```
> end
> Nine.new.has # => 42
> 
> ```
> 
> In order to allow for automated testing the test code shall not prompt the user  
> in case of an ambiguous abbreviation but return an array containing all (and  
> only all) possible completions as symbols. Note that the test suite sets the  
> global variable $TESTING to a true value for your convenience.
> 
> ```
> http://rubyquiz.com/test-abbrev.rb
> 
> ```

My Solution doesn’t pass all tests, since i think that when a method  
:aa is defined it should offer completion from :a to :aa (even when  
the method doesn’t exist in the first place but is defined later on)

[http://pastie.caboo.se/34631](http://pastie.caboo.se/34631)

It’s nothing fancy, just a quick 15 minutes-hack, was fun though 🙂

^manveru

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [January 22, 2007, 2:50am UTC](https://www.ruby-forum.com/t/method-auto-completion-110/82358/7 "2007-01-22T02:50:39Z")

</div>

This is my solution. Probably my fastest and shortest ruby quiz  
solution, but no less fun! Props to the creator of this quiz.

In technicolor glory: [Parked at Loopia](http://pastie.caboo.se/34720)  
Also attached. And pasted below.

class Object  
def method\_missing(method, \*args, &blk)  
# Gather all possible methods it could be.  
possibleMethods = self.methods.select {|x| x =~  
/^#{Regexp.escape(method.to\_s)}/ }

```
case possibleMethods.size
# No matching method.
when 0
  raise NoMethodError.new("undefined method `#{method}' for

```

#{self.inspect}:#{self.class.name}")

```
# One matching method, call it.
when 1
  method = possibleMethods.first
  self.send(method, *args, &blk)

# Multiple possibilities, return an array of the possibilities.
else
  possibleMethods
end

```

end  
end
