>> 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.
···
--- Robert Klemme <bob.news@gmx.net> wrote:
__________________________________
Discover Yahoo!
Use Yahoo! to plan a weekend, have fun online and more. Check it out!
http://discover.yahoo.com/