Skip Menu |

Preferred bug tracker

Please visit the preferred bug tracker to report your issue.

This queue is for tickets about the Bread-Board CPAN distribution.

Report information
The Basics
Id: 64478
Status: resolved
Priority: 0/
Queue: Bread-Board

People
Owner: stevan.little [...] gmail.com
Requestors: hanenkamp [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: Normal
Broken in: 0.15
Fixed in: (no value)



Subject: depends_on('../foo') causes Deep recursion
I don't think this is a bug, but I do find it counter-intuitive. Consider the following Bread::Board: my $c = container 'Star_Wars' => as { service han_solo => 'Shot First'; container Mos_Eisley => as { service han_solo => 'Shot Back'; service cantina => ( block => sub { my $s = shift; return { han_solo => $s->param('han_solo'), }; }, dependencies => { han_solo => depends_on('../../../han_solo'), }, ); }; }; my $cantina = $c->resolve( service => 'Mos_Eisley/cantina' ); say "Han Solo $cantina->{han_solo}"; This will print the correct string, "Han Solo Shot First" (which we all know is true). However, the line: han_solo => depends_on('../../../han_solo'), seems counter-intuitive. I would expect: han_solo => depends_on('han_solo'), to give the wrong answer "Han Solo Shot Back" and it does. I would expect: han_solo => depends_on('../han_solo'), to do what I got with ../../../han_solo, but instead we crash altogether: Deep recursion on subroutine "Bread::Board::Dependency::is_locked" at .../lib/site_perl/5.12.1/darwin-2level/Moose/Meta/Method/Delegation.pm line 108. I don't expect ../../han_solo to do anything correctly at all, but: han_solo => depends_on('../../han_solo'), Actually yields "Han Solo Shot Back" which is so wrong and also the same as depends_on('han_solo') which does seem right. Now, I understand why each of these is happening, because the dependency object gets the service itself as it's parent. So ../han_solo refers to the dependency, so it is referring back to itself, which wreaks havoc. This also means that we have to do .. to traverse to the cantina service, .. to get to the Mos_Eisley container, and .. to get to the Star_wars container to finally get to a place we can get to the correct han_solo. This does not match up at all with my expectation since: depends_on('han_solo') === depends_on('../../han_solo') I did not expect to be dependencies to be traversed this way. If at all traversable, I would expect them to be available depends_on('cantina/han_solo') so that the notion would match up with usual directory traversal (where I've added the notion of "."): depends_on('.') === depends_on('/Mos_Eisley') depends_on('..') === depends_on('/') depends_on('../han_solo') === depends_on('/han_solo') depends_on('./han_solo') === depends_on('/Mos_Eisley/han_solo') === depends_on('han_solo') depends_on('./cantina') === depends_on('/Mos_Eisley/cantina') === depends_on('cantina') depends_on('./cantina/han_solo') === depends_on('/Mos_Eisley/cantina/han_solo') == depends_on('cantina/han_solo') Why does it work this way so that 'foo' === '../../foo" within the injector? I'm assuming, since I have lots of respect for Stevan's designs, that there are good reasons for this. However, would it be possible to get a "./" prefix or "~/" or something that could be used as a shortcut that would make up for the deficiency?
Wow, that is a thorough bug report, thanks! I will be honest, I don't think that I really have a good reason for this behavior, but I also don't currently have the time to look deeply into this. I always felt the whole ../../ stuff was kinda hackish and ugly so I tend to not use them too heavily. That said the path metaphor is an easy one for people to understand, so to continue it with ./ and ~/ might not be a bad idea and i would be open to a patch to support that. All this code is contained in Bread::Board::Traversable and should be fairly easy to isolate and extend. I will try and carve out some time later this week to look into this more. Thanks, - Stevan On Tue Jan 04 18:16:00 2011, HANENKAMP wrote: Show quoted text
> I don't think this is a bug, but I do find it counter-intuitive. > Consider the following > Bread::Board: > > my $c = container 'Star_Wars' => as { > service han_solo => 'Shot First'; > > container Mos_Eisley => as { > service han_solo => 'Shot Back'; > service cantina => ( > block => sub { > my $s = shift; > return { > han_solo => $s->param('han_solo'), > }; > }, > dependencies => { > han_solo => depends_on('../../../han_solo'), > }, > ); > }; > }; > > my $cantina = $c->resolve( service => 'Mos_Eisley/cantina' ); > say "Han Solo $cantina->{han_solo}"; > > This will print the correct string, "Han Solo Shot First" (which we > all know is true). However, > the line: > > han_solo => depends_on('../../../han_solo'), > > seems counter-intuitive. > > I would expect: > > han_solo => depends_on('han_solo'), > > to give the wrong answer "Han Solo Shot Back" and it does. > > I would expect: > > han_solo => depends_on('../han_solo'), > > to do what I got with ../../../han_solo, but instead we crash > altogether: > > Deep recursion on subroutine "Bread::Board::Dependency::is_locked" at > .../lib/site_perl/5.12.1/darwin-2level/Moose/Meta/Method/Delegation.pm > line 108. > > I don't expect ../../han_solo to do anything correctly at all, but: > > han_solo => depends_on('../../han_solo'), > > Actually yields "Han Solo Shot Back" which is so wrong and also the > same as > depends_on('han_solo') which does seem right. > > Now, I understand why each of these is happening, because the > dependency object gets the > service itself as it's parent. So ../han_solo refers to the > dependency, so it is referring back to > itself, which wreaks havoc. This also means that we have to do .. to > traverse to the cantina > service, .. to get to the Mos_Eisley container, and .. to get to the > Star_wars container to finally > get to a place we can get to the correct han_solo. > > This does not match up at all with my expectation since: > > depends_on('han_solo') === depends_on('../../han_solo') > > I did not expect to be dependencies to be traversed this way. If at > all traversable, I would > expect them to be available depends_on('cantina/han_solo') so that the > notion would match > up with usual directory traversal (where I've added the notion of > "."): > > depends_on('.') === depends_on('/Mos_Eisley') > depends_on('..') === depends_on('/') > depends_on('../han_solo') === depends_on('/han_solo') > depends_on('./han_solo') === depends_on('/Mos_Eisley/han_solo') === > depends_on('han_solo') > depends_on('./cantina') === depends_on('/Mos_Eisley/cantina') === > depends_on('cantina') > depends_on('./cantina/han_solo') === > depends_on('/Mos_Eisley/cantina/han_solo') == > depends_on('cantina/han_solo') > > Why does it work this way so that 'foo' === '../../foo" within the > injector? > > I'm assuming, since I have lots of respect for Stevan's designs, that > there are good reasons for > this. However, would it be possible to get a "./" prefix or "~/" or > something that could be > used as a shortcut that would make up for the deficiency?
Understood. After more thought, I'm wondering if adding another notation might not be the wrong solution. Adding another esoteric notation to fix the deficiency in an already somewhat hacky notation is probably the wrong thing. Instead, what if there was a way to alias containers. container star_wars => as { container a_new_hope => as { container millenium_falcon => as { service hyperdrive => 'works'; }; service death_star_attack_fleet => ( block => sub { ... }, dependencies => wire_names(qw( millenium_falcon ... )), ); }; container empire_strikes_back => as { container millenium_falcon => as { service hyperdrive => 'broken'; }; }; container return_of_the_jedi => as { alias millenium_falcon => '/a_new_hope/millenium_falcon'; service death_star_attack_fleet => ( block => sub { ... }, dependencies => wire_names(qw( millenium_falcon ... )), ); }; }; That might more easily solve my worry that someday I'm going to shuffle the container hierarchy, which is the real source of my desire for a useful "..".
I had pondered some kind of alias before, but it never seemed to make sense and always felt like to many level of indirection. It really would only be useful if it was extremely lazy. Additionally, your example would still fail if you refactored like this: container 'lucasfilm' => as { container 'star_wars' => as { .... }; container 'indiana_jones' => as { ... }; }; The alias would no longer work since star-wars was not at the root of it. I actually released a new version of Bread::Board the other day and was looking over the traversal code some. I see where the confusion you originally had is coming from, which was exactly as you diagnosed it. I also realised that while i think heavy use of the ../../ notation can feel hackish, the path notation itself is not. And the idea of a ~ notation, which simply means "the container I am in" is pretty simple and really unambiguous. I think being able to navigate in a such a relative fashion would actually make the ../../ stuff less mucky too. And in some ways I wonder if the hackish feeling of ../../ is not simply because it doesn't actually implement all the various path notations that we are comfortable with (like ~). So, to re-use your example (which btw is broken cause you can't depend on a container, but sokay I get the idea you're trying to convey), it would become: container star_wars => as { container a_new_hope => as { container millenium_falcon => as { service hyperdrive => 'works'; }; service death_star_attack_fleet => ( block => sub { ... }, dependencies => wire_names(qw( millenium_falcon ... )), ); }; container empire_strikes_back => as { container millenium_falcon => as { service hyperdrive => 'broken'; }; }; container return_of_the_jedi => as { service death_star_attack_fleet => ( block => sub { ... }, dependencies => { millenium_falcon => '~/../a_new_hope/millenium_falcon } ); }; }; So in this case you are saying, take me to the return_of_the_jedi container, then navigate up one, and descend into the a_new_hope container. This approach is, IMO, safer for refactoring, however that is probably biased by the fact I tend to wrap my containers often and usually don't want the subcontainers to know that they are being wrapped. Thoughts? - Stevan On Tue Jan 11 16:41:17 2011, HANENKAMP wrote: Show quoted text
> Understood. After more thought, I'm wondering if adding another > notation might not be the > wrong solution. Adding another esoteric notation to fix the deficiency > in an already somewhat > hacky notation is probably the wrong thing. > > Instead, what if there was a way to alias containers. > > container star_wars => as { > container a_new_hope => as { > container millenium_falcon => as { > service hyperdrive => 'works'; > }; > > service death_star_attack_fleet => ( > block => sub { ... }, > dependencies => wire_names(qw( millenium_falcon ... )), > ); > }; > container empire_strikes_back => as { > container millenium_falcon => as { > service hyperdrive => 'broken'; > }; > }; > container return_of_the_jedi => as { > alias millenium_falcon => '/a_new_hope/millenium_falcon'; > > service death_star_attack_fleet => ( > block => sub { ... }, > dependencies => wire_names(qw( millenium_falcon ... )), > ); > }; > }; > > That might more easily solve my worry that someday I'm going to > shuffle the container > hierarchy, which is the real source of my desire for a useful "..".
If you have a moment I would appreciate if you could test out the latest Bread::Board in github https://github.com/stevan/BreadBoard In particular this fix: https://github.com/stevan/BreadBoard/commit/86dbb5268ec5d1d9c0be1ca58a5b1a6159ba 4996 After much discussion with Jesse (doy) we decided that sanity should prevail and that the whole ../../ business really had not usage and was just laziness in the design. The change really ends up being quite small, but should fix the issues you noticed. Let me know, I wont release quite yet, I need to see how much (if anything) it breaks for me first. Thanks, - Stevan On Tue Jan 11 17:49:06 2011, STEVAN wrote: Show quoted text
> I had pondered some kind of alias before, but it never seemed to make > sense and always felt > like to many level of indirection. It really would only be useful if > it was extremely lazy. > > Additionally, your example would still fail if you refactored like > this: > > container 'lucasfilm' => as { > container 'star_wars' => as { .... }; > container 'indiana_jones' => as { ... }; > }; > > The alias would no longer work since star-wars was not at the root of > it. > > I actually released a new version of Bread::Board the other day and > was looking over the > traversal code some. I see where the confusion you originally had is > coming from, which was > exactly as you diagnosed it. > > I also realised that while i think heavy use of the ../../ notation > can feel hackish, the path > notation itself is not. And the idea of a ~ notation, which simply > means "the container I am in" > is pretty simple and really unambiguous. I think being able to > navigate in a such a relative > fashion would actually make the ../../ stuff less mucky too. And in > some ways I wonder if the > hackish feeling of ../../ is not simply because it doesn't actually > implement all the various path > notations that we are comfortable with (like ~). > > So, to re-use your example (which btw is broken cause you can't depend > on a container, but > sokay I get the idea you're trying to convey), it would become: > > container star_wars => as { > container a_new_hope => as { > container millenium_falcon => as { > service hyperdrive => 'works'; > }; > > service death_star_attack_fleet => ( > block => sub { ... }, > dependencies => wire_names(qw( millenium_falcon ... )), > ); > }; > container empire_strikes_back => as { > container millenium_falcon => as { > service hyperdrive => 'broken'; > }; > }; > container return_of_the_jedi => as { > service death_star_attack_fleet => ( > block => sub { ... }, > dependencies => { > millenium_falcon => '~/../a_new_hope/millenium_falcon > } > ); > }; > }; > > So in this case you are saying, take me to the return_of_the_jedi > container, then navigate up > one, and descend into the a_new_hope container. This approach is, IMO, > safer for refactoring, > however that is probably biased by the fact I tend to wrap my > containers often and usually don't > want the subcontainers to know that they are being wrapped. > > Thoughts? > > - Stevan > > > On Tue Jan 11 16:41:17 2011, HANENKAMP wrote:
> > Understood. After more thought, I'm wondering if adding another > > notation might not be the > > wrong solution. Adding another esoteric notation to fix the
> deficiency
> > in an already somewhat > > hacky notation is probably the wrong thing. > > > > Instead, what if there was a way to alias containers. > > > > container star_wars => as { > > container a_new_hope => as { > > container millenium_falcon => as { > > service hyperdrive => 'works'; > > }; > > > > service death_star_attack_fleet => ( > > block => sub { ... }, > > dependencies => wire_names(qw( millenium_falcon ... )), > > ); > > }; > > container empire_strikes_back => as { > > container millenium_falcon => as { > > service hyperdrive => 'broken'; > > }; > > }; > > container return_of_the_jedi => as { > > alias millenium_falcon => '/a_new_hope/millenium_falcon'; > > > > service death_star_attack_fleet => ( > > block => sub { ... }, > > dependencies => wire_names(qw( millenium_falcon ... )), > > ); > > }; > > }; > > > > That might more easily solve my worry that someday I'm going to > > shuffle the container > > hierarchy, which is the real source of my desire for a useful "..".
> >
This fix has been released for a while, and seems to work well, so I think I'll close this. Feel free to reopen it if there are still any issues.