Forum: Ruby from block to code (also: how to get the bindings of a block

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
D8fb06dfc08a477ecb0a76ffdbff3475?d=identicon&s=25 Chiaro Scuro (chiaroscuro)
on 2006-04-27 09:38
(Received via mailing list)
Does anyone know how to convert a block to a string representation of
the code it contains?

I would need that to inject some code within a block and evaluate the
resulting code.

* I cannot simply append the code because I need the bindings of the
block.
* I cannot explicitly return the bindings from the block because I am
working on a DSL.
* I cannot get the bindings after the call to the block because that
scope seems to have been cleaned after the call
0b561a629b87f0bbf71b45ee5a48febb?d=identicon&s=25 Dave Burt (Guest)
on 2006-04-27 13:32
(Received via mailing list)
chiaro scuro wrote:
> Does anyone know how to convert a block to a string representation of
> the code it contains?

Read the source file?

Cheers,
Dave
D8fb06dfc08a477ecb0a76ffdbff3475?d=identicon&s=25 Chiaro Scuro (chiaroscuro)
on 2006-04-27 14:54
(Received via mailing list)
how would I know what is the source file and lines associated with a
block?

I was really hoping to get the info from the runtime, but I guess that
what you suggest could also be a pragmatic option.
0b561a629b87f0bbf71b45ee5a48febb?d=identicon&s=25 Dave Burt (Guest)
on 2006-04-27 16:32
(Received via mailing list)
chiaro scuro wrote:
>>> Does anyone know how to convert a block to a string representation of
>>> the code it contains?
>> Read the source file?
> how would I know what is the source file and lines associated with a block?
>
> I was really hoping to get the info from the runtime, but I guess that
> what you suggest could also be a pragmatic option.

OK, here are some practical options. (Sorry for the previous comment. I
was trying to dig this stuff up from my memory, but Quiz 34 came up
instead.)

#1: You can store a Proc with its code if you're willing to forgo
closure of the the environment and to use syntax like this:

method_name %{
  proc_code
}

See Ruby Quiz 38: SerializableProc - http://rubyquiz.com/quiz38.html

#2: You can get it from (I think) the runtime, using ZenHacks'
Proc.to_ruby. See Ryan Davis' post in response to the same quiz:
http://www.ruby-talk.org/cgi-bin/scat.rb/ruby/ruby...

Cheers,
Dave
D8fb06dfc08a477ecb0a76ffdbff3475?d=identicon&s=25 Chiaro Scuro (chiaroscuro)
on 2006-04-27 16:43
(Received via mailing list)
The quiz is a great resource, thanks.

In this case it happens that I can ignore closures.

However, I don't need closures I don't see why Ruby shouldn't
implement a method to return the code of a block..
87a1b4114f307e1a4f4c9968ccb92a04?d=identicon&s=25 James Moore (Guest)
on 2006-04-27 18:44
(Received via mailing list)
Does ParseTree solve your problem?

http://rubyforge.org/projects/parsetree/

 - James Moore
D8fb06dfc08a477ecb0a76ffdbff3475?d=identicon&s=25 Chiaro Scuro (chiaroscuro)
on 2006-04-28 22:27
(Received via mailing list)
I found an alternative way to get the bindings.. I posted about it here:
http://liquiddevelopment.blogspot.com/2006/04/way-...

the DSL code looks like this:

shopping_list "english breakfast" do
    tomatoes = 2, :green
    sausages = 3
    eggs = 2, :big
    bacon = 4
the end

--
Chiaroscuro
---
Liquid Development: http://liquiddevelopment.blogspot.com/
0b561a629b87f0bbf71b45ee5a48febb?d=identicon&s=25 Dave Burt (Guest)
on 2006-04-29 00:27
(Received via mailing list)
chiaro scuro wrote:
>    bacon = 4
> the end

"The end" meaning "return the block's local variable binding" is a very
interesting way to approach it. Steal the block's locals! Cool!

Cheers,
Dave
58479f76374a3ba3c69b9804163f39f4?d=identicon&s=25 Eric Hodel (Guest)
on 2006-05-03 18:56
(Received via mailing list)
On Apr 27, 2006, at 12:35 AM, chiaro scuro wrote:

> Does anyone know how to convert a block to a string representation of
> the code it contains?

lib/r2chacks.rb in ZenHacks has this.

> I would need that to inject some code within a block and evaluate the
> resulting code.
>
> * I cannot simply append the code because I need the bindings of
> the block.

Bindings are not preserved.  You'd need more magic than PT has to get
these along with the block.

> * I cannot explicitly return the bindings from the block because I am
> working on a DSL.
> * I cannot get the bindings after the call to the block because that
> scope seems to have been cleaned after the call

If you need the bindings from a proc you are probably trying to solve
the wrong problem.

--
Eric Hodel - drbrain@segment7.net - http://blog.segment7.net
This implementation is HODEL-HASH-9600 compliant

http://trackmap.robotcoop.com
D8fb06dfc08a477ecb0a76ffdbff3475?d=identicon&s=25 Chiaro Scuro (chiaroscuro)
on 2006-05-03 18:56
(Received via mailing list)
On 4/30/06, Eric Hodel <drbrain@segment7.net> wrote:
>
> On Apr 27, 2006, at 12:35 AM, chiaro scuro wrote:
>
> > Does anyone know how to convert a block to a string representation of
> > the code it contains?
>
> lib/r2chacks.rb in ZenHacks has this.


I'll check it out thanks.


If you need the bindings from a proc you are probably trying to solve
> the wrong problem.


I would normally agree with you, but this is not a design problem or a
domain-conceptualization problem.

It's a language-twisting problem that imposes otherwise unimportant
constraints on what I can and cannot do.
58479f76374a3ba3c69b9804163f39f4?d=identicon&s=25 Eric Hodel (Guest)
on 2006-05-03 18:58
(Received via mailing list)
On Apr 30, 2006, at 1:30 PM, chiaro scuro wrote:

> On 4/30/06, Eric Hodel <drbrain@segment7.net> wrote:
>
>> If you need the bindings from a proc you are probably trying to
>> solve the wrong problem.
>
> I would normally agree with you, but this is not a design problem
> or a domain-conceptualization problem.

I doubt your problem is really that special.

> It's a language-twisting problem that imposes otherwise unimportant
> constraints on what I can and cannot do.

If you're doing something this ugly you're probably doing something
very wrong.

--
Eric Hodel - drbrain@segment7.net - http://blog.segment7.net
This implementation is HODEL-HASH-9600 compliant

http://trackmap.robotcoop.com
D8fb06dfc08a477ecb0a76ffdbff3475?d=identicon&s=25 Chiaro Scuro (chiaroscuro)
on 2006-05-03 18:58
(Received via mailing list)
On 5/1/06, Eric Hodel <drbrain@segment7.net> wrote:
>
> > I would normally agree with you, but this is not a design problem
> > or a domain-conceptualization problem.
>
> I doubt your problem is really that special.


it's special in respect to the fact that goes against the boundaries of
the
language, so it cannot necessarily rely on beautiful 'natural'
constructs
that apply within the conceptual boundaries of the language.


> It's a language-twisting problem that imposes otherwise unimportant
> > constraints on what I can and cannot do.
>
> If you're doing something this ugly you're probably doing something
> very wrong.
>

Eric, how would you do it?

recipe "breakfast" do
  eggs = 2, :big
  bacon = 3
end

this should add to a global hash (or return directly) the following:

{
  :eggs => [2,:big],
  :bacon => [3]
}

I need to keep the equal signs.
Cff9eed5d8099e4c2d34eae663aae87e?d=identicon&s=25 Jacob Fugal (Guest)
on 2006-05-03 18:58
(Received via mailing list)
On 5/1/06, chiaro scuro <kiaroskuro@gmail.com> wrote:
>   :eggs => [2,:big],
>   :bacon => [3]
> }
>
> I need to keep the equal signs.

