Is there a method_eval or similar thing?

I have a method called askyesno which takes a string and returns whether
the user pressed y or n. However, I now want the user to be able to pass
a block in with blocks for what to do for YES and NO.

However, in this block I need to be able to access method level
variables. Here's how i am trying to code a sample of it. btw, askyesno
will actually sit inside a module in my app.

···

--
# the ch here is only for testing, it contains y or n, so we can test
this out
# easily

def askyn(str, ch, &bl)
  puts str
  h = {}

  def actionbind(key, &block)
    h[key] = block
  end

  if block_given?
    method_eval(&bl) # or module_eval etc
  end

  case ch
  when 'y'
    h[:yes].call if h.include? :yes
  when 'n'
    h[:no].call if h.include? :no
  end
end

askyn("Do you wish to proceed?", 'n') do
  actionbind(:yes) { puts "user pressed yes" }
  actionbind(:no) { puts "user pressed no" }
end

Currently, i have "ask" methods that allow for many options (not just
yes/no), the selection is passed back to the caller and he has a
case-when in which he takes appropriate action.

I was considering trying out something like the above where he could
pass in proc - bindings for each key. Is there any way to access the
hash "h" from the block ?
--
Posted via http://www.ruby-forum.com/.

I think that maybe you can wrap everything in a class:

class Ask
  def initialize(question)
    @question = question; @h={}
    yield(self)
  end
  def actionbind(key, &block)
    @h[key] = block
  end
end

Ask.new("Do you wish to proceed?") do |question|
question.actionbind(:yes){ puts "user pressed yes" }
question.actionbind(:no) { puts "user pressed no" }
end

···

On Wed, Oct 22, 2008 at 9:38 AM, Nit Khair <sentinel.2001@gmx.com> wrote:

I have a method called askyesno which takes a string and returns whether
the user pressed y or n. However, I now want the user to be able to pass
a block in with blocks for what to do for YES and NO.

However, in this block I need to be able to access method level
variables. Here's how i am trying to code a sample of it. btw, askyesno
will actually sit inside a module in my app.

--
# the ch here is only for testing, it contains y or n, so we can test
this out
# easily

def askyn(str, ch, &bl)
puts str
h = {}

def actionbind(key, &block)
   h[key] = block
end

if block_given?
   method_eval(&bl) # or module_eval etc
end

case ch
when 'y'
   h[:yes].call if h.include? :yes
when 'n'
   h[:no].call if h.include? :no
end
end

askyn("Do you wish to proceed?", 'n') do
actionbind(:yes) { puts "user pressed yes" }
actionbind(:no) { puts "user pressed no" }
end

Currently, i have "ask" methods that allow for many options (not just
yes/no), the selection is passed back to the caller and he has a
case-when in which he takes appropriate action.

I was considering trying out something like the above where he could
pass in proc - bindings for each key. Is there any way to access the
hash "h" from the block ?
--
Posted via http://www.ruby-forum.com/\.

--
Go outside! The graphics are amazing!

Nit Khair wrote:

askyn("Do you wish to proceed?", 'n') do
  actionbind(:yes) { puts "user pressed yes" }
  actionbind(:no) { puts "user pressed no" }
end

As you have already realised, you need somewhere to store the bindings
between keys and actions. This could be a hash allocated by askyn:

  askyn("Do you wish to proceed?", "n") do |h|
    actionbind(h,:yes) { puts "user pressed yes" }
    ... etc

