Discussion:
RFC: IOC Configuration syntax/mini-language
Stevan Little
2004-12-27 21:04:40 UTC
Permalink
So I have been using my module IOC a lot lately, so far so good, but I
have found that writing the code to create all the containers and
services and such gets quite tedious. For instance to do a basic DBI
container, I do something like this:

my $r = IOC::Registry->new('MyApp');
my $c = IOC::Container->new('DBI');
$c->register(IOC::Service::Literal->new('dsn' =>
'dbi:mysql:test'));
$c->register(IOC::Service::Literal->new('username' => 'test'));
$c->register(IOC::Service::Literal->new('password' => '****'));
$c->register(IOC::Service::ConstructorInjection->new('connection' => (
'DBI', 'connect' [
IOC::Service::ConstructorInjection->ComponentParameter('dsn'),
IOC::Service::ConstructorInjection->ComponentParameter('username'),
IOC::Service::ConstructorInjection->ComponentParameter('password')
]
)));
$r->registerContainer($c);

# ... then in my app all I have to do is

my $reg = IOC::Registry->instance();
my $db_conn = $reg->locateService('/MyApp/DBI/connection');

Writing the code to configure the services, containers and registry
though, can get quite tiring. Fortunately, I really only need to do
this once and then just use it, but ideally I would like it to be a
little less taxing on the carpal tunnel.

My first idea was to create subclasss of IOC::Container that would take
a number of arguments and return a fully formed container. I wrote a
basic IOC::Container::DBI which only needed to be passed a $dsn,
$username and $password and it would create what I have above. But that
idea only works for common things which you do all the time, not for
custom stuff.

Then it occurred to me that it would be even nicer to have some kind of
very basic configuration syntax/mini-language which would do this. So i
searched the Config:: namespace on CPAN and found nothing which seemed
to fit the bill.

So before I start really writing a custom parser and such I thought I
would get the opinion of others. Here is an example of how the above
configuration might look:

MyApp: [
DBI: [
dsn: 'dbi:mysql:test'
username: test
password: '****'
connection: [ DBI connect [ @dsn @username @password ] ]
]
]

This would roughly translate into the following perl data structure,
which I would then use to create the actual IOC objects:

{
'MyApp' => {
'DBI' => {
'dsn' => 'dbi:mysql:test',
'username' => 'test',
'password' => '****',
'connection' => [ 'DBI', 'connect', [ 'dsn', 'username', 'password'
]]
}
}
}

So as you can see in some cases, blocks ([]) will become hashes, and in
others they will become arrays. The way it knows is that if the first
value is a string with a colon at the end (:) it is a hash, otherwise
it is an array. The '@' prefix/sigil would indicate that the value is
not just a string, but a path or name of a component within the
IOC::Container.

I have worked out more complex examples as well, for instance this is
how IOC::Service::SetterInjection would be handled:

FileLogger: [
FlieLogger new [
[ set_log_file: @/MyApp/log_file_name ]
]]

this would become the following IOC configuration:

IOC::Service::SetterInjection->new('FileLogger' => (
'FileLogger', 'new', [
{ set_log_file => '/MyApp/log_file_name' }
]
)))

I have also devised a way to do the regular IOC::Service object, which
use the sub-ref parameter. Here is an example of the DBI configuration
above using this:

MyApp: [
DBI: [
dsn: 'dbi:mysql:test'
username: test
password: '****'
connection: <<<
my $c = shift;
return DBI->connect($c->get('dsn'), $c->get('username'),
$c->get('password'));
]
]

It would be like substituting this in the first config example:

$c->register(IOC::Service::->new('connection' => sub {
my $c = shift;
return DBI->connect($c->get('dsn'), $c->get('username'),
$c->get('password'));
}));

Obviously the string between '<<<' and '>>>' will get wrapped in 'sub {
}' and evaled. I am assuming that this will work since '<<<' and '>>>'
are not valid perl identifiers, so I can easily find the end of that
string.

As I said, I am still toying around with this. I have created a quick
and dirty parser already so I know the syntax is workable for that
angle. My question to you all, is;

1) Does this sound like a good idea?
2) Does this syntax look good?
3) Is there any existing Config:: module out there which will do the
dirty work for me?
4) Anyone got any better ideas?

Thanks,

Steve
Rob Kinyon
2004-12-28 14:20:27 UTC
Permalink
What's wrong with Config::ApacheFormat? You have the ability to have
arbitrarily-deep nested structures that you can call whatever you want
...

Rob