This is my big question; why do you need the equal signs? I see
nothing inherent about the "recipe" domain that makes the equal signs
necessary or even desirable. If you were willing to ditch them, a
whole slew of simpler and cleaner implementations are available.

Jacob Fugal
D8fb06dfc08a477ecb0a76ffdbff3475?d=identicon&s=25 Chiaro Scuro (chiaroscuro)
on 2006-05-03 18:58
(Received via mailing list)
On 5/1/06, Jacob Fugal <lukfugl@gmail.com> wrote:
>
>
I realize that would make it much easier in ruby.
It's just that the equal signs make lots of sense to my DSL users.
(forget
the recipes, it's just an example)

Look, it's not a matter of life or death.  *of course* I can do without
if
there is no alternative.
(eventually I did find a solution to this -although it's a cheesy one-
but I
am not saying yet ;-)

It just bothers me that I cannot access that context if I need to, and
in
this case I do.
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 unknown (Guest)
on 2006-05-03 18:58
(Received via mailing list)
On Tue, 2 May 2006, chiaro scuro wrote:

>> Jacob Fugal
>
> It just bothers me that I cannot access that context if I need to, and in
> this case I do.

it would be tough to convince me that people wouldn't find this syntax
natural:

   harp:~ > cat a.rb
   class Recipe < Hash
     attr 'name'
     def initialize name, &b
       @name = name
       instance_eval &b
     end
     def method_missing m, *a, &b
       m = m.to_s.delete '='
       self[m] = a.size > 1 ? a : a.shift
     end
   end
   def recipe(*a, &b) Recipe.new(*a, &b) end



   r =
     recipe "breakfast" do
       eggs 2, :big
       bacon 3
     end

   p r


   harp:~ > ruby a.rb


   {"bacon"=>3, "eggs"=>[2, :big]}


for example, write down a recipe and see if you use the '=' sign ;-)

