[QUIZ] Method Auto Completion (#110)

R

Ruby Quiz

The three rules of Ruby Quiz:

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 Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

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

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

by Robert Dober

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
 
K

Krishna Dole

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 Quiz. 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



The three rules of Ruby Quiz:

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 Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

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

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

by Robert Dober

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
 
K

Ken Bloom

The three rules of Ruby Quiz:

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 Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

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

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

by Robert Dober

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

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
 
M

Maël Clérambault

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
 
M

Michael Fellinger

The three rules of Ruby Quiz:

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 Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

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

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

by Robert Dober

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

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

It's nothing fancy, just a quick 15 minutes-hack, was fun though :)

^manveru
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,989
Messages
2,570,207
Members
46,783
Latest member
RickeyDort

Latest Threads

Top