use v6; class Bread::Board::Container {...} class Bread::Board::Dependency {...} role Bread::Board::Service { has Str $.name; has Bread::Board::Container $.parent is rw = Bread::Board::Container; # XXX not sure how to make these optional - specifying the types here # makes it fail when the parameters aren't passed # shouldn't the " = {}" part be taking care of that? # has Hash of Bread::Board::Dependency $.dependencies = {}; has $.dependencies = {}; # XXX overriding new here is an extremely suboptimal solution # does perl 6 have anything like moose's coercions? method new (*%params is copy) { if %params. { my $deps = {}; for %params..keys -> $name { my $dep = %params..{$name}; $deps.{$name} = $dep.isa(Bread::Board::Dependency) ?? $dep !! Bread::Board::Dependency.new(service => $dep); } %params. = $deps; } nextwith(|%params); } method get {*}; method get_dependency ($name) { return $.dependencies.{$name}; } } role Bread::Board::HasParameters { # XXX not sure how to make these optional - specifying the types here # makes it fail when the parameters aren't passed # shouldn't the " = {}" part be taking care of that? # has Hash of Hash $.parameters = {}; has $.parameters = {}; method check_parameters (%params) { for $.parameters.keys -> $name { if not %params.{$name}:exists { die "Required parameter $name not given"; } } for %params.keys -> $name { if not $.parameters.{$name}:exists { die "Unknown parameter $name given"; } if not %params.{$name}.isa($.parameters.{$name}.) { die "{%params.{$name}.perl} is not a valid value for the $name parameter"; } } # XXX why is this return necessary? return; } } class Bread::Board::Dependency { has Bread::Board::Service $.service handles 'get'; } class Bread::Board::ConstructorInjection does Bread::Board::Service does Bread::Board::HasParameters { has $.class; has Str $.constructor_name is rw = 'new'; method get (*%params is copy) { # XXX remove more duplication? self.check_parameters(%params); for $.dependencies.keys -> $name { %params{$name} = $.dependencies{$name}.get; } return $.class."$.constructor_name"(|%params); } } class Bread::Board::Parameters { has Hash $.params; # XXX do we really want to keep this API? has $.class; method param (Str $name) { return $.params.{$name}; } } class Bread::Board::BlockInjection does Bread::Board::Service does Bread::Board::HasParameters { has Callable $.block; has $.class = Any; method get (*%params is copy) { # XXX remove more duplication? self.check_parameters(%params); for $.dependencies.keys -> $name { %params{$name} = $.dependencies{$name}.get; } return $.block.( Bread::Board::Parameters.new( params => %params, class => $.class, ) ); } } class Bread::Board::Literal does Bread::Board::Service { has $.value; method get { return $.value; } } class Bread::Board::Container { has Str $.name; has Bread::Board::Container $.parent is rw = Bread::Board::Container; # XXX again, as above # has Hash of Bread::Board::Container $.sub_containers = {}; # has Hash of Bread::Board::Service $.services = {}; has $.sub_containers = {}; has $.services = {}; # XXX again, as above method new (*%params is copy) { if %params..isa(Array) { %params. = %params..map(-> $c { $c.name => $c }).hash; } if %params..isa(Array) { %params. = %params..map(-> $c { $c.name => $c }).hash; } my $container = callwith(|%params); if %params.:exists { for %params..values -> $c { $c.parent = $container; } } if %params.:exists { for %params..values -> $c { $c.parent = $container; } } return $container; } method add_sub_container (Bread::Board::Container $c) { $.sub_containers.{$c.name} = $c; $c.parent = self; } method get_sub_container (Str $name) { return $.sub_containers.{$name}; } method has_services { return $.services > 0; } method get_service (Str $name) { return $.services.{$name}; } }