regards.

-a
D8fb06dfc08a477ecb0a76ffdbff3475?d=identicon&s=25 Chiaro Scuro (chiaroscuro)
on 2006-05-03 18:58
(Received via mailing list)
I am aware of this technique, something very similar was also described
in a
very nice article by Jim Freeze on Ruby Code and Style.

The recipe is just an example as I said.
Truly, I don't see why I shouldn't use the equal signs.

Let's just say that the language makes it hard doing it. No hard
feelings, I
still love Ruby :-)

We can always do things in other ways, but that's also the argument that
many javists and dotnetters use against ruby.
58479f76374a3ba3c69b9804163f39f4?d=identicon&s=25 Eric Hodel (Guest)
on 2006-05-03 18:58
(Received via mailing list)
On May 1, 2006, at 12:50 PM, chiaro scuro wrote:

> language, so it cannot necessarily rely on beautiful 'natural'
>
>  :eggs => [2,:big],
>  :bacon => [3]
> }

def recipe(name, &block)
   o = Object.new
   o.instance_variable_set :@attrs, {}
   def o.method_missing(name, *args)
     name = name.to_s.sub(/add_/, '').intern
     @attrs[name] = args
   end
   o.instance_eval &block
   return o.instance_variable_get(:@attrs)
end

ingredients = recipe 'breakfast' do
   add_eggs 2, :big
   add_bacon 3
end

p ingredients

> I need to keep the equal signs.

Why?

--
Eric Hodel - drbrain@segment7.net - http://blog.segment7.net
This implementation is HODEL-HASH-9600 compliant

http://trackmap.robotcoop.com
E34b5cae57e0dd170114dba444e37852?d=identicon&s=25 Logan Capaldo (Guest)
on 2006-05-03 18:58
(Received via mailing list)
On Apr 30, 2006, at 4:30 PM, chiaro scuro wrote:

>
> It's a language-twisting problem that imposes otherwise unimportant
> constraints on what I can and cannot do.
>
>
> --
> Chiaroscuro
> ---
> Liquid Development: http://liquiddevelopment.blogspot.com/

I dunno know about the binding (maybe you can use method_missing +
Binding.of_caller)