But in this case I think a wrapper object to hold the bindings would be
better, as already posted by someone else. (That's what the optparse.rb
library does, amongst others; it's a clean and well-recognised solution)

However you could turn it around and get the caller to pass a
pre-prepared hash containing all the bindings to askyn:

  askyn("Do you wish to proceed?", "n",
    :yes => lambda { puts "user pressed yes" }
    :no => lambda { puts "user pressed no" }
  )

I quite like that approach. This sort of API also makes it easy for the
user to invoke other methods in their object for each response, e.g.

  askyn("Do you wish to proceed?", "n"
    :yes => method(:do_yes),
    :no => method(:do_no)
  )

Now, as you say, you could keep your hash in a global variable, but that
would be very poor as it would make your code non-threadsafe. But it's
possible to fix this by using a thread-local variable:

  def askyn(prompt, default)
    Thread[:askyn] = {}
    yield
    .. etc
  end

  def actionbind(key, &block)
    Thread[:askyn][key] = block
  end

I'd say that smells somewhat, but it would work and it hides the hash.

Or scarily, you could attempt to create a local variable in the binding
of the caller, which is I think what you were asking for initially. This
is pretty horrible and I won't even attempt it here :slight_smile: You can google
for binding_of_caller, and note that eval lets you pass in a binding.

Regards,

Brian.

P.S. Note that if you nest def within a def, as your posted code does,
it probably doesn't do what you expect. The inner def will define a new
method in your object, at the time when the outer def is called.

···

--
Posted via http://www.ruby-forum.com/\.

Sandro Paganotti wrote:

I think that maybe you can wrap everything in a class:

class Ask
  def initialize(question)
    @question = question; @h={}
    yield(self)
  end
  def actionbind(key, &block)
    @h[key] = block
  end
end

Ask.new("Do you wish to proceed?") do |question|
question.actionbind(:yes){ puts "user pressed yes" }
question.actionbind(:no) { puts "user pressed no" }
end

Thanks, I will be using this whereever there are classes, but would like
to know, is there any way i can do it with methods. I know i could
define the hash as a global (or maybe module level object) but that
would not be a clean solution.

There is also one question I would like to ask as a ruby newbie
(struggling to grow up). Is the above approach of passing blocks to an
ask method making it more "rubyish" or better than returning the input
character and letting the user decide what to do.

···

--
Posted via http://www.ruby-forum.com/\.

  askyn("Do you wish to proceed?", "n",
    :yes => lambda { puts "user pressed yes" }
    :no => lambda { puts "user pressed no" }
  )

Oops, I missed a comma from the end of the second line.

···

--
Posted via http://www.ruby-forum.com/\.

Brian Candler wrote:

However you could turn it around and get the caller to pass a
pre-prepared hash containing all the bindings to askyn:

  askyn("Do you wish to proceed?", "n",
    :yes => lambda { puts "user pressed yes" }
    :no => lambda { puts "user pressed no" }
  )

That is something which immediately struck me as a possibility since I
was considering putting some configurations into a hash anyway.

   askyn("Do you wish to proceed?", config = {}, &block)

I had thought that would be ugly, and I might be missing something
elegant and simple. However, perhaps that *is* the simple way to go.

I'll do a check on your other options but keep them for future use.

P.S. Note that if you nest def within a def, as your posted code does,
it probably doesn't do what you expect. The inner def will define a new
method in your object, at the time when the outer def is called.

Actually, while generating code some time back, I had accidentally
generated some methods within my run() and not noticed. The code had
worked fine. I've rectified that, but used that "mistake" here!
Thanks again.

···

--
Posted via http://www.ruby-forum.com/\.

Sorry to self-promote, but you may want to use HighLine or at least
look at it for ideas:

http://highline.rubyforge.org/

···

On Wed, Oct 22, 2008 at 7:54 AM, Nit Khair <sentinel.2001@gmx.com> wrote:

There is also one question I would like to ask as a ruby newbie
(struggling to grow up). Is the above approach of passing blocks to an
ask method making it more "rubyish" or better than returning the input
character and letting the user decide what to do.

--
Technical Blaag at: http://blog.majesticseacreature.com | Non-tech
stuff at: http://metametta.blogspot.com

How about this? (Someone suggested before using an object to abstract
your needs, I've only implemented it)

class Answer
  def yes(&block)
    @yes = block
  end

  def no(&block)
    @no = block
  end

  def process(string)
    (string =~ /\by(es)?\b/i ? @yes : @no).call
  end
end

def ask_yn(question, &block)
  ans = Answer.new
  ans.instance_eval(&block)
  ans.process(gets.chomp)
end

ask_yn("Are you cool?") do
  yes { puts "Awesome" }
  no { puts "Lame" }
end

···

On Thu, Oct 23, 2008 at 8:12 AM, Nit Khair <sentinel.2001@gmx.com> wrote:

Brian Candler wrote:

However you could turn it around and get the caller to pass a
pre-prepared hash containing all the bindings to askyn:

  askyn("Do you wish to proceed?", "n",
    :yes => lambda { puts "user pressed yes" }
    :no => lambda { puts "user pressed no" }
  )

That is something which immediately struck me as a possibility since I
was considering putting some configurations into a hash anyway.

  askyn("Do you wish to proceed?", config = {}, &block)

I had thought that would be ugly, and I might be missing something
elegant and simple. However, perhaps that *is* the simple way to go.

--
Technical Blaag at: http://blog.majesticseacreature.com | Non-tech
stuff at: http://metametta.blogspot.com

Gregory Brown wrote:

Sorry to self-promote, but you may want to use HighLine or at least
look at it for ideas:

http://highline.rubyforge.org/

Ah. that's fine :slight_smile: I incidentally have been studying *a lot* of code in
the last month, and Highline is what i did go through the other day. It
was recommended to me by someone in another thread as a project to read
to improve my ruby.

However, here I mean passing a block to a method in a more general way.
This was just an example. Thanks for the reminder, will go back and
check ...

···

On Wed, Oct 22, 2008 at 7:54 AM, Nit Khair <sentinel.2001@gmx.com> > wrote:

--
Posted via http://www.ruby-forum.com/\.

Is this the main source repository: git://
github.com/elliottcable/highline.git ?

···

On Wed, Oct 22, 2008 at 7:40 AM, Gregory Brown <gregory.t.brown@gmail.com>wrote:

On Wed, Oct 22, 2008 at 7:54 AM, Nit Khair <sentinel.2001@gmx.com> wrote:

> There is also one question I would like to ask as a ruby newbie
> (struggling to grow up). Is the above approach of passing blocks to an
> ask method making it more "rubyish" or better than returning the input
> character and letting the user decide what to do.

Sorry to self-promote, but you may want to use HighLine or at least
look at it for ideas:

http://highline.rubyforge.org/

Gregory Brown wrote:

How about this? (Someone suggested before using an object to abstract
your needs, I've only implemented it)

Looks awesome ! May i use it, with credits of course.

···

--
Posted via http://www.ruby-forum.com/\.

No. It's here:

   http://rubyforge.org/scm/?group_id=683

James Edward Gray II

···

On Oct 22, 2008, at 10:52 AM, David Rio wrote:

On Wed, Oct 22, 2008 at 7:40 AM, Gregory Brown <gregory.t.brown@gmail.com > >wrote:

On Wed, Oct 22, 2008 at 7:54 AM, Nit Khair <sentinel.2001@gmx.com> >> wrote:

There is also one question I would like to ask as a ruby newbie
(struggling to grow up). Is the above approach of passing blocks to an
ask method making it more "rubyish" or better than returning the input
character and letting the user decide what to do.

Sorry to self-promote, but you may want to use HighLine or at least
look at it for ideas:

http://highline.rubyforge.org/

Is this the main source repository: git://
github.com/elliottcable/highline.git ?

Sure, no credit necessary, it's fairly generic. Enjoy!

-greg

···

On Oct 24, 2008, at 12:00 AM, Nit Khair wrote:

Gregory Brown wrote:

How about this? (Someone suggested before using an object to abstract
your needs, I've only implemented it)

Looks awesome ! May i use it, with credits of course.