--- 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/