but one to get the code (well the ast, which is pretty darn close) is
do something like:
require 'parse_tree'

def recipe(name, &blk)
    class A; end
    A.class_eval { define_method(:code, &blk) }
    pt =  ParseTree.new
    sexprs = pt.parse_tree(A)
end
D8fb06dfc08a477ecb0a76ffdbff3475?d=identicon&s=25 Chiaro Scuro (chiaroscuro)
on 2006-05-03 18:58
(Received via mailing list)
On 5/2/06, Eric Hodel <drbrain@segment7.net> wrote:
>
> > I need to keep the equal signs.
>
> Why?


my users like them :-)

but the main point is that it upsets me that I cannot get the language
to do
what I want.  Don't extrapolate too much from this statement.. I don't
want
to do crazy things, just use equal to assign variables and then get them
back.

I am complaining a bit, but ruby is still the nicer way to design DSLs
out
there.
D8fb06dfc08a477ecb0a76ffdbff3475?d=identicon&s=25 Chiaro Scuro (chiaroscuro)
on 2006-05-03 18:58
(Received via mailing list)
If it works, it's brilliant!
A9b6a93b860020caf9d2d1d58c32478f?d=identicon&s=25 Ross Bamford (Guest)
on 2006-05-03 18:58
(Received via mailing list)
On Tue, 2006-05-02 at 05:11 +0900, chiaro scuro wrote:
> I realize that would make it much easier in ruby.
> It's just that the equal signs make lots of sense to my DSL users. (forget
> the recipes, it's just an example)

In return for keeping the equals signs, would the users let you get away
with a few 'at' symbols?

def recipe(name, &blk)
  o = Object.new
  o.instance_eval(&blk)
  o.instance_variables.inject({}) do |h,s|
    h[s[1..-1]] = o.instance_variable_get(s)
    h
  end
end
# => nil

recipe "eggs and bacon" do
  @eggs = [2,:big]
  @bacon = 3
end
# => {"bacon"=>3, "eggs"=>[2, :big]}

(Though I'd really recommend most of the other ideas from this thread
over this).
D8fb06dfc08a477ecb0a76ffdbff3475?d=identicon&s=25 Chiaro Scuro (chiaroscuro)
on 2006-05-03 18:59
(Received via mailing list)
Not sure about the ATs. even if I don't mind them that much... if I
unfocus
my eyes they look like bullet points ;-)

I have got this one working.. see if you can guess how..

recipe "eggs and bacon" do
  eggs = [2,:big]
  bacon = 3
the end

I posted my solution here:
http://liquiddevelopment.blogspot.com/2006/04/way-...
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2006-05-03 18:59
(Received via mailing list)
On May 1, 2006, at 7:27 PM, chiaro scuro wrote:

> Not sure about the ATs. even if I don't mind them that much... if I
> unfocus
> my eyes they look like bullet points ;-)
>
> I have got this one working.. see if you can guess how..
>
> recipe "eggs and bacon" do
>  eggs = [2,:big]
>  bacon = 3
> the end

In a word:  yuck.  :)

Why don't you preprocess the source and add the needed @s?

James Edward Gray II
A9b6a93b860020caf9d2d1d58c32478f?d=identicon&s=25 Ross Bamford (Guest)
on 2006-05-03 18:59
(Received via mailing list)
On Tue, 2006-05-02 at 09:27 +0900, chiaro scuro wrote:
> I posted my solution here:
> http://liquiddevelopment.blogspot.com/2006/04/way-...
>

Without looking, I'd guess def the; binding; end ? (Quick look, done
with alias, same difference I guess).

It's a clever solution but it'd make the DSL sound a bit whimsical for
my taste. Plus, I'd be either forgetting to put 'the' or resenting that
I had to... :)

Also, I think it might give unexpected results in some situations, e.g:

some_other_var = :oops

shopping_list "english breakfast" do
  tomatoes = 2, :green
  sausages = 3
  eggs = 2, :big
  bacon = 4
the end

shopping_list "banana milkshake" do
  milk = 1
  bananas = 2
