# --8<--8<--8<--8<--
#
# Copyright (C) 2012 Smithsonian Astrophysical Observatory
#
# This file is part of MooX-Attributes-Shadow
#
# MooX-Attributes-Shadow is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# -->8-->8-->8-->8--

package MooX::Attributes::Shadow::Role;

use strict;

our $VERSION = '0.01_05';

use Moo::Role;

use MooX::Attributes::Shadow ':all';

## no critic (ProhibitSubroutinePrototypes)
sub shadowable_attrs (@) {

    my $attrs = [ @_ ];

    ## no critic (ProhibitNoStrict)
    no strict 'refs';
    no warnings 'redefine';

    *{ caller() . '::shadowable_attrs' } = sub (@) { @{$attrs} };

    return;
}


1;


__END__

=head1 NAME

MooX::Attributes::Shadow::Role - enumerate shadowable attributes in a contained class

=head1 SYNOPSIS

  # in the contained class
  package Foo;
  use Moo;
  with 'MooX::Attributes::Shadow::Role';

  shadowable_attrs 'x', 'y';

  has x => ( is => 'ro' );
  has y => ( is => 'ro' );


  # in the container class

  package Bar;

  use Moo;
  use Foo;

  # create accessors with a prefix to avoid collisions; no need to
  # specify which ones
  Foo->shadow_attrs( fmt => sub { 'pfx_' . shift } );

  # create an attribute which holds the contained oject, and
  # delegate the shadowed accessors to it.
  has foo   => ( is => 'ro',
                 lazy => 1,
                 default => sub { Foo->new( Foo->xtract_attrs(  shift ) ) },
                 handles => Foo->shadowed_attrs,
               );


=head1 DESCRIPTION

B<MooX::Attributes::Shadow::Role> provides a means for a class to
identify attributes which should be shadowed to classes which contain
it.  (See B<MooX::Attributes::Shadow> for more on what this means).
A class containing a class composed with this role need know nothing about
the attributes which will be shadowed, and can use the class methods to
integrate the shadowable attributes into their interface.


=head1 INTERFACE

=head2 Contained class functions

=over

=item B<shadowable_attrs>

  shadowable_attrs @attrs;

This is called by the contained class to identify which attributes are
available for shadowing.  It does B<not> create them, it merely
records them.

Subsequent calls will return the names of the attributes which may be
shadowed as an array:

   Class->shadowable_attrs( qw[ a b c ] );
   @attrs = Class->shadowable_attrs;


=back


=head2 Class methods for use by the Container Classes

=over

=item B<shadow_attrs>

   ContainedClass->shadow_attrs( %options );

This method creates read-only attributes shadowing the
I<ContainedClass>'s shadowable attributes in the class which calls it.

It takes the following options:

=over

=item fmt

This is a reference to a subroutine which should return a modified
attribute name (e.g. to prevent attribute collisions).  It is passed
the attribute name as its first parameter.

=item instance

In the case where more than one instance of an object is contained,
this (string) is used to identify an individual instance.

=item private

If true, the actual attribute name is mangled; the attribute
initialization name is left untouched (see the C<init_arg> option to
the B<Moo> C<has> subroutine).  This defaults to true.

=back

=item B<shadowed_attrs>

  $attrs = ContainedClass->( [$container,] \%options );

Return a hash of attributes shadowed into C<$container>.  If
C<$container> is provided it may be either a class name or an
object. If it is not provided, the package name of the calling routine
is used.

It takes the following options:

=over

=item instance

In the case where more than one instance of an object is contained,
this (string) is used to identify an individual instance.

=back

The keys in the returned hash are the attribute initialization names
(not the mangled ones) in the I<container> class; the hash values are
the attribute names in the I<contained> class.  This makes it easy to
delegate accessors to the contained class:

  has foo   => ( is => 'ro',
                 lazy => 1,
                 default => sub { Foo->new( Foo->xtract_attrs( shift ) ) },
                 handles => Foo->shadowed_attrs,
               );

=item B<xtract_attrs>

  %attrs = ContainedClass->xtract_attrs( $container_obj, \%options );

After the container class is instantiated, B<xtract_attrs> is used to
extract attributes for the contained object from the container object.

It takes the following options:

=over

=item instance

In the case where more than one instance of an object is contained,
this (string) is used to identify an individual instance.

=back

=back

=head1 COPYRIGHT & LICENSE

Copyright 2012 Smithsonian Astrophysical Observatory

This software is released under the GNU General Public License.  You
may find a copy at

   http://www.fsf.org/copyleft/gpl.html


=head1 AUTHOR

Diab Jerius E<lt>djerius@cfa.harvard.eduE<gt>