From 18e0c21c891deec7847d2cb43fc4e48d85baa219 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Thu, 27 Dec 2012 04:57:50 -0600 Subject: start working on this --- lib/Bread/Board.pm | 81 +++++++++++++++++++++++++++++++++++++ t/001_constructor_injection.t | 94 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+) create mode 100644 lib/Bread/Board.pm create mode 100644 t/001_constructor_injection.t diff --git a/lib/Bread/Board.pm b/lib/Bread/Board.pm new file mode 100644 index 0000000..c3d1a26 --- /dev/null +++ b/lib/Bread/Board.pm @@ -0,0 +1,81 @@ +use v6; + +class Bread::Board::Dependency {...} + +role Bread::Board::Service { + has Str $.name; + + # 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 Hash of Hash $.parameters = {}; + has $.dependencies = {}; + has $.parameters = {}; + + # 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 -> $dep { + $deps.{$dep} = Bread::Board::Dependency.new( + service => %params..{$dep}, + ); + } + %params. = $deps; + } + nextwith(|%params); + } + + method get {*}; + + method get_dependency ($name) { + return $.dependencies.{$name}; + } +} + +class Bread::Board::Dependency { + has Bread::Board::Service $.service handles 'get'; +} + +class Bread::Board::ConstructorInjection does Bread::Board::Service { + has $.class; + has Str $.constructor_name is rw = 'new'; + + method get (*%params is copy) { + self.check_parameters(%params); + for $.dependencies.keys -> $name { + %params{$name} = $.dependencies{$name}.get; + } + return $.class."$.constructor_name"(|%params); + } + + my 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::Literal does Bread::Board::Service { + has $.value; + + method get { + return $.value; + } +} diff --git a/t/001_constructor_injection.t b/t/001_constructor_injection.t new file mode 100644 index 0000000..331deee --- /dev/null +++ b/t/001_constructor_injection.t @@ -0,0 +1,94 @@ +use v6; +use Test; + +use Bread::Board; + +sub does_ok(Mu $var, Mu $type, $msg = ("The object does '" ~ $type.perl ~ "'")) { + ok($var.does($type), $msg); +} + +class Needle { } +class Mexican::Black::Tar { } +class Addict { + has $.needle; + has $.spoon; + has $.stash; + has $.overdose; + + method shoot_up_good (Addict $class: *%args) { + $class.new(|%args, overdose => 1); + } +} + +{ + my $s = Bread::Board::ConstructorInjection.new( + name => 'William', + class => Addict, + dependencies => { + needle => Bread::Board::ConstructorInjection.new( + name => 'spike', + class => Needle, + ), + spoon => Bread::Board::Literal.new( + name => 'works', + value => 'Spoon!', + ), + }, + parameters => { + stash => { isa => Mexican::Black::Tar }, + }, + ); + + isa_ok($s, Bread::Board::ConstructorInjection); + does_ok($s, Bread::Board::Service); + + { + my $i = $s.get(stash => Mexican::Black::Tar.new); + + isa_ok($i, Addict); + isa_ok($i.needle, Needle); + is($i.spoon, 'Spoon!'); + isa_ok($i.stash, Mexican::Black::Tar); + ok(!$i.overdose); + + my $i2 = $s.get(stash => Mexican::Black::Tar.new); + isnt($i, $i2); + } + + # $s.constructor_name('shoot_up_good'); + $s.constructor_name = 'shoot_up_good'; + + { + my $i = $s.get(stash => Mexican::Black::Tar.new); + isa_ok($i, Addict); + ok($i.overdose, 'Alternate constructor called'); + } + + is($s.name, 'William'); + is($s.class.perl, Addict.perl); + + my $deps = $s.dependencies; + is_deeply([$deps.keys.sort], [qw/needle spoon/]); + + my $needle = $s.get_dependency('needle'); + isa_ok($needle, Bread::Board::Dependency); + isa_ok($needle.service, Bread::Board::ConstructorInjection); + is($needle.service.name, 'spike'); + is($needle.service.class.perl, Needle.perl); + + my $spoon = $s.get_dependency('spoon'); + isa_ok($spoon, Bread::Board::Dependency); + isa_ok($spoon.service, Bread::Board::Literal); + is($spoon.service.name, 'works'); + is($spoon.service.value, 'Spoon!'); + + my $params = $s.parameters; + is_deeply([$params.keys.sort], [qw/stash/]); + is_deeply($params., { isa => Mexican::Black::Tar }); + + dies_ok { $s.get }; + dies_ok { $s.get(stash => []) }; + dies_ok { $s.get(stash => Mexican::Black::Tar.new, foo => 10) }; +} + +done; -- cgit v1.2.3