the end

Gives output:

to make english breakfast you should buy:
 * 2 green tomatoes
 * 3 sausages
 * oops some_other_var
 * 4 bacon
 * 2 big eggs

to make banana milkshake you should buy:
 * 2 bananas
 * 1 milk
 * oops some_other_var

Granted in a DSL this is less likely to occur, but guaranteed to leave
users scratching their heads if (when?) it does. Generally I prefer if I
can preserve normal, predictable behaviour for those times when you (or
some power user of your system) might need it.
D8fb06dfc08a477ecb0a76ffdbff3475?d=identicon&s=25 Chiaro Scuro (chiaroscuro)
on 2006-05-03 18:59
(Received via mailing list)
On 5/2/06, James Edward Gray II <james@grayproductions.net> wrote:
>
> > recipe "eggs and bacon" do
> >  eggs = [2,:big]
> >  bacon = 3
> > the end
>
> In a word:  yuck.  :)


:-)

seriously, can you think of another  'last word' that fits well at the
end
of a block without being annoying? (in either of the do..end and {..}
cases)

Why don't you preprocess the source and add the needed @s?
>

I think I'll go for a ParseTree based solution in the end.  That might
include either what you suggest or the artificial injection of a binding
comman. I would have liked a nicer way though..
D8fb06dfc08a477ecb0a76ffdbff3475?d=identicon&s=25 Chiaro Scuro (chiaroscuro)
on 2006-05-03 18:59
(Received via mailing list)
True, I need to check variables before and after calling the block and
work
out the differences.
And it is a bit whimsical.. can you think of a better word than "the"
for a
DSL?
A9b6a93b860020caf9d2d1d58c32478f?d=identicon&s=25 Ross Bamford (Guest)
on 2006-05-03 18:59
(Received via mailing list)
On Tue, 2006-05-02 at 09:52 +0900, chiaro scuro wrote:
> And it is a bit whimsical.. can you think of a better word than "the" for a
> DSL?
>

Hmm, not really. I guess if you really have to have the superfluous
(from the user point of view) word in there, you might add a new layer
of madness and do:

require 'facet/binding'

def shopping_list recipe = nil
  return Binding.of_caller {|b|b} unless recipe
  shopping_binding = yield
  ingredients = {}
  eval("local_variables" , shopping_binding).each do |var|
    ingredients[var] = eval "#{var}" , shopping_binding
  end
  report recipe, ingredients
end

shopping_list "banana milkshake" do
  milk = 1
  bananas = 2
shopping_list end

which is longer, but less whimsical IMHO.