On Mon, 27 Dec 2004 16:04:40 -0500, Stevan Little
Post by Stevan Little
So I have been using my module IOC a lot lately, so far so good, but I
have found that writing the code to create all the containers and
services and such gets quite tedious. For instance to do a basic DBI
my $r = IOC::Registry->new('MyApp');
my $c = IOC::Container->new('DBI');
$c->register(IOC::Service::Literal->new('dsn' =>
'dbi:mysql:test'));
$c->register(IOC::Service::Literal->new('username' => 'test'));
$c->register(IOC::Service::Literal->new('password' => '****'));
$c->register(IOC::Service::ConstructorInjection->new('connection' => (
'DBI', 'connect' [
IOC::Service::ConstructorInjection->ComponentParameter('dsn'),
IOC::Service::ConstructorInjection->ComponentParameter('username'),
IOC::Service::ConstructorInjection->ComponentParameter('password')
]
)));
$r->registerContainer($c);
# ... then in my app all I have to do is
my $reg = IOC::Registry->instance();
my $db_conn = $reg->locateService('/MyApp/DBI/connection');
Writing the code to configure the services, containers and registry
though, can get quite tiring. Fortunately, I really only need to do
this once and then just use it, but ideally I would like it to be a
little less taxing on the carpal tunnel.
My first idea was to create subclasss of IOC::Container that would take
a number of arguments and return a fully formed container. I wrote a
basic IOC::Container::DBI which only needed to be passed a $dsn,
$username and $password and it would create what I have above. But that
idea only works for common things which you do all the time, not for
custom stuff.
Then it occurred to me that it would be even nicer to have some kind of
very basic configuration syntax/mini-language which would do this. So i
searched the Config:: namespace on CPAN and found nothing which seemed
to fit the bill.
So before I start really writing a custom parser and such I thought I
would get the opinion of others. Here is an example of how the above
MyApp: [
DBI: [
dsn: 'dbi:mysql:test'
username: test
password: '****'
]
]
This would roughly translate into the following perl data structure,
{
'MyApp' => {
'DBI' => {
'dsn' => 'dbi:mysql:test',
'username' => 'test',
'password' => '****',
'connection' => [ 'DBI', 'connect', [ 'dsn', 'username', 'password'
]]
}
}
}
So as you can see in some cases, blocks ([]) will become hashes, and in
others they will become arrays. The way it knows is that if the first
value is a string with a colon at the end (:) it is a hash, otherwise
not just a string, but a path or name of a component within the
IOC::Container.
I have worked out more complex examples as well, for instance this is
FileLogger: [
FlieLogger new [
]]
IOC::Service::SetterInjection->new('FileLogger' => (
'FileLogger', 'new', [
{ set_log_file => '/MyApp/log_file_name' }
]
)))
I have also devised a way to do the regular IOC::Service object, which
use the sub-ref parameter. Here is an example of the DBI configuration
MyApp: [
DBI: [
dsn: 'dbi:mysql:test'
username: test
password: '****'
connection: <<<
my $c = shift;
return DBI->connect($c->get('dsn'), $c->get('username'),
$c->get('password'));
]
]
$c->register(IOC::Service::->new('connection' => sub {
my $c = shift;
return DBI->connect($c->get('dsn'), $c->get('username'),
$c->get('password'));
}));
Obviously the string between '<<<' and '>>>' will get wrapped in 'sub {
}' and evaled. I am assuming that this will work since '<<<' and '>>>'
are not valid perl identifiers, so I can easily find the end of that
string.
As I said, I am still toying around with this. I have created a quick
and dirty parser already so I know the syntax is workable for that
angle. My question to you all, is;
1) Does this sound like a good idea?
2) Does this syntax look good?
3) Is there any existing Config:: module out there which will do the
dirty work for me?
4) Anyone got any better ideas?
Thanks,
Steve
_______________________________________________
sw-design mailing list
http://metaperl.com/cgi-bin/mailman/listinfo/sw-design
Stevan Little
2004-12-28 15:59:25 UTC
Permalink
Rob,

My problem is that I am not sure Config::ApacheFormat can handle some
of the more complex IOC::Service configurations.

<Registry MyApp>
<Container DBI>
dsn dbi:mysql:test
username test
password ****
connect ??? << how to do this one
</Container>
</Registry>

I want to avoid doing things like this (which can start to get very
verbose):

<Service connect>
class DBI
constructor connect
args @dsn @username @password
</Service>

or this (which I don't actually think would work since the tag name
would be so arbitrary):

<connect>
class DBI
constructor connect
args @dsn @username @password
</connect>

and I am not a fan of this style since it looses the structure of the
args and limits me in the future.

connect DBI connect @dsn @username @password

This has not even touched on the IOC::Service with sub-ref issue. I am
not 100% sure, but I don't think this would be possible:

<Service connect>
my $c = shift;
DBI->connect($c->get('dsn'), $c->get('username'), $c->get('password'));
</Service>

My guess is that Config::Apache format would try to break down the code
into variables.

I have to admit, I do kind of like the <Registry> part of things
though. Its a bit more explicit than just the [] blocks. Something like
this maybe would be ideal:

<Registry MyApp>
<Container DBI>
dsn dbi:mysql:test
username test
password ****
connect [ DBI connect [ @dsn @username @password ]]
</Container>
</Registry>

Believe me, I would love to use a canned Config solution, but I am just
not sure it can handle all the details I need handled (in particular
the sub-ref code one seems to be the most difficult).

- Steve
Post by Rob Kinyon
What's wrong with Config::ApacheFormat? You have the ability to have
arbitrarily-deep nested structures that you can call whatever you want
...
Rob
On Mon, 27 Dec 2004 16:04:40 -0500, Stevan Little
Post by Stevan Little
So I have been using my module IOC a lot lately, so far so good, but I
have found that writing the code to create all the containers and
services and such gets quite tedious. For instance to do a basic DBI
my $r = IOC::Registry->new('MyApp');
my $c = IOC::Container->new('DBI');
$c->register(IOC::Service::Literal->new('dsn' =>
'dbi:mysql:test'));
$c->register(IOC::Service::Literal->new('username' => 'test'));
$c->register(IOC::Service::Literal->new('password' => '****'));
$c->register(IOC::Service::ConstructorInjection->new('connection' => (
'DBI', 'connect' [
IOC::Service::ConstructorInjection->ComponentParameter('dsn'),
IOC::Service::ConstructorInjection->ComponentParameter('username'),
IOC::Service::ConstructorInjection->ComponentParameter('password')
]
)));
$r->registerContainer($c);
# ... then in my app all I have to do is
my $reg = IOC::Registry->instance();
my $db_conn = $reg->locateService('/MyApp/DBI/connection');
Writing the code to configure the services, containers and registry
though, can get quite tiring. Fortunately, I really only need to do
this once and then just use it, but ideally I would like it to be a
little less taxing on the carpal tunnel.
My first idea was to create subclasss of IOC::Container that would take
a number of arguments and return a fully formed container. I wrote a
basic IOC::Container::DBI which only needed to be passed a $dsn,
$username and $password and it would create what I have above. But that
idea only works for common things which you do all the time, not for
custom stuff.
Then it occurred to me that it would be even nicer to have some kind of
very basic configuration syntax/mini-language which would do this. So i
searched the Config:: namespace on CPAN and found nothing which seemed
to fit the bill.
So before I start really writing a custom parser and such I thought I
would get the opinion of others. Here is an example of how the above
MyApp: [
DBI: [
dsn: 'dbi:mysql:test'
username: test
password: '****'
]
]
This would roughly translate into the following perl data structure,
{
'MyApp' => {
'DBI' => {
'dsn' => 'dbi:mysql:test',
'username' => 'test',
'password' => '****',
'connection' => [ 'DBI', 'connect', [ 'dsn',
'username', 'password'
]]
}
}
}
So as you can see in some cases, blocks ([]) will become hashes, and in
others they will become arrays. The way it knows is that if the first
value is a string with a colon at the end (:) it is a hash, otherwise
not just a string, but a path or name of a component within the
IOC::Container.
I have worked out more complex examples as well, for instance this is
FileLogger: [
FlieLogger new [
]]
IOC::Service::SetterInjection->new('FileLogger' => (
'FileLogger', 'new', [
{ set_log_file => '/MyApp/log_file_name' }
]
)))
I have also devised a way to do the regular IOC::Service object, which
use the sub-ref parameter. Here is an example of the DBI configuration
MyApp: [
DBI: [
dsn: 'dbi:mysql:test'
username: test
password: '****'
connection: <<<
my $c = shift;
return DBI->connect($c->get('dsn'),
$c->get('username'),
$c->get('password'));
]
]
$c->register(IOC::Service::->new('connection' => sub {
my $c = shift;
return DBI->connect($c->get('dsn'), $c->get('username'),
$c->get('password'));
}));
Obviously the string between '<<<' and '>>>' will get wrapped in 'sub {
}' and evaled. I am assuming that this will work since '<<<' and '>>>'
are not valid perl identifiers, so I can easily find the end of that
string.
As I said, I am still toying around with this. I have created a quick
and dirty parser already so I know the syntax is workable for that
angle. My question to you all, is;
1) Does this sound like a good idea?
2) Does this syntax look good?
3) Is there any existing Config:: module out there which will do the
dirty work for me?
4) Anyone got any better ideas?
Thanks,
Steve
_______________________________________________
sw-design mailing list
http://metaperl.com/cgi-bin/mailman/listinfo/sw-design
_______________________________________________
sw-design mailing list
http://metaperl.com/cgi-bin/mailman/listinfo/sw-design
Terrence Brannon
2004-12-28 16:50:26 UTC
Permalink
Post by Stevan Little
Rob,
My problem is that I am not sure Config::ApacheFormat can handle some
of the more complex IOC::Service configurations.
<Registry MyApp>
<Container DBI>
dsn dbi:mysql:test
username test
password ****
connect ??? << how to do this one
If you want evaluable code there, that is easy. My CPAN module
Config::DBI uses Config::ApacheFormat for just that reason. DBI takes
a HandleError attribute which is a routine which can be called when it
wants to throw an exception.

