From block to code (also: how to get the bindings of a block

Not sure about the ATs. even if I don’t mind them that much… if I
unfocus
my eyes they look like bullet points :wink:

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-of-meta-part-iv-hijacking-local.html

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

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 :wink:

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. :slight_smile:

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

James Edward G. II

On Tue, 2006-05-02 at 09:27 +0900, chiaro scuro wrote:

I posted my solution here:
http://liquiddevelopment.blogspot.com/2006/04/way-of-meta-part-iv-hijacking-local.html

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… :slight_smile:

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.

On 5/2/06, James Edward G. II [email protected] wrote:

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

In a word: yuck. :slight_smile:

:slight_smile:

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…

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?

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 :frowning: )

On 5/1/06, chiaro scuro [email protected] 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 :wink:

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.

On Tue, 2006-05-02 at 11:38 +0900, James Edward G. II wrote:

On May 1, 2006, at 8:25 PM, Ross B. wrote:

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

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.

On May 1, 2006, at 8:25 PM, Ross B. 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 :frowning: )

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 G. II

Ross B. [email protected] 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

I like it :slight_smile:

it feels very ‘natural’ in the recipe case.

you could also go for a:

take 2, :green, :tomatoes

“chiaro scuro” [email protected] writes:

I like it :slight_smile:

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

RiffRaff has driven the last meta-nail in my coffin with this inventive
solution:
http://riffraff.blogsome.com/2006/05/02/metaprogramming-breakfast/

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs