On 8/20/06, Rick DeNatale <rick.denatale / gmail.com> wrote:
> I've been away from e-mail for a few days.  I haven't looked at anyone
> elses solution yet.
>
> Here's my initial solution which didn't take long.  I expect to refine
> this with more function in the next few days.

Okay, I've spent a few hours adding things:

1) First, I added the ability to optionally use the Readline module to
prompt for method source if it's available.  This allows the history
in, say irb, to be used.  Note the use of class_eval to include the
module. This allows the code to compile if the module isn't available.

2) I added a check to see if the method actually compiles, and loop
back to reprompt if it doesn't.  This make the saving of the history
of input using readline worthwhile.

3) I then added the ability to 'dump' any methods created by
prompting.  The Kernel method prompted_methods produces a string which
could be saved to a file and compiled by ruby.

4) Finally, it occured to me that you might want to put the method in
another class (or module) than the class of the receiver.  I added a
prompt allowing the user to select any class or module in the ancestry
of the receiver, EXCEPT the MethodPrompter module.  The idea behind
this restriction is to keep that module pristine, with nothing but
method_missing.

The last idea I have is to add a resend of the original message when
the method is defined, and allow the user to remove it if he doesn't
like the result.  I just haven't gotten around to this yet.
>
> === method_prompter.rb ===
module MethodPrompter

        class Interactor

                # Try to include the readline library
                # if it's not available define a lower-function method
                begin
                        require 'readline'
                        Interactor.class_eval('include Readline')
                rescue Exception => ex
                        puts ex.to_s
                        warn "The Readline module is not available."
                        def readline(prompt,addHistory)
                                print prompt
                                line = gets
                                # Readline.readline returns empty string instead
                                # of "\n"
                                line = "" if line == "\n"
                                line
                        end
                end

                def initialize(target_object)
                        @target_object = target_object
                end

                def prompt_for_body(symbol, *args)
                        puts "Please define what I should do (end with
a newline):"
                        method_body = []
                        line_num = 1
                        while line = readline("#{line_num}: ", true)
                                break if line.empty?
                                line_num += 1
                                method_body << line
                        end
                        method_body
                end

               def prompt_for_parms(args, block)
                        puts "There were #{args.length} arguments in the call."
                        puts "block = #{block}"
                        p args if !args.empty?
                        puts "enter method argument list"
                        args = readline("arguments: ", false)
                        args.empty? ? "" : "(#{args})"
                end

                def prompt_for_module
                        possibles =
@target_object.class.ancestors.reject { |m| m == MethodPrompter }
                        result = nil
                        while result == nil
                                puts "Possible modules/classes to implement"
                                possibles.each_with_index do | mod, i |
                                        puts "  #{i}: #{mod.name}"
                                end
                                p "Enter
selection(#{@target_object.class.name}): "
                                result = possibles[gets.to_i]
                        end
                        p result
                        result
                end

                def make_method(target_module, symbol, source)
                        target_module.module_eval(source, 'line', 0)
                end

                def method_source(symbol, parms, method_body)
                        "def #{symbol.to_s}#{parms}\n" <<
                             method_body.join("\n") <<
                             "\nend"
                end

                def prompt_for_method(symbol, args, block)
                        puts "#{symbol} is undefined"
                        target_module = prompt_for_module
                        parms = prompt_for_parms(args, block)
                        good_to_go = false
                        until good_to_go
                                method_body = prompt_for_body(symbol)
                                if method_body.empty?
                                        puts "Okay, nothing to do, so
I've not defined #{symbol.to_s}"
                                        return
                                end
                                begin
                                        source = method_source(symbol,
parms, method_body)
                                        make_method(target_module,
symbol, source)
                                rescue Exception => ex
                                        puts "#{ex.class}: #{ex}"
                                ensure
                                        good_to_go =
target_module.method_defined?(symbol)
                                end
                                puts "\nThat didn't work. Empty method
body will give up." unless good_to_go
                        end

                        Repository.save_method(target_module, symbol,
parms, method_body)
                end

        end

        class Repository

                require 'singleton'
                include Singleton

                def Repository.save_method(target_module, symbol,
parms, method_body )

instance.module_entry(target_module).save_method(symbol,parms,
method_body)
                end

                def module_entry(target_module)
                        @modules = @modules || Hash.new {|hash, k|
hash[k] = ModuleEntry.new }
                        @modules[target_module]
                end

                def Repository.list_all
                        instance.list_all
                end

                def list_all
                        return "" unless @modules
                        result = ""
                        @modules.each do | mod, entry |
                             result << (mod.kind_of?(Class) ? "class
": "module ")
                             result << mod.name << "\n"
                             result << entry.list_all
                             result << "end\n"
                        end
                        result
                end

                def modules
                        @modules
                end
        end

        class ModuleEntry

                def save_method(symbol, parms, method_body)
                        @methods = @methods || Hash.new
                        @methods[symbol] = source_for(symbol, parms,
method_body)
                end

                def list_all
                        return "" unless @methods
                        result = ""
                        @methods.each do | symbol, source |
                        result << source << "\n"
                        end
                        result
                end

                private
                def source_for(symbol, parms, method_body)
                        indent = " " * 8
                        source_array = [indent + "def " + symbol.to_s + parms ]
                        method_body.each do | line |
                                source_array << (indent * 2) + line
                        end
                        source_array << indent + 'end'
                        source_array.join("\n")
                end
        end

        def method_missing(symbol, *args, &block)
                Interactor.new(self).prompt_for_method(symbol, args, block)
        end

end

module Kernel
        def prompted_methods
                MethodPrompter::Repository.list_all
        end
end

-- 
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/