Here is how I built the HandleError attribute in the DBI handler to
pass to DBI->connect()

if (my $handler = $block->HandleError)
{
my $hardref = eval "\\&$handler" ;
$A{HandleError} = $hardref;
}

In fact, that Container block above looks just like the one in
Config::DBI.

Config::DBI was my 2nd module focused on storing DBI connection info
and providing $dbh via a simple API. The first was DBIx::Connect

http://search.cpan.org/~tbone/DBIx-Connect-1.13/lib/DBIx/Connect.pm

it was based on AppConfig. I found AppConfig difficult to deal with
for reasons discussed here:

http://perlmonks.org/index.pl?node_id=292455

and so I developed Config::DBI based on Config::ApacheFormat

http://search.cpan.org/~tbone/Config-DBI-1.8/lib/Config/DBI.pm

More recently, I realized that it was wrong of me to expect database
connection information to be structured in a certain
way. Organizations think about their database connection info in
different ways and they have their own preferenecs for which concfig
tool to use... your Registry and Container syntax is living proof of
that. A flexible database connection module should allow you to pass
your data to it but not put restrictions on how your data was
cataloged in your personal tech stack.

I realized that underlying all config representation was
a nested Perl data structure (isn't it great how hashes, arrays and
scalars are standard in Perl so that we dont have confusion over which
array package to use). So even more recently, I coded DBIx::DBH which
takes a hashref as it's argument. The distinctive features about
DBIx::DBH are:

1/ It forms the DSN string for you. DSN strings vary from db vendor to
vendor quite a bit. A DSN is a compound data type. A basic rule of
software design is to have simple elements formed into complex
entities via an API.

2/ It uses Params::Validate for rigourous input validation
Post by Stevan Little
</Container>
</Registry>
I want to avoid doing things like this (which can start to get very
<Service connect>
class DBI
constructor connect
</Service>
or this (which I don't actually think would work since the tag name
tag names can be "strict" or not-validated based on the constructor
call to Config::ApacheFormat.

In addition, there is an evaluate vars constructor parameter which
allows for evluation of defined vars.

I guess my question is: if there are Factories for creating $dbh, then
can you use one of these in IOC fashion? Here are the CPAN $dbh
factories:

# DBIx::DBH - the latest on CPAN. by me. uses perl hashrefs
http://cpan.uwinnipeg.ca/htdocs/DBIx-DBH/DBIx/DBH.html

# DBIx::Password - the oldest on CPAN, by Brian Aker. He refused my
patches to support Alzabo-style and DBIx:AnyDBD style DBI datbase
connections, so I had to write DBIx::Connect
http://cpan.uwinnipeg.ca/htdocs/DBIx-Password/DBIx/Password.html

# Config::DBI - Config::ApacheFormat based. written by me.
http://search.cpan.org/~tbone/Config-DBI-1.8/lib/Config/DBI.pm

# DBIx::Connect - AppConfig based. written by me.
http://search.cpan.org/~tbone/DBIx-Connect-1.13/lib/DBIx/Connect.pm

# Ima::DBI - the most widely used one because it is part of
Class::DBI. Originally by Mike Schwern, now maintained by T. Bowden.
http://cpan.uwinnipeg.ca/search?query=Ima%3A%3ADBI&mode=dist

--
Stevan Little
2004-12-28 17:16:11 UTC
Permalink
Terrance,
Post by Terrence Brannon
and so I developed Config::DBI based on Config::ApacheFormat
http://search.cpan.org/~tbone/Config-DBI-1.8/lib/Config/DBI.pm
I see where you are passing the code ref in the example, but it seems
to be all on one line, which is not good for me as sometimes
IOC::Service sub-refs can get quite large.

Is the single line a restriction? Or do you end up having to use \
characters to span multiple lines (which is just nasty).
Post by Terrence Brannon
More recently, I realized that it was wrong of me to expect database
connection information to be structured in a certain
way. Organizations think about their database connection info in
different ways and they have their own preferenecs for which concfig
tool to use... your Registry and Container syntax is living proof of
that. A flexible database connection module should allow you to pass
your data to it but not put restrictions on how your data was
cataloged in your personal tech stack.
IOC actually aims to be not provide any more structure than the
Registry->Container->Service hierarchy, and allow you to structure your
database info in any way you like from there. My example is just how we
do it here (actually it's simplified from how we actually do it, but
why bother you all with the details), but it is by no means how I would
expect others to do it, nor is it the only way you can do it with IOC.
Post by Terrence Brannon
I realized that underlying all config representation was
a nested Perl data structure (isn't it great how hashes, arrays and
scalars are standard in Perl so that we dont have confusion over which
array package to use).
Amen to that!!
Post by Terrence Brannon
Post by Stevan Little
</Container>
</Registry>
I want to avoid doing things like this (which can start to get very
<Service connect>
class DBI
constructor connect
</Service>
or this (which I don't actually think would work since the tag name
tag names can be "strict" or not-validated based on the constructor
call to Config::ApacheFormat.
In addition, there is an evaluate vars constructor parameter which
allows for evluation of defined vars.
Nice, I did not see that in the docs.
Post by Terrence Brannon
I guess my question is: if there are Factories for creating $dbh, then
can you use one of these in IOC fashion? Here are the CPAN $dbh
You can use any of these with IOC (I assume). IOC's main goal is to
manage the configuration information and (if needed) the components
lifecycle. IOC does somewhat conflict with modules which have their own
complex, but for the most part they are still useable with it.

- Steve
Terrence Brannon
2004-12-29 01:47:30 UTC
Permalink
Post by Stevan Little
Terrance,
Post by Terrence Brannon
and so I developed Config::DBI based on Config::ApacheFormat
http://search.cpan.org/~tbone/Config-DBI-1.8/lib/Config/DBI.pm
I see where you are passing the code ref in the example, but it seems
to be all on one line, which is not good for me as sometimes
IOC::Service sub-refs can get quite large.
Is the single line a restriction? Or do you end up having to use \
characters to span multiple lines (which is just nasty).
I think so. But why not name your subrefs and put them in a module and
refer to them in a single clean line of config code?
--
Carter's Compass: I know I'm on the right track when,
by deleting something, I'm adding functionality.
Stevan Little
2004-12-29 15:36:08 UTC
Permalink
Post by Terrence Brannon
I think so. But why not name your subrefs and put them in a module and
refer to them in a single clean line of config code?
Hmmmm, Thats a good thought.

Steve
Rob Kinyon
2004-12-29 16:18:24 UTC
Permalink
I initially thought of this as well, but thinking through it further
leads to some questions:
1) Are you really going to have such re-usable components?
2) If you do, why aren't they in some sort of child class that you can specify?
3) If you cannot put them in some child class, then all you have is a
junk drawer of items that are used in one place in some config file
that need unique names. That's a real smell to me ...

Rob


On Wed, 29 Dec 2004 10:36:08 -0500, Stevan Little
Post by Stevan Little
Post by Terrence Brannon
I think so. But why not name your subrefs and put them in a module and
refer to them in a single clean line of config code?
Hmmmm, Thats a good thought.
Steve
_______________________________________________
sw-design mailing list
http://metaperl.com/cgi-bin/mailman/listinfo/sw-design
Stevan Little
2004-12-29 16:27:06 UTC
Permalink
Post by Rob Kinyon
I initially thought of this as well, but thinking through it further
1) Are you really going to have such re-usable components?
sometimes.
Post by Rob Kinyon
2) If you do, why aren't they in some sort of child class that you can specify?
True, and a better solution. This of course means that I need to be
able to allow for subclasses in the config file then as well (which is
a good thing).
Post by Rob Kinyon
3) If you cannot put them in some child class, then all you have is a
junk drawer of items that are used in one place in some config file
that need unique names. That's a real smell to me ...
*sniff*,... *sniff*,... yeah I think smell something too.

I agree, Junk drawer modules are bad.

Hmmmm.

I think I am going to look at Config::ApacheFormat and see if I can't
hack in multi-line support. That will answer my questions in the end
anyway.

Steve
Post by Rob Kinyon
Rob
On Wed, 29 Dec 2004 10:36:08 -0500, Stevan Little
Post by Stevan Little
Post by Terrence Brannon
I think so. But why not name your subrefs and put them in a module and
refer to them in a single clean line of config code?
Hmmmm, Thats a good thought.
Steve
_______________________________________________
sw-design mailing list
http://metaperl.com/cgi-bin/mailman/listinfo/sw-design
_______________________________________________
sw-design mailing list
http://metaperl.com/cgi-bin/mailman/listinfo/sw-design
Rob Kinyon
2004-12-29 18:07:38 UTC
Permalink
On Wed, 29 Dec 2004 11:27:06 -0500, Stevan Little
Post by Stevan Little
Post by Rob Kinyon
I initially thought of this as well, but thinking through it further
1) Are you really going to have such re-usable components?
sometimes.
Post by Rob Kinyon
2) If you do, why aren't they in some sort of child class that you can specify?
True, and a better solution. This of course means that I need to be
able to allow for subclasses in the config file then as well (which is
a good thing).
Post by Rob Kinyon
3) If you cannot put them in some child class, then all you have is a
junk drawer of items that are used in one place in some config file
that need unique names. That's a real smell to me ...
*sniff*,... *sniff*,... yeah I think smell something too.
I agree, Junk drawer modules are bad.
Hmmmm.
I think I am going to look at Config::ApacheFormat and see if I can't
hack in multi-line support. That will answer my questions in the end
anyway.
You're going to want to look at doing something like heredocs. Maybe,
it would look something like:

<Foo foo1>
Directive
multiline
text
here
EndDirective

SomeOtherDirective With Some Values
</Foo>

That way, you can make it so that it's easy to parse, both for
computers and for humans.

Note from the Apache documentation:
Apache configuration files contain one directive per line. The
back-slash "\" may be used as the last character on a line to
indicate that the directive continues onto the next line. There must
be no other characters or white space between the back-slash and the
end of the line.

This means you'll want to look at the while-loop labelled LINE:,
especially the do-while loop with the comment "# accumulate a full
line, dealing with line-continuation". Adding a new directive called
"multi_line" would probably be best, along the same lines as
hash_directives or duplicate_directives, where you specify which
directives have this multi_line property set for them. That way,
people who don't want it don't get it, it's backwards-compatible, and
it's self-documenting as to which directives break the Apache standard
expectations.

