package Submodules::Result;
use File::Spec;
use strict;
use warnings;
use Carp;
use overload (
	fallback	=> 1,
	'""'		=> 'SCALAR',
);
our $AUTOLOAD;
our $VERSION = '1.0000';
our @CARP_NOT = qw(Submodules);
our $default_property = 'Module';
our $SCALAR = sub {
	my $self = shift;
	my @call = caller(1);
	return $self if $call[0] eq __PACKAGE__;
	eval {
		$self->isa('UNIVERSAL');
	};
	return $self if $@;
	$self->{$default_property};
};

sub SCALAR {
	# Easily oerride stringification through $Submodules::SCALAR
	return &$SCALAR(@_) if 'CODE' eq ref $SCALAR;
}

sub new {
	my $class = shift;
	bless {
		AbsPath	=> undef,
		Clobber	=> undef,
		Module	=> undef,
		Name	=> undef,
		Path	=> undef,
		RelPath	=> undef,
		@_,
	}, $class;
}

sub require {
	my $self = shift;
	my @call = caller(0);
	my $r = eval {
		require "$self->{Path}";
	};
	if (my $e = $@) {
		if ($e =~ /did not return a true value/) {
			die $self->Path." did not return a true value at $call[1] line $call[2]\n";
		} else {
			$e =~ s/(Compilation failed in require at) .*?$/$1 $call[1] line $call[2]/g;
			die $e;
		}
	}
	$r;
}

sub use {
	my $self = shift;
	$self->require;
	if (my $import = $self->Module->can('import')) {
		unshift @_, $self->Module;
		goto &$import;
	}
}

sub read {
	my $self = shift;
	undef local $/;
	open my $in, '<', $self->AbsPath or croak "Failed to open $self->{AbsPath} for reading: $!";
	binmode $in;
	my $data = <$in>;
	close $in;
	$data;
}

sub AUTOLOAD : lvalue {
	(my $name = $AUTOLOAD) =~ s/^.+:://;
	my $self = shift;
	if (exists $self->{$name}) {
		$self->{$name};
	} else {
		eval {
			$self->{$default_property}->$name;
		};
		croak "Unknown method or property '$name': $@" if $@;
	}
}

1;

__END__
=pod

=head1 NAME

Submodules::Result - Efficient way to load or handle results from L<Submodules>.

=head1 VERSION

Version 1.0000

=head1 SYNOPSIS

This is the object returned by L<Submodules/find>. It has several methods that makes it
easy to handle the tasks that are more commonly needed for a module. It's automagically
stringified to the name of the module (in B<Module::Name> format), but even inside string
interpolation, you can access its properties as you would with a hashref:

    package MyModule;
    use Submodules;
    for my $i (Submodules->find) {
        $i->use;  # Equivalent to 'use Some::Module'
        $i->require; # Equivalent to 'require Some::Module'
        
        print "I found $i";
        # Will print: I found Some::Module
        
        print "The path is $i->{RelPath}";
        # Will print something like Some/Module.pm
        
        print "The absolute path is $i->{AbsPath}";
        # Will print something like /usr/local/lib64/perl5/lib/Some/Module.pm
        
        print "The name of the module is $i->{Module}";
        # Will print The name of the module is Some::Module
    }

=head1 PROPERTIES

All properties can be called as methods too. For example:

    package MyModule;
    use Submodules;
    for my $i (Submodules->find) {
        # Used as method
        next if $i->Clobber;
        
        # Used as property (hash element)
        print "The value of Clobber is $i->{Clobber}";
    }

=head2 Module

This property correponds to the complete name of the module in the format of B<Some::Module>.

=head2 Name

This property refers to the last part of the name of the module. For example, B<Some::Module>
would be only B<Module>.

=head2 Path

This property contains a path exactly like the one that Perl stores internaly in its symbol table.
This means that, for B<Some::Module>, it will be B<Some/Module.pm>, using forward slashes independently
of the current operating system.

=head2 AbsPath

Corresponds to the absolute path where the module can be found. Depending on your system, the format
can be different. For example, on Windows you'll get back-slashes on the path. This behavior comes
directly from L<File::Spec>.

=head2 RelPath

Correspods to the relative path to the module. Similar to L<AbsPath> except for being relative to
the location of the current execution.

=head2 Clobber

This property indicates that a module cannot (or should not) be visible by commands like B<use> or
B<require>. It generally means that another module with the same name is the one being read and
is masking this one. One example of this would be a core module that came with Perl by default,
but was later upgraded and installed into the site/lib section. It's value is the absolute path
to the first module that is directly masking it.

=head1 METHODS

As mentioned in L<PROPERTIES>, all properties can be called as methods too. Besides that, there are
a few more useful methods:

=head2 read

This will read and return the contents of the module, as plain text. No code is parsed or executed.

=head2 require

This acts just like Perl's C<require>, meaning that the module in question will be read, evaluated
(executed) and the last statement will be returned, which in case of it not being a true value it
will die. Nothing gets imported.

=head2 use

This acts just like Perl's C<use>, meaning that it will do the same as in L<require>, but it will
also call C<< ->use >> into the current namespace. For some modules and features (like prototypes),
this should be called inside a C<BEGIN> block to force its execution at compile time.

=head2 new

This is the constructor and exists mainly for internal purposes. It requires all of its properties
to be passed as arguments and that's something that L<Submodules> does by itself. There should
be no reason to use this method directly.

Read the documentation for L<Submodules> to learn about its own methods.

=head1 SEE ALSO

L<Submodules> for more detail on its own methods.

=head1 AUTHOR

Francisco Zarabozo, C<< <zarabozo at cpan.org> >>

=head1 BUGS

Please report any bugs or feature requests to C<bug-submodules at rt.cpan.org>, or through
the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Submodules>.  I will be notified, and then you'll
automatically be notified of progress on your bug as I make changes.

=head1 SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc Submodules::Result

You can also look for information at:

=over 4

=item * RT: CPAN's request tracker (report bugs here)

L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Submodules>

=item * AnnoCPAN: Annotated CPAN documentation

L<http://annocpan.org/dist/Submodules>

=item * CPAN Ratings

L<http://cpanratings.perl.org/d/Submodules>

=item * Search CPAN

L<http://search.cpan.org/dist/Submodules/>

=back

=head1 LICENSE AND COPYRIGHT

Copyright 2015 Francisco Zarabozo.

This program is free software; you can redistribute it and/or modify it
under the terms of either: the GNU General Public License as published
by the Free Software Foundation; or the Artistic License.

See L<http://dev.perl.org/licenses/> for more information.

=cut
