| line | stmt | bran | cond | sub | pod | time | code | 
| 1 | 9 |  |  | 12 |  | 491009 | use 5.008; | 
|  | 9 |  |  |  |  | 35 |  | 
|  | 9 |  |  |  |  | 378 |  | 
| 2 | 9 |  |  | 9 |  | 52 | use strict; | 
|  | 9 |  |  |  |  | 16 |  | 
|  | 9 |  |  |  |  | 271 |  | 
| 3 | 9 |  |  | 9 |  | 58 | use warnings; | 
|  | 9 |  |  |  |  | 16 |  | 
|  | 9 |  |  |  |  | 471 |  | 
| 4 |  |  |  |  |  |  |  | 
| 5 | 9 | 50 |  | 9 |  | 598 | BEGIN { if ($] < 5.010000) { require UNIVERSAL::DOES } }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | package MooX::ObjectBuilder; | 
| 8 |  |  |  |  |  |  |  | 
| 9 |  |  |  |  |  |  | our $AUTHORITY = 'cpan:TOBYINK'; | 
| 10 |  |  |  |  |  |  | our $VERSION   = '0.002'; | 
| 11 |  |  |  |  |  |  |  | 
| 12 | 9 |  |  | 9 |  | 8580 | use B::Hooks::EndOfScope; | 
|  | 9 |  |  |  |  | 86651 |  | 
|  | 9 |  |  |  |  | 64 |  | 
| 13 | 9 |  |  | 9 |  | 9466 | use Exporter::Shiny our(@EXPORT) = qw(make_builder); | 
|  | 9 |  |  |  |  | 40381 |  | 
|  | 9 |  |  |  |  | 80 |  | 
| 14 | 9 |  |  | 9 |  | 9378 | use Lexical::Accessor; | 
|  | 9 |  |  |  |  | 112257 |  | 
|  | 9 |  |  |  |  | 139 |  | 
| 15 | 9 |  |  | 9 |  | 26923 | use MooseX::ConstructInstance -with; | 
|  | 9 |  |  |  |  | 119455 |  | 
|  | 9 |  |  |  |  | 132 |  | 
| 16 | 9 |  |  | 9 |  | 5633 | use Sub::Name qw(subname); | 
|  | 9 |  |  |  |  | 38 |  | 
|  | 9 |  |  |  |  | 1250 |  | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | sub _generate_make_builder | 
| 19 |  |  |  |  |  |  | { | 
| 20 | 11 |  |  | 11 |  | 3991 | my $me = shift; | 
| 21 | 11 |  |  |  |  | 28 | my ($name, $args, $globals) = @_; | 
| 22 |  |  |  |  |  |  |  | 
| 23 | 11 |  |  |  |  | 58 | lexical_has(accessor => \(my $storage)); | 
| 24 | 11 |  |  |  |  | 6791 | my @need_to_store; | 
| 25 |  |  |  |  |  |  |  | 
| 26 | 11 |  |  |  |  | 30 | my $caller = $globals->{into}; | 
| 27 |  |  |  |  |  |  | # around BUILD | 
| 28 |  |  |  |  |  |  | on_scope_end { | 
| 29 | 9 |  |  | 9 |  | 55 | no strict 'refs'; | 
|  | 9 |  |  |  |  | 16 |  | 
|  | 9 |  |  |  |  | 4571 |  | 
| 30 | 11 | 50 |  | 11 |  | 1999 | my $next = exists(&{"$caller\::BUILD"}) ? \&{"$caller\::BUILD"} : undef; | 
|  | 11 |  |  |  |  | 74 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 31 | 11 |  |  |  |  | 63 | *{"$caller\::BUILD"} = sub { | 
| 32 | 15 |  |  | 15 |  | 51198 | my $self = shift; | 
|  |  |  |  | 5 |  |  |  | 
| 33 | 15 |  |  |  |  | 35 | my ($params) = @_; | 
| 34 | 15 | 100 |  |  |  | 222 | $self->$storage({ map exists($params->{$_})?($_=>$params->{$_}):(), @need_to_store }); | 
| 35 | 15 | 50 |  |  |  | 544 | $self->$next(@_) if $next; | 
| 36 | 11 |  |  |  |  | 60 | }; | 
| 37 | 11 |  |  |  |  | 31 | subname("$caller\::BUILD", \&{"$caller\::BUILD"}); | 
|  | 11 |  |  |  |  | 117 |  | 
| 38 | 11 |  |  |  |  | 95 | }; | 
| 39 |  |  |  |  |  |  |  | 
| 40 |  |  |  |  |  |  | return sub { # make_builder | 
| 41 | 20 |  |  | 20 |  | 245352 | my $klass = shift; | 
| 42 | 11 |  |  |  |  | 67 | my %attrs = | 
| 43 | 2 |  |  |  |  | 14 | @_==1 && ref($_[0]) eq 'HASH'  ? %{$_[0]} : | 
| 44 | 20 | 100 | 100 |  |  | 283 | @_==1 && ref($_[0]) eq 'ARRAY' ? map(+($_=>$_), @{$_[0]}) : | 
|  |  | 100 | 66 |  |  |  |  | 
| 45 |  |  |  |  |  |  | @_; | 
| 46 | 20 |  |  |  |  | 68 | push @need_to_store, keys(%attrs); | 
| 47 |  |  |  |  |  |  | my $code = sub { | 
| 48 | 28 |  |  | 28 |  | 57243 | my $self    = shift; | 
|  |  |  |  | 5 |  |  |  | 
|  |  |  |  | 5 |  |  |  | 
|  |  |  |  | 5 |  |  |  | 
|  |  |  |  | 5 |  |  |  | 
|  |  |  |  | 23 |  |  |  | 
|  |  |  |  | 23 |  |  |  | 
|  |  |  |  | 2 |  |  |  | 
| 49 | 28 |  |  |  |  | 112 | my $storage = $self->$storage; | 
| 50 | 28 | 100 |  |  |  | 477 | my %args    = map exists($storage->{$_})?($attrs{$_}=>$storage->{$_}):(), keys(%attrs); | 
| 51 | 28 | 100 |  |  |  | 286 | my $bless   = exists($args{'__CLASS__'}) ? delete($args{'__CLASS__'}) : $klass; | 
| 52 |  |  |  |  |  |  |  | 
| 53 | 28 | 100 |  |  |  | 535 | $self->DOES('MooseX::ConstructInstance') | 
| 54 |  |  |  |  |  |  | ? $self->construct_instance($bless, \%args) | 
| 55 |  |  |  |  |  |  | : MooX::ObjectBuilder->construct_instance($bless, \%args); | 
| 56 | 20 |  |  |  |  | 117 | }; | 
| 57 | 20 | 100 |  |  |  | 131 | wantarray ? ('lazy', builder => $code) : $code; | 
| 58 |  |  |  |  |  |  | } | 
| 59 | 11 |  |  |  |  | 350 | } | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | 1; | 
| 62 |  |  |  |  |  |  |  | 
| 63 |  |  |  |  |  |  | __END__ | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | =pod | 
| 66 |  |  |  |  |  |  |  | 
| 67 |  |  |  |  |  |  | =encoding utf-8 | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  | =for stopwords Torbjørn | 
| 70 |  |  |  |  |  |  |  | 
| 71 |  |  |  |  |  |  | =head1 NAME | 
| 72 |  |  |  |  |  |  |  | 
| 73 |  |  |  |  |  |  | MooX::ObjectBuilder - lazy construction of objects from extra init args | 
| 74 |  |  |  |  |  |  |  | 
| 75 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 76 |  |  |  |  |  |  |  | 
| 77 |  |  |  |  |  |  | package Person { | 
| 78 |  |  |  |  |  |  | use Moo; | 
| 79 |  |  |  |  |  |  |  | 
| 80 |  |  |  |  |  |  | has name  => (is => "ro"); | 
| 81 |  |  |  |  |  |  | has title => (is => "ro"); | 
| 82 |  |  |  |  |  |  | } | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | package Organization { | 
| 85 |  |  |  |  |  |  | use Moo; | 
| 86 |  |  |  |  |  |  | use MooX::ObjectBuilder; | 
| 87 |  |  |  |  |  |  |  | 
| 88 |  |  |  |  |  |  | has name => (is => "ro"); | 
| 89 |  |  |  |  |  |  | has boss => ( | 
| 90 |  |  |  |  |  |  | is => make_builder( | 
| 91 |  |  |  |  |  |  | "Person" => ( | 
| 92 |  |  |  |  |  |  | boss_name   => "name", | 
| 93 |  |  |  |  |  |  | boss_title  => "title", | 
| 94 |  |  |  |  |  |  | ), | 
| 95 |  |  |  |  |  |  | ), | 
| 96 |  |  |  |  |  |  | ); | 
| 97 |  |  |  |  |  |  | } | 
| 98 |  |  |  |  |  |  |  | 
| 99 |  |  |  |  |  |  | my $org = Organization->new( | 
| 100 |  |  |  |  |  |  | name       => "Catholic Church", | 
| 101 |  |  |  |  |  |  | boss_name  => "Francis", | 
| 102 |  |  |  |  |  |  | boss_title => "Pope", | 
| 103 |  |  |  |  |  |  | ); | 
| 104 |  |  |  |  |  |  |  | 
| 105 |  |  |  |  |  |  | use Data::Dumper; | 
| 106 |  |  |  |  |  |  | print Dumper( $org->boss ); | 
| 107 |  |  |  |  |  |  |  | 
| 108 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 109 |  |  |  |  |  |  |  | 
| 110 |  |  |  |  |  |  | This module exports a function C<make_builder> which can be used to | 
| 111 |  |  |  |  |  |  | generate lazy builders suitable for L<Moo> attributes. The import | 
| 112 |  |  |  |  |  |  | procedure also performs some setup operations on the caller class | 
| 113 |  |  |  |  |  |  | necessary for C<make_builder> to work correctly. | 
| 114 |  |  |  |  |  |  |  | 
| 115 |  |  |  |  |  |  | =head2 Functions | 
| 116 |  |  |  |  |  |  |  | 
| 117 |  |  |  |  |  |  | =over | 
| 118 |  |  |  |  |  |  |  | 
| 119 |  |  |  |  |  |  | =item C<< make_builder( $class|$coderef, \%args|\@args|%args ) >> | 
| 120 |  |  |  |  |  |  |  | 
| 121 |  |  |  |  |  |  | The C<make_builder> function conceptually takes two arguments, though | 
| 122 |  |  |  |  |  |  | the second one (which is normally a hashref or arrayref) may be passed | 
| 123 |  |  |  |  |  |  | as a flattened hash. | 
| 124 |  |  |  |  |  |  |  | 
| 125 |  |  |  |  |  |  | The C<< %args >> hash is a mapping of argument names where keys are | 
| 126 |  |  |  |  |  |  | names in the "aggregating" or "container" class (i.e. "Organization" | 
| 127 |  |  |  |  |  |  | in the L</SYNOPSIS>) and values are names in the "aggregated" or | 
| 128 |  |  |  |  |  |  | "contained" class (i.e. "Person" in the L</SYNOPSIS>). | 
| 129 |  |  |  |  |  |  |  | 
| 130 |  |  |  |  |  |  | If C<< \@args >> is provided instead, this is expanded into a hash as | 
| 131 |  |  |  |  |  |  | follows: | 
| 132 |  |  |  |  |  |  |  | 
| 133 |  |  |  |  |  |  | my %args = map { $_ => $_ } @args; | 
| 134 |  |  |  |  |  |  |  | 
| 135 |  |  |  |  |  |  | The builder returned by this function will accept arguments from the | 
| 136 |  |  |  |  |  |  | aggregating class and map them into arguments for the aggregated class. | 
| 137 |  |  |  |  |  |  | The builder will then construct an instance of C<< $class >> passing | 
| 138 |  |  |  |  |  |  | it a hashref of arguments. If C<< $coderef >> has been provided instead | 
| 139 |  |  |  |  |  |  | of a class name, this will be called with the hashref of arguments | 
| 140 |  |  |  |  |  |  | instead. | 
| 141 |  |  |  |  |  |  |  | 
| 142 |  |  |  |  |  |  | The C<make_builder> function behaves differently in scalar and list | 
| 143 |  |  |  |  |  |  | context. In list context, it returns a three item list. The first two | 
| 144 |  |  |  |  |  |  | items are the strings C<< "lazy" >> and C<< "builder" >>; the third | 
| 145 |  |  |  |  |  |  | item is the builder coderef described above. In scalar context, only | 
| 146 |  |  |  |  |  |  | the coderef is returned. Thus the following two examples work | 
| 147 |  |  |  |  |  |  | equivalently: | 
| 148 |  |  |  |  |  |  |  | 
| 149 |  |  |  |  |  |  | # Scalar context | 
| 150 |  |  |  |  |  |  | my $builder = make_builder($class, {...}); | 
| 151 |  |  |  |  |  |  | has attr => ( | 
| 152 |  |  |  |  |  |  | is      => "lazy", | 
| 153 |  |  |  |  |  |  | builder => $builder, | 
| 154 |  |  |  |  |  |  | ); | 
| 155 |  |  |  |  |  |  |  | 
| 156 |  |  |  |  |  |  | # List context | 
| 157 |  |  |  |  |  |  | has attr => ( | 
| 158 |  |  |  |  |  |  | is => make_builder($class, {...}), | 
| 159 |  |  |  |  |  |  | ); | 
| 160 |  |  |  |  |  |  |  | 
| 161 |  |  |  |  |  |  | =back | 
| 162 |  |  |  |  |  |  |  | 
| 163 |  |  |  |  |  |  | =head2 Class Setup | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | On import, this module installs a sub called C<BUILD> into your class. | 
| 166 |  |  |  |  |  |  | If your class already has a sub with this name, it will be wrapped. | 
| 167 |  |  |  |  |  |  |  | 
| 168 |  |  |  |  |  |  | The point of this sub is to capture argument passed to the aggregating | 
| 169 |  |  |  |  |  |  | class' constructor, to enable them to be later forwarded to the | 
| 170 |  |  |  |  |  |  | aggregated class. | 
| 171 |  |  |  |  |  |  |  | 
| 172 |  |  |  |  |  |  | See also: L<Moo/"BUILD">. | 
| 173 |  |  |  |  |  |  |  | 
| 174 |  |  |  |  |  |  | =head2 Using MooX::ObjectBuilder with Moose and Mouse | 
| 175 |  |  |  |  |  |  |  | 
| 176 |  |  |  |  |  |  | It is possible to use C<make_builder> in scalar context with L<Moose> | 
| 177 |  |  |  |  |  |  | and L<Mouse> classes: | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | has attr => ( | 
| 180 |  |  |  |  |  |  | is      => "ro", | 
| 181 |  |  |  |  |  |  | lazy    => 1, | 
| 182 |  |  |  |  |  |  | default => scalar make_builder($class, {...}), | 
| 183 |  |  |  |  |  |  | ); | 
| 184 |  |  |  |  |  |  |  | 
| 185 |  |  |  |  |  |  | =head2 MooseX::ConstructInstance | 
| 186 |  |  |  |  |  |  |  | 
| 187 |  |  |  |  |  |  | If your object does the L<MooseX::ConstructInstance> role, then this | 
| 188 |  |  |  |  |  |  | module will automatically do the right thing and delegate to that for | 
| 189 |  |  |  |  |  |  | the actual object construction. | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | =head1 BUGS | 
| 192 |  |  |  |  |  |  |  | 
| 193 |  |  |  |  |  |  | Please report any bugs to | 
| 194 |  |  |  |  |  |  | L<http://rt.cpan.org/Dist/Display.html?Queue=MooX-ObjectBuilder>. | 
| 195 |  |  |  |  |  |  |  | 
| 196 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 197 |  |  |  |  |  |  |  | 
| 198 |  |  |  |  |  |  | L<Moo>, L<Moose>, L<Mouse>. | 
| 199 |  |  |  |  |  |  |  | 
| 200 |  |  |  |  |  |  | L<MooseX::ConstructInstance>. | 
| 201 |  |  |  |  |  |  |  | 
| 202 |  |  |  |  |  |  | L<MooX::LazyRequire>, | 
| 203 |  |  |  |  |  |  | L<MooseX::LazyRequire>, | 
| 204 |  |  |  |  |  |  | L<MooseX::LazyCoercion>, | 
| 205 |  |  |  |  |  |  | etc. | 
| 206 |  |  |  |  |  |  |  | 
| 207 |  |  |  |  |  |  | =head1 AUTHOR | 
| 208 |  |  |  |  |  |  |  | 
| 209 |  |  |  |  |  |  | Toby Inkster E<lt>tobyink@cpan.orgE<gt>. | 
| 210 |  |  |  |  |  |  |  | 
| 211 |  |  |  |  |  |  | =head1 CREDITS | 
| 212 |  |  |  |  |  |  |  | 
| 213 |  |  |  |  |  |  | Most of the test suite was written by Torbjørn Lindahl (cpan:TORBJORN). | 
| 214 |  |  |  |  |  |  |  | 
| 215 |  |  |  |  |  |  | Various advice was given by Graham Knop (cpan:HAARG) and Matt S Trout | 
| 216 |  |  |  |  |  |  | (cpan:MSTROUT). | 
| 217 |  |  |  |  |  |  |  | 
| 218 |  |  |  |  |  |  | =head1 COPYRIGHT AND LICENCE | 
| 219 |  |  |  |  |  |  |  | 
| 220 |  |  |  |  |  |  | This software is copyright (c) 2014 by Toby Inkster. | 
| 221 |  |  |  |  |  |  |  | 
| 222 |  |  |  |  |  |  | This is free software; you can redistribute it and/or modify it under | 
| 223 |  |  |  |  |  |  | the same terms as the Perl 5 programming language system itself. | 
| 224 |  |  |  |  |  |  |  | 
| 225 |  |  |  |  |  |  | =head1 DISCLAIMER OF WARRANTIES | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED | 
| 228 |  |  |  |  |  |  | WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF | 
| 229 |  |  |  |  |  |  | MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. |