Rob
Post by Stevan Little
Steve
Post by Rob Kinyon
Rob
On Wed, 29 Dec 2004 10:36:08 -0500, Stevan Little
Post by Stevan Little
Post by Terrence Brannon
I think so. But why not name your subrefs and put them in a module and
refer to them in a single clean line of config code?
Hmmmm, Thats a good thought.
Steve
_______________________________________________
sw-design mailing list
http://metaperl.com/cgi-bin/mailman/listinfo/sw-design
_______________________________________________
sw-design mailing list
http://metaperl.com/cgi-bin/mailman/listinfo/sw-design
_______________________________________________
sw-design mailing list
http://metaperl.com/cgi-bin/mailman/listinfo/sw-design
Stevan Little
2004-12-29 18:31:56 UTC
Permalink
Rob,
Post by Rob Kinyon
Post by Stevan Little
I think I am going to look at Config::ApacheFormat and see if I can't
hack in multi-line support. That will answer my questions in the end
anyway.
You're going to want to look at doing something like heredocs. Maybe,
<Foo foo1>
Directive
multiline
text
here
EndDirective
SomeOtherDirective With Some Values
</Foo>
Actually I was thinking they would work best as <blocks> and not
directives. This would allow for more free-form text inside the block
and not force the introduction of an 'End<directive name>' token.
Post by Rob Kinyon
This means you'll want to look at the while-loop labelled LINE:,
especially the do-while loop with the comment "# accumulate a full
line, dealing with line-continuation". Adding a new directive called
"multi_line" would probably be best, along the same lines as
hash_directives or duplicate_directives, where you specify which
directives have this multi_line property set for them. That way,
people who don't want it don't get it, it's backwards-compatible, and
it's self-documenting as to which directives break the Apache standard
expectations.
I already have something hacked together and working with <blocks>
right now, and I have introduced a 'valid_multi_line_blocks' option to
turn it on for specific blocks only. And this:

<Registry MyApp>
<Container DBI>
dsn dbi:mysql:test
username test
password ****
<Service connect>
my $c = shift;
DBI->connect($c->get('dsn'), $c->get('username'),
$c->get('password'));
</Service>
</Container>
</Registry>

is parsing succusfully.

I am actually going to email Sam to see if he is interested in it
before I start cleaning up the code.

Steve
Post by Rob Kinyon
Rob
Post by Stevan Little
Steve
Post by Rob Kinyon
Rob
On Wed, 29 Dec 2004 10:36:08 -0500, Stevan Little
Post by Stevan Little
Post by Terrence Brannon
I think so. But why not name your subrefs and put them in a module and
refer to them in a single clean line of config code?
Hmmmm, Thats a good thought.
Steve
_______________________________________________
sw-design mailing list
http://metaperl.com/cgi-bin/mailman/listinfo/sw-design
_______________________________________________
sw-design mailing list
http://metaperl.com/cgi-bin/mailman/listinfo/sw-design
_______________________________________________
sw-design mailing list
http://metaperl.com/cgi-bin/mailman/listinfo/sw-design
_______________________________________________
sw-design mailing list
http://metaperl.com/cgi-bin/mailman/listinfo/sw-design
Rob Kinyon
2004-12-28 17:22:46 UTC
Permalink
Post by Stevan Little
<Registry MyApp>
<Container DBI>
dsn dbi:mysql:test
username test
password ****
</Container>
</Registry>
<Registry MyApp>
<Container DBI>
Literal dsn dbi:mysql:test
Literal username test
Literal password ****
<ConstructorInjection connect>
Class DBI
Constructor connect
Parameter dsn
Parameter username
Parameter password
</ConstructorInjection>
</Container>
</Registry>

Or, you can have the Literal items be containers. It's up to you.

The important things to note:
1) You can have multiple items on the same line
2) You can aggregate multiple lines into the same option
3) You can have hash options, so that the first item is the key to the
rest of the line
4) Structure your config file the way you structure your code - it'll
make more sense to you.

Rob
Stevan Little
2004-12-28 17:29:25 UTC
Permalink
Very Nice Rob!

I knew it was a good idea to ask the list before I started writing any
code.

The only thing still is multi-line code entries, I would like to be
able to do this:

<Registry MyApp>
<Container DBI>
Literal dsn dbi:mysql:test
Literal username test
Literal password ****
<Service connect>
my $c = shift;
return DBI->connect($c->get('dsn'), $c->get('username'),
$c->get('password'));
</Service>
</Container>
</Registry>
Post by Stevan Little
<Registry MyApp>
<Container DBI>
Literal dsn dbi:mysql:test
Literal username test
Literal password ****
<ConstructorInjection connect>
Class DBI
Constructor connect
Parameter dsn
Parameter username
Parameter password
</ConstructorInjection>
</Container>
</Registry>
Or, you can have the Literal items be containers. It's up to you.
1) You can have multiple items on the same line
2) You can aggregate multiple lines into the same option
3) You can have hash options, so that the first item is the key to the
rest of the line
4) Structure your config file the way you structure your code - it'll
make more sense to you.
Rob
_______________________________________________
sw-design mailing list
http://metaperl.com/cgi-bin/mailman/listinfo/sw-design
Rob Kinyon
2004-12-28 17:39:59 UTC
Permalink
Uhh ... like this?

<Service connect>
Code my $c = shift;
Code return DBI->connect( ... );
</Service>

By marking Code as a "combine", you will end up with all the code
within that block in the same scalar that you can eval later.

But, I personally wouldn't have code in your config files. Instead, I
would do what I suggested earlier and build the code from basic
concepts. *shrugs* If you have code in the config file, that ends up
providing a security hole - you have to secure not just your code from
prying hands, but your config files as well. While config parameters
are sensitive, someone could change the code and you'd execute it
without knowing. That code could do something like email your database
to someone else. Changing parameter values could never do that.

Rob

