H
Hugh Sasse Staff Elec Eng
I seem to have run into a conceptual barrier, and it has caused me
to rethink over ideas I've had about procs and blocks in the
past....
I'm trying to build a gui based system -- the application doesn't
matter for this discussion -- and I'd like to use Tk because it is
more dynamic that the C++ based kits. So I think about the ideas in
The Pragmatic Programmer about Providing Options, and Configure,
Don't Integrate, and it seems a good idea to make some of the
windows pickup their controls from a file.
That's all well and good, and things like Marshal or Yaml handle
those details nicely. But my widgets have callbacks, and in Ruby/Tk
those are proc objects which can't be marshalled. Ok, I can save
the block as a string and load it back in and do eval on it, or
instance eval or some such, but then the block doesn't get checked
for correctness very early because it is no longer code. Also this
opens the whole thing up for abuse: the usual "deliver us from eval"
issues of importing code from files and trusting it. I'm going to
be encouraging people to tweak these configuration files, so I'd be
inviting them to insert code, which is bad. I accept that having the
ruby code on the machine is a weakness anyway, but most users won't
poke around in that.
I could introduce a mini language, so that my procs defined
elsewhere get picked up, but that feels like an abstraction too far,
if I try to make it flexible enough to be useful it will be too big
to manage. I think, anyway.
Is there another way to meet these conflicting goals of flexibility
and some security?
The second part of this is:
I find myself wanting blocks to be real objects before they become
procs. I'm trying to think this out but it feels like grasping wet
soap -- I can't quite get a hold on the idea. I'm thinking
something like this:
We can't marshal procs because they carry external state.
We can't marshal blocks or lambdas for the same reason.
When you marshal an object you give it enough information to be
re-constructed, possibly minus a few features (things that don't get
dumped).
Can we marshal a proto-block, that when loaded in a context
causes a block to come into being in that context, without
opening ourselves up to a full eval? Then we could do something
like Block.new(Marshal.load(...)) to get a block, to pass to
proc or whatever. Possibilities that this would have include
warnings on the code within the block before/after it is
serialized/restored, and formatting as some AST so it is less
prone to casual hacking when serialized [caveats about security
through obscurity apply].
I'm thinking that simplifying this would enable greater utility, as
is true for all abstractions, as well as aid debugging.
This is probably hard for me to think about clearly because there's
a big flaw in it....
Thank you,
Hugh
to rethink over ideas I've had about procs and blocks in the
past....
I'm trying to build a gui based system -- the application doesn't
matter for this discussion -- and I'd like to use Tk because it is
more dynamic that the C++ based kits. So I think about the ideas in
The Pragmatic Programmer about Providing Options, and Configure,
Don't Integrate, and it seems a good idea to make some of the
windows pickup their controls from a file.
That's all well and good, and things like Marshal or Yaml handle
those details nicely. But my widgets have callbacks, and in Ruby/Tk
those are proc objects which can't be marshalled. Ok, I can save
the block as a string and load it back in and do eval on it, or
instance eval or some such, but then the block doesn't get checked
for correctness very early because it is no longer code. Also this
opens the whole thing up for abuse: the usual "deliver us from eval"
issues of importing code from files and trusting it. I'm going to
be encouraging people to tweak these configuration files, so I'd be
inviting them to insert code, which is bad. I accept that having the
ruby code on the machine is a weakness anyway, but most users won't
poke around in that.
I could introduce a mini language, so that my procs defined
elsewhere get picked up, but that feels like an abstraction too far,
if I try to make it flexible enough to be useful it will be too big
to manage. I think, anyway.
Is there another way to meet these conflicting goals of flexibility
and some security?
The second part of this is:
I find myself wanting blocks to be real objects before they become
procs. I'm trying to think this out but it feels like grasping wet
soap -- I can't quite get a hold on the idea. I'm thinking
something like this:
We can't marshal procs because they carry external state.
We can't marshal blocks or lambdas for the same reason.
When you marshal an object you give it enough information to be
re-constructed, possibly minus a few features (things that don't get
dumped).
Can we marshal a proto-block, that when loaded in a context
causes a block to come into being in that context, without
opening ourselves up to a full eval? Then we could do something
like Block.new(Marshal.load(...)) to get a block, to pass to
proc or whatever. Possibilities that this would have include
warnings on the code within the block before/after it is
serialized/restored, and formatting as some AST so it is less
prone to casual hacking when serialized [caveats about security
through obscurity apply].
I'm thinking that simplifying this would enable greater utility, as
is true for all abstractions, as well as aid debugging.
This is probably hard for me to think about clearly because there's
a big flaw in it....
Thank you,
Hugh