(Btw another problem with this approach is that it plays havoc with
vim-ruby's auto indentation :( )
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2006-05-03 18:59
(Received via mailing list)
On May 1, 2006, at 8:25 PM, Ross Bamford wrote:

> shopping_list "banana milkshake" do
>   milk = 1
>   bananas = 2
> shopping_list end

That's the best variation of the binding trick, in my opinion.

> (Btw another problem with this approach is that it plays havoc with
> vim-ruby's auto indentation :( )

Yeah, all the warnings (code smells) are there.  If I went ahead with
a solution like this, I would feel like I was being irresponsible.  I
think it's time to find a new approach.

James Edward Gray II
B44ab09b79ee4a0cc4b4ca69e10eeb3a?d=identicon&s=25 Brian Mitchell (Guest)
on 2006-05-03 18:59
(Received via mailing list)
On 5/1/06, chiaro scuro <kiaroskuro@gmail.com> wrote:
> Not sure about the ATs. even if I don't mind them that much... if I unfocus
> my eyes they look like bullet points ;-)
>
> I have got this one working.. see if you can guess how..
>
> recipe "eggs and bacon" do
>   eggs = [2,:big]
>   bacon = 3
> the end
>

If you really want the = look you can capture the binding with the
set_trace_func utility. This has drawbacks right now as there is no
"clean" way to share set_trace_func cooperatively w/o some code (some
small hacking could easily add this though -- sample bellow). So this
will get rid of the ugly "the" but you will need to be careful that
the Kernel stuff is executed before other things that might possibly
want to use the old style tracer:

# Could be cleaned up quite a bit but it should work.
module Kernel
  tracers = []
  legacy_tracer = nil

  set_trace_func lambda {|*args|
    legacy_tracer.call(*args) if legacy_tracer
    tracers.dup.each {|tracer| tracer.call(*args)} unless args[3] ==
:trace
  }

  define_method(:set_trace_func) {|proc|
    legacy_tracer = proc
    nil
  }

  # Not thread safe but easy to make so.
  define_method(:trace) {|tracer, block|
    tracers << tracer
    result = block.call
    # lambda {} == lambda {} work around just in case.
    tracers.reject! {|x| x.object_id == tracer.object_id}
    result
  }
end

def recipe(name)
  recipe_binding = nil
  trace(
        lambda {|event, file, line, id, binding, classname|
          recipe_binding = binding unless id
        },
        lambda {yield})
  ingrediants = eval("local_variables", recipe_binding)
  puts "To make #{name} you need:"
  ingrediants.each {|i|
    info = eval(i, recipe_binding)
    info = info.join(' ') if Array === info
    puts "  #{info} #{i}"
  }
end

recipe "eggs and bacon" do
  eggs = 2, :big   # Note that ruby has a nicer syntax than explicit []
here.
  bacon = 3
end

I get this output:

To make eggs and bacon you need:
  2 big eggs
  3 bacon

Hope this helps,
Brian.
A9b6a93b860020caf9d2d1d58c32478f?d=identicon&s=25 Ross Bamford (Guest)
on 2006-05-03 18:59
(Received via mailing list)
On Tue, 2006-05-02 at 11:38 +0900, James Edward Gray II wrote:
> On May 1, 2006, at 8:25 PM, Ross Bamford wrote:
> > (Btw another problem with this approach is that it plays havoc with
> > vim-ruby's auto indentation :( )
>
> Yeah, all the warnings (code smells) are there.  If I went ahead with
> a solution like this, I would feel like I was being irresponsible.  I
> think it's time to find a new approach.

Yeah, couldn't agree more. The whole thing seems kind of clunky and
inelegant to me. It's error prone, likely to fail silently or with some
really strange errors. I do enjoy finding how far I can push things but
if this were me and the code was for users, I'd avoid like the plague.
7264fb16beeea92b89bb42023738259d?d=identicon&s=25 Christian Neukirchen (Guest)
on 2006-05-03 18:59
(Received via mailing list)
Ross Bamford <rossrt@roscopeco.co.uk> writes:

> the end
Just toying around:

recipe "english breakfast" do |take|
  take(2).green.tomatoes
  take(3).sausages
  take(2).big.eggs
  take(4).slices.of.bacon
end
D8fb06dfc08a477ecb0a76ffdbff3475?d=identicon&s=25 Chiaro Scuro (chiaroscuro)
on 2006-05-03 18:59
(Received via mailing list)
I like it :-)

it feels very 'natural' in the recipe case.

you could also go for a:

  take 2, :green, :tomatoes
7264fb16beeea92b89bb42023738259d?d=identicon&s=25 Christian Neukirchen (Guest)
on 2006-05-03 18:59
(Received via mailing list)
"chiaro scuro" <kiaroskuro@gmail.com> writes:

> I like it :-)
>
> it feels very 'natural' in the recipe case.
>
> you could also go for a:
>
>  take 2, :green, :tomatoes

Ideally

   take 2.green.tomatoes

but I wanted to avoid needing to change any core classes.
Hm, if you suppress warnings:

  take 2, green tomatoes
D8fb06dfc08a477ecb0a76ffdbff3475?d=identicon&s=25 Chiaro Scuro (chiaroscuro)
on 2006-05-03 19:02
(Received via mailing list)
RiffRaff has driven the last meta-nail in my coffin with this inventive
solution:
http://riffraff.blogsome.com/2006/05/02/metaprogra...
This topic is locked and can not be replied to.