On Tue, 28 Dec 2004 12:29:25 -0500, Stevan Little
Post by Stevan Little
Very Nice Rob!
I knew it was a good idea to ask the list before I started writing any
code.
The only thing still is multi-line code entries, I would like to be
<Registry MyApp>
<Container DBI>
Literal dsn dbi:mysql:test
Literal username test
Literal password ****
<Service connect>
my $c = shift;
return DBI->connect($c->get('dsn'), $c->get('username'),
$c->get('password'));
</Service>
</Container>
</Registry>
Post by Stevan Little
<Registry MyApp>
<Container DBI>
Literal dsn dbi:mysql:test
Literal username test
Literal password ****
<ConstructorInjection connect>
Class DBI
Constructor connect
Parameter dsn
Parameter username
Parameter password
</ConstructorInjection>
</Container>
</Registry>
Or, you can have the Literal items be containers. It's up to you.
1) You can have multiple items on the same line
2) You can aggregate multiple lines into the same option
3) You can have hash options, so that the first item is the key to the
rest of the line
4) Structure your config file the way you structure your code - it'll
make more sense to you.
Rob
_______________________________________________
sw-design mailing list
http://metaperl.com/cgi-bin/mailman/listinfo/sw-design
_______________________________________________
sw-design mailing list
http://metaperl.com/cgi-bin/mailman/listinfo/sw-design
Stevan Little
2004-12-28 17:50:03 UTC
Permalink
Post by Rob Kinyon
Uhh ... like this?
<Service connect>
Code my $c = shift;
Code return DBI->connect( ... );
</Service>
Yuk
Post by Rob Kinyon
But, I personally wouldn't have code in your config files. Instead, I
would do what I suggested earlier and build the code from basic
concepts.
Well part of the problem is that IOC::Service requires a code-ref. The
ConstructorInjection and SetterInjection ones do not, but you can't
always fit something into one of those. IOC::Service provides a nice
bridge to do both ConstructorInjection and SetterInjection at the same
time, plus any other weirdness your object might want/need in order to
be fully configured when you get it from the IOC::Container.
Post by Rob Kinyon
*shrugs* If you have code in the config file, that ends up
providing a security hole - you have to secure not just your code from
prying hands, but your config files as well.
I would argue that you _should_ secure your config file as much as
possible, if not more than you secure your code. Your config file will
usually have the database connection parameters as well as username and
password. That type of data should be protected more than just
DBI->connect($config{dsn}....) stuff IMO.
Post by Rob Kinyon
While config parameters
are sensitive, someone could change the code and you'd execute it
without knowing. That code could do something like email your database
to someone else. Changing parameter values could never do that.
True, it could open up some funky exploits, but on some level you need
to put that responsibilty on the user of the code, and not on the code.
If the concern is there that sub-refs are dangerous, then you just need
to be sure that your objects can all be configured with Setter and
Constructor injection, but I don't want to force that restriction on
others.

One thought, would a Safe compartment be any help with this? I could
create an IOC::Service::Safe which would initialize the component (i.e.
- execute the sub-ref from the config) within a Safe compartment. I
have never used Safe, so I am not very familair.

Any thoughts on that?

Steve
Post by Rob Kinyon
Rob
On Tue, 28 Dec 2004 12:29:25 -0500, Stevan Little
Post by Stevan Little
Very Nice Rob!
I knew it was a good idea to ask the list before I started writing any
code.
The only thing still is multi-line code entries, I would like to be
<Registry MyApp>
<Container DBI>
Literal dsn dbi:mysql:test
Literal username test
Literal password ****
<Service connect>
my $c = shift;
return DBI->connect($c->get('dsn'), $c->get('username'),
$c->get('password'));
</Service>
</Container>
</Registry>
Post by Stevan Little
<Registry MyApp>
<Container DBI>
Literal dsn dbi:mysql:test
Literal username test
Literal password ****
<ConstructorInjection connect>
Class DBI
Constructor connect
Parameter dsn
Parameter username
Parameter password
</ConstructorInjection>
</Container>
</Registry>
Or, you can have the Literal items be containers. It's up to you.
1) You can have multiple items on the same line
2) You can aggregate multiple lines into the same option
3) You can have hash options, so that the first item is the key to the
rest of the line
4) Structure your config file the way you structure your code - it'll
make more sense to you.
Rob
_______________________________________________
sw-design mailing list
http://metaperl.com/cgi-bin/mailman/listinfo/sw-design
_______________________________________________
sw-design mailing list
http://metaperl.com/cgi-bin/mailman/listinfo/sw-design
_______________________________________________
sw-design mailing list
http://metaperl.com/cgi-bin/mailman/listinfo/sw-design
Rob Kinyon
2004-12-28 18:05:26 UTC
Permalink
On Tue, 28 Dec 2004 12:50:03 -0500, Stevan Little
Post by Stevan Little
Post by Rob Kinyon
Uhh ... like this?
<Service connect>
Code my $c = shift;
Code return DBI->connect( ... );
</Service>
Yuk
Ok ... How about:
<Service connect>
Code \
my $c = shift; \
return DBI->connect( ... );
</Service>

