--- Robert Klemme <bob.news / gmx.net> wrote:
> >> When do you think will unbind_locals be useful?
> >
> > As a replacement for many string evals - which are ugly,
> > inefficient, and possibly dangerous.  Many (most?) times
> that
> > you need to eval a string it is because you need to pull in
> > some local variables to help define the string to be
> evaled.
> > Here is the first example of a string eval in the 1.8
> library I
> > found:
> >
> > for element in %w[ HTML HEAD BODY P PLAINTEXT DT DD
> >                    LI OPTION tr th td ]
> >     methods += <<-BEGIN + nO_element_def(element) + <<-END
> >       def #{element.downcase}(attributes = {})
> >     BEGIN
> >       end
> >     END
> > end
> > eval(methods)
> >
> > This could be replaced by:
> >
> > for element in %w[ HTML HEAD BODY P PLAINTEXT DT DD
> >                    LI OPTION tr th td ]
> >     define_method(element.downcase.to_sym ,
> >         proc { |attributes={}|
> >             nO_element_def(element,attributes)
> >         }.unbind_locals # replace element with constant
> >     )
> > end
> >
> > Much cleaner, huh?
> 
> Not really (at least to my eyes).  Also, there are some
> issues:
> 
>  - I don't know what nO_element_def does exactly, but it will
> have to be
> rewritten to not create a string with ruby code

Yep.  I wasn't paying attention very well.  Here is its
definition (this is from cgi.rb, BTW):

def nOE_element_def(element, append = nil)
    s = <<-END
        "<#{element.upcase}" + attributes.collect{|name, value|
            next unless value
            " " + CGI::escapeHTML(name) +
            if true == value
                ""
            else
                '="' + CGI::escapeHTML(value) + '"'
            end
        }.to_s + ">"
    END
    s.sub!(/\Z/, " +") << append if append
    s
end
def nO_element_def(element)
   nOE_element_def(element, <<-END)
       if block_given?
           yield.to_s + "</#{element.upcase}>"
       else
           ""
       end
   END
end

Here would be the evaluating (instead of generating) version:

def nOE_element_def(element,attributes)
    "<#{element.upcase}" + attributes.collect{|name, value|
        next unless value
        " " + CGI::escapeHTML(name) +
        if true == value
            ""
        else
            '="' + CGI::escapeHTML(value) + '"'
        end
    }.to_s + ">"
end
def nO_element_def(element,attributes)
    nOE_element_def(element,attributes) +
    if block_given?
        yield.to_s + "<#{element.upcase}>"
    else
        ""
    end
end

>  - Your version might be less efficient.

If you flatten the hierarchy, you should be able to get the
same efficiency:

for element in %w[ HTML HEAD BODY P PLAINTEXT DT DD
                   LI OPTION tr th td ]
  define_method(element.downcase.to_sym ,
    proc { |attributes={}|
      "<#{element.upcase}" + attributes.collect{|name, value|
        next unless value
        " " + CGI::escapeHTML(name) +
        if true == value
          ""
        else
          '="' + CGI::escapeHTML(value) + '"'
        end
      }.to_s + ">" +
      if block_given?
        yield.to_s + "<#{element.upcase}>"
      else
        ""
      end
    }.unbind_locals # replace element with constant
  )
end

>  - There might be subtle differences because "element" is
> pulled into the
> closure
> 
>  - Also, unbind_locals will remove "element" from the procs
> bindings

I'm assuming that unbind_locals will simply replace any local
variables (external to the proc) with what their current value
is: element will become "HTML", "HEAD", "BODY", etc.

> >> A proc
> >> typically needs some
> >> of the variables bound.  As for the rebindings, I would
> >> prefer a general
> >> mechanism to transfer state from one binding to another.
> >> Then one could
> >> implement all your rebind* methods in terms of that
> general
> >> mechanism plus
> >> do more.  Alternatively one could think about conversion
> >> methods Binding <->
> >> Hash.
> >
> > Transferring locals might be pretty easy, but transferring
> the
> > meaning of self would be more difficult, I think.  At least
> > without making a new Binding (and then you'd still need a
> way
> > to rebind it to the original proc).
> 
> Why do you think that self is special?  If there is a general
> mechanism to
> transfer state (i.e. bindings) into a binding, any variable
> can be
> rebound.  I imagine something like
> 
> proc.binding.bind(:self => whatever, :foo => "bar")
> eval("self", proc.binding) # -> whatever
> eval("foo", proc.binding) # -> "bar"

I was assuming that with local variables, you just be moving
the value from one binding to another - not aliasing.  The
problem is that you can't assign to "self".  If you really
could do the above, then this:

binding.bind(:self => whatever)

would be equivalent to:

self = whatever

I don't that would be a trivial thing to implement.  But, if
this "bind" created a new binding (i.e. non-destructive instead
of destructive), it would seem more feasible:

whateverBinding = binding.bind(:self => whatever)
whateverBinding.self.object_id==whatever.object_id

But then you are back to where you started - you still have a
bind the proc to a different binding (probably returning a new
proc).

> > Anybody know of an
> > equivalent to #define_method for making class methods?  I
> > couldn't figure out any way to define a class method from a
> > proc - just out of curiosity.
> 
> Since a class method is just an instance method of the class:
> 
> >> class Foo;end
> => nil
> >> class <<Foo
> >>   define_method(:bar) {"bar"}
> >> end

Thanks!  That works, but I wanted the proc to have visibility
to the local variables.  I think this would be more useful:

class Foo;end
(class<<Foo;self;end).send(:define_method,:bar) {"bar"}

> Thanks for the interesting exchange!

and thanks for your ideas.


... back to my original topic - making a duck.  For my stuff, I
think I've decided to have 2 ways of doing it:

# make a duck using methods from obj
myDuck = obj.duck(newSym, oldSym, ...)

# make a duck using arbitrary procs
myDuck = duck(methSym => methProc, ...)

One of the applications I'm looking at now is for a method that
looks like this:

Cursor#scan(value)

where value is String/Array like.  All it needs is to respond
to #[int] which should return an object that responds to #==. 
Although this #scan may look like it just matches to a verbatim
String/Array, you could also do something like this to match to
some digits:

# make == look like ===
digit = (?0..?9).duck(:==,:===)
# match to a string of 4 digits
# Proc responds to [] like a String/Array
cursor.scan(proc{|i|(i<4)? digit : nil}) 

Do people do things like this with duck-typing?  Or are most
just all talk.  Doing something like the above is where I see
the power.




		
__________________________________ 
Discover Yahoo! 
Use Yahoo! to plan a weekend, have fun online and more. Check it out! 
http://discover.yahoo.com/