It looks somewhat C macro-ish, but it'll work. :-)
Post by Stevan Little
Post by Rob Kinyon
But, I personally wouldn't have code in your config files. Instead, I
would do what I suggested earlier and build the code from basic
concepts.
Well part of the problem is that IOC::Service requires a code-ref. The
ConstructorInjection and SetterInjection ones do not, but you can't
always fit something into one of those. IOC::Service provides a nice
bridge to do both ConstructorInjection and SetterInjection at the same
time, plus any other weirdness your object might want/need in order to
be fully configured when you get it from the IOC::Container.
Post by Rob Kinyon
*shrugs* If you have code in the config file, that ends up
providing a security hole - you have to secure not just your code from
prying hands, but your config files as well.
I would argue that you _should_ secure your config file as much as
possible, if not more than you secure your code. Your config file will
usually have the database connection parameters as well as username and
password. That type of data should be protected more than just
DBI->connect($config{dsn}....) stuff IMO.
Post by Rob Kinyon
While config parameters
are sensitive, someone could change the code and you'd execute it
without knowing. That code could do something like email your database
to someone else. Changing parameter values could never do that.
True, it could open up some funky exploits, but on some level you need
to put that responsibilty on the user of the code, and not on the code.
If the concern is there that sub-refs are dangerous, then you just need
to be sure that your objects can all be configured with Setter and
Constructor injection, but I don't want to force that restriction on
others.
One thought, would a Safe compartment be any help with this? I could
create an IOC::Service::Safe which would initialize the component (i.e.
- execute the sub-ref from the config) within a Safe compartment. I
have never used Safe, so I am not very familair.
Any thoughts on that?
Steve
Post by Rob Kinyon
Rob
On Tue, 28 Dec 2004 12:29:25 -0500, Stevan Little
Post by Stevan Little
Very Nice Rob!
I knew it was a good idea to ask the list before I started writing any
code.
The only thing still is multi-line code entries, I would like to be
<Registry MyApp>
<Container DBI>
Literal dsn dbi:mysql:test
Literal username test
Literal password ****
<Service connect>
my $c = shift;
return DBI->connect($c->get('dsn'),
$c->get('username'),
$c->get('password'));
</Service>
</Container>
</Registry>
Post by Stevan Little
<Registry MyApp>
<Container DBI>
Literal dsn dbi:mysql:test
Literal username test
Literal password ****
<ConstructorInjection connect>
Class DBI
Constructor connect
Parameter dsn
Parameter username
Parameter password
</ConstructorInjection>
</Container>
</Registry>
Or, you can have the Literal items be containers. It's up to you.
1) You can have multiple items on the same line
2) You can aggregate multiple lines into the same option
3) You can have hash options, so that the first item is the key to the
rest of the line
4) Structure your config file the way you structure your code - it'll
make more sense to you.
Rob
_______________________________________________
sw-design mailing list
http://metaperl.com/cgi-bin/mailman/listinfo/sw-design
_______________________________________________
sw-design mailing list
http://metaperl.com/cgi-bin/mailman/listinfo/sw-design
_______________________________________________
sw-design mailing list
http://metaperl.com/cgi-bin/mailman/listinfo/sw-design
_______________________________________________
sw-design mailing list
http://metaperl.com/cgi-bin/mailman/listinfo/sw-design
Stevan Little
2004-12-28 18:18:36 UTC
Permalink
Post by Rob Kinyon
On Tue, 28 Dec 2004 12:50:03 -0500, Stevan Little
Post by Stevan Little
Post by Rob Kinyon
Uhh ... like this?
<Service connect>
Code my $c = shift;
Code return DBI->connect( ... );
</Service>
Yuk
<Service connect>
Code \
my $c = shift; \
return DBI->connect( ... );
</Service>
It looks somewhat C macro-ish, but it'll work. :-)
Okay, not quite as YUK, but not as nice as:

<Service connect>
... free form code here
</Service>

I suppose I could try to subclass Config::ApacheFormat into supporting
free form multi-line text inside tags somehow, or maybe patch it (if
Sam would accept it that is).

Hmmmmm.....

Steve
Post by Rob Kinyon
Post by Stevan Little
Post by Rob Kinyon
But, I personally wouldn't have code in your config files. Instead, I
would do what I suggested earlier and build the code from basic
concepts.
Well part of the problem is that IOC::Service requires a code-ref. The
ConstructorInjection and SetterInjection ones do not, but you can't
always fit something into one of those. IOC::Service provides a nice
bridge to do both ConstructorInjection and SetterInjection at the same
time, plus any other weirdness your object might want/need in order to
be fully configured when you get it from the IOC::Container.
Post by Rob Kinyon
*shrugs* If you have code in the config file, that ends up
providing a security hole - you have to secure not just your code from
prying hands, but your config files as well.
I would argue that you _should_ secure your config file as much as
possible, if not more than you secure your code. Your config file will
usually have the database connection parameters as well as username and
password. That type of data should be protected more than just
DBI->connect($config{dsn}....) stuff IMO.
Post by Rob Kinyon
While config parameters
are sensitive, someone could change the code and you'd execute it
without knowing. That code could do something like email your database
to someone else. Changing parameter values could never do that.
True, it could open up some funky exploits, but on some level you need
to put that responsibilty on the user of the code, and not on the code.
If the concern is there that sub-refs are dangerous, then you just need
to be sure that your objects can all be configured with Setter and
Constructor injection, but I don't want to force that restriction on
others.
One thought, would a Safe compartment be any help with this? I could
create an IOC::Service::Safe which would initialize the component (i.e.
- execute the sub-ref from the config) within a Safe compartment. I
have never used Safe, so I am not very familair.
Any thoughts on that?
Steve
Post by Rob Kinyon
Rob
On Tue, 28 Dec 2004 12:29:25 -0500, Stevan Little
Post by Stevan Little
Very Nice Rob!
I knew it was a good idea to ask the list before I started writing any
code.
The only thing still is multi-line code entries, I would like to be
<Registry MyApp>
<Container DBI>
Literal dsn dbi:mysql:test
Literal username test
Literal password ****
<Service connect>
my $c = shift;
return DBI->connect($c->get('dsn'),
$c->get('username'),
$c->get('password'));
</Service>
</Container>
</Registry>
Post by Stevan Little
<Registry MyApp>
<Container DBI>
Literal dsn dbi:mysql:test
Literal username test
Literal password ****
<ConstructorInjection connect>
Class DBI
Constructor connect
Parameter dsn
Parameter username
Parameter password
</ConstructorInjection>
</Container>
</Registry>
Or, you can have the Literal items be containers. It's up to you.
1) You can have multiple items on the same line
2) You can aggregate multiple lines into the same option
3) You can have hash options, so that the first item is the key to the
rest of the line
4) Structure your config file the way you structure your code - it'll
make more sense to you.
Rob
_______________________________________________
sw-design mailing list
http://metaperl.com/cgi-bin/mailman/listinfo/sw-design
_______________________________________________
sw-design mailing list
http://metaperl.com/cgi-bin/mailman/listinfo/sw-design
_______________________________________________
sw-design mailing list
http://metaperl.com/cgi-bin/mailman/listinfo/sw-design
_______________________________________________
sw-design mailing list
http://metaperl.com/cgi-bin/mailman/listinfo/sw-design
_______________________________________________
sw-design mailing list
http://metaperl.com/cgi-bin/mailman/listinfo/sw-design
Terrence Brannon
2004-12-29 01:50:39 UTC
Permalink
Post by Stevan Little
One thought, would a Safe compartment be any help with this?
I contacted Tim Bunce about some problems I was having with Safe while
trying to get Resources (a CPAN module) updated. He said that Safe was
a failed experiment and showed no interest in fixing the problem with
Safe.
--
Carter's Compass: I know I'm on the right track when,
by deleting something, I'm adding functionality.
Rob Kinyon
2004-12-29 13:36:05 UTC
Permalink
On Wed, 29 Dec 2004 01:50:39 +0000, Terrence Brannon
Post by Terrence Brannon
Post by Stevan Little
One thought, would a Safe compartment be any help with this?
I contacted Tim Bunce about some problems I was having with Safe while
trying to get Resources (a CPAN module) updated. He said that Safe was
a failed experiment and showed no interest in fixing the problem with
Safe.
If you have documentation of this, I think it would be good to post to
Perlmonks. Safe is still being recommended on a somewhat regular basis
and, if Tim Bunce feels it's a failed experiment, that's a problem.

Rob
Matthew Simon Cavalletto
2004-12-29 16:05:40 UTC
Permalink
Post by Rob Kinyon
Tim Bunce [...] said that Safe was a failed experiment and showed no
interest in fixing the problem with Safe.
If you have documentation of this, I think it would be good to post to
Perlmonks. Safe is still being recommended on a somewhat regular basis
and, if Tim Bunce feels it's a failed experiment, that's a problem.
Seconded -- I've been using Safe in production for a while, although in
a relatively minor role, and was under the impression that it was
generally working and reasonably secure... If that's not the case, it
would be good to further advise the community of how and why.

-Simon
Matthew Simon Cavalletto
2004-12-30 22:53:46 UTC
Permalink
Post by Stevan Little
So I have been using my module IOC a lot lately, so far so good, but I
have found that writing the code to create all the containers and
services and such gets quite tedious. For instance to do a basic DBI
my $r = IOC::Registry->new('MyApp');
my $c = IOC::Container->new('DBI');
$c->register(IOC::Service::Literal->new('dsn' =>
'dbi:mysql:test'));
$c->register(IOC::Service::Literal->new('username' => 'test'));
$c->register(IOC::Service::Literal->new('password' => '****'));
$c->register(IOC::Service::ConstructorInjection->new('connection' => (
'DBI', 'connect' [
IOC::Service::ConstructorInjection->ComponentParameter('dsn'),
IOC::Service::ConstructorInjection->ComponentParameter('username'),
IOC::Service::ConstructorInjection->ComponentParameter('password')
]
)));
$r->registerContainer($c);
In addition to the Config::ApacheFormat mechanism that has been
discussed, I think there's also an argument for adding a simpler Perl
calling API.

For example, I think that the following can be unambiguously converted
to the above:

IOC::Registry->add_registration( 'MyApp' => 'DBI' => {
'dsn' => 'dbi:mysql:test',
'username' => 'test',
'password' => '****',
'connection' => [ 'DBI', 'connect', '@dsn', '@username', '@password'
],
} );

That's a lot easier to read, and one could always switch back to the
other syntax where needed... Perhaps this convenience interface belongs
in a separate "IOC::Easy" facade package, which could provide class
methods (or even exportable functions) that obscured the whole
underlying network of various kinds of IOC objects.

Lastly, it's worth noting that lightweight versions of this kind of
system have been in the Perl toolkit for years, whether in home-rolled
form or using a module like Tie::Discovery:

use Tie::Discovery;
my %config = ();
my $config = tie %config, 'Tie::Discovery';

$config->store('dsn' => 'dbi:mysql:test');
$config->store('username' => 'test');
$config->store('password' => '****');
$config->register( 'connection' => sub {
DBI->connect( @config{'dsn', 'username', 'password'} )
} );

$config{ 'connection' }->do( ... );

I can see that the IOC framework is much more powerful and extensible,
but it would be nice if the simple cases could also be as simple.

-Simon
Stevan Little
2004-12-31 04:22:07 UTC
Permalink
Post by Matthew Simon Cavalletto
In addition to the Config::ApacheFormat mechanism that has been
discussed, I think there's also an argument for adding a simpler Perl
calling API.
For example, I think that the following can be unambiguously converted
IOC::Registry->add_registration( 'MyApp' => 'DBI' => {
'dsn' => 'dbi:mysql:test',
'username' => 'test',
'password' => '****',
],
} );
That's a lot easier to read, and one could always switch back to the
other syntax where needed... Perhaps this convenience interface
belongs in a separate "IOC::Easy" facade package, which could provide
class methods (or even exportable functions) that obscured the whole
underlying network of various kinds of IOC objects.
I like this idea, and is something I have given some consideration to
in the past, but never gotten around too. My only worry though is that
the perl syntax may eventually get really messy with too many {}[]()s
everywhere if the configuration is very large and complex (which some
of my IOC configs are).

Hmmmm, this is some serious food for thought though.
Post by Matthew Simon Cavalletto
Lastly, it's worth noting that lightweight versions of this kind of
system have been in the Perl toolkit for years, whether in home-rolled
I was not aware of Tie::Discovery, it is interesting, it is like a
IOC::SuperSuperLite (written a few years before).

IOC and its ideas are not anything new though, I agree. I have a number
of home-rolled solutions, all of which eventually fell apart after
hitting some sort of complexity/size barrier and turned into spaghetti
configurations. At this point IOC is holding up pretty well, and I am
keeping my fingers crossed.
Post by Matthew Simon Cavalletto
I can see that the IOC framework is much more powerful and extensible,
but it would be nice if the simple cases could also be as simple.
I agree, simple things should be simple, although to be honest, while
IOC is made to be as lightweight as possible, it is really only
lightweight within the context of large applications. For small stuff,
it tends to dwarf the actual code, and is not worth using. So I guess
what I am getting at is that really simple stuff shouldn't use IOC, and
therefore I am assuming my base level will have a non-trivial amount of
complexity to it.

I suppose (at least for my purposes) I am looking for something which
helps reduce the complexity of larger IOC configurations, rather than
makes simpler ones even simpler.

But its getting late, and I am rambling, and I don't want to seem like
I am dismissing your idea/point because that is not my intention, I
just need to get some sleep.

Thanks Simon, interesting food for thought always :)

- Steve

Loading...