The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
Changes 06
MANIFEST 12
META.json 22
META.yml 22
lib/MooX/ConfigFromFile/Role.pm 823
lib/MooX/ConfigFromFile.pm 570
t/01-moo.t 012
t/01-parse.t 490
t/02-moose.t 381
t/testlib.pm 086
10 files changed (This is a version diff) 105204
@@ -1,5 +1,11 @@
 Revision history for MooX-ConfigFromFile
 
+0.004	2014-10-28
+    - add ability for options to importer of MooX::ConfigFromFile
+    - add option to have a singleton config (loaded once).
+    - improve documentation
+    - harmonize tests
+
 0.003	2014-08-02
     - fix links in documentation to fix RT#97429 (thanks abraxxa for
       reporting)
@@ -7,7 +7,8 @@ MANIFEST
 MANIFEST.SKIP
 README.md
 t/00-load.t
-t/01-parse.t
+t/01-moo.t
 t/02-moose.t
+t/testlib.pm
 META.yml                                 Module YAML meta-data (added by MakeMaker)
 META.json                                Module JSON meta-data (added by MakeMaker)
@@ -4,7 +4,7 @@
       "Jens Rehsack <rehsack@cpan.org>"
    ],
    "dynamic_config" : 1,
-   "generated_by" : "ExtUtils::MakeMaker version 6.98, CPAN::Meta::Converter version 2.141520",
+   "generated_by" : "ExtUtils::MakeMaker version 6.98, CPAN::Meta::Converter version 2.142060",
    "license" : [
       "perl_5"
    ],
@@ -65,5 +65,5 @@
          "http://dev.perl.org/licenses/"
       ]
    },
-   "version" : "0.003"
+   "version" : "0.004"
 }
@@ -11,7 +11,7 @@ build_requires:
 configure_requires:
   ExtUtils::MakeMaker: '0'
 dynamic_config: 1
-generated_by: 'ExtUtils::MakeMaker version 6.98, CPAN::Meta::Converter version 2.141520'
+generated_by: 'ExtUtils::MakeMaker version 6.98, CPAN::Meta::Converter version 2.142060'
 license: perl
 meta-spec:
   url: http://module-build.sourceforge.net/META-spec-v1.4.html
@@ -30,4 +30,4 @@ requires:
   perl: v5.8.1
 resources:
   license: http://dev.perl.org/licenses/
-version: '0.003'
+version: '0.004'
@@ -3,7 +3,7 @@ package MooX::ConfigFromFile::Role;
 use strict;
 use warnings;
 
-our $VERSION = '0.003';
+our $VERSION = '0.004';
 
 use Moo::Role;
 
@@ -41,17 +41,22 @@ has 'config_prefix' => ( is => 'lazy' );
 
 sub _build_config_prefix { $Script; }
 
+has 'config_extensions' => ( is => 'lazy' );
+
+sub _build_config_extensions { [ Config::Any->extensions() ] }
+
 has 'config_files' => ( is => 'lazy' );
 
 sub _build_config_files
 {
     my ( $class, $params ) = @_;
 
-    defined $params->{config_prefix} or $params->{config_prefix} = $class->_build_config_prefix($params);
-    defined $params->{config_dirs}   or $params->{config_dirs}   = $class->_build_config_dirs($params);
+    defined $params->{config_prefix}     or $params->{config_prefix}     = $class->_build_config_prefix($params);
+    defined $params->{config_dirs}       or $params->{config_dirs}       = $class->_build_config_dirs($params);
+    defined $params->{config_extensions} or $params->{config_extensions} = $class->_build_config_extensions($params);
 
     ref $params->{config_dirs} eq "ARRAY" or $params->{config_dirs} = ["."];
-    my @cfg_pattern = map { $params->{config_prefix} . "." . $_ } Config::Any->extensions();
+    my @cfg_pattern = map { $params->{config_prefix} . "." . $_ } @{ $params->{config_extensions} };
     my @cfg_files = File::Find::Rule->file()->name(@cfg_pattern)->maxdepth(1)->in( @{ $params->{config_dirs} } );
 
     return \@cfg_files;
@@ -103,6 +108,13 @@ already in C<$params>.
 This role uses following attributes which might be suitable customized by
 overloading the appropriate builder or pass defaults in construction arguments.
 
+Be sure to read L<MooX::File::ConfigDir/ATTRIBUTES>, especially
+L<MooX::File::ConfigDir/config_identifier> to understand how the L</config_dirs>
+are build.
+
+When you miss a directory - see L<File::ConfigDir/plug_dir_source> and
+L<File::ConfigDir::Plack>.
+
 =head2 config_prefix
 
 This attribute defaults to L<FindBin>'s C<$Script>. It's interpreted as the
@@ -110,14 +122,17 @@ basename of the config file name to use.
 
 =head2 config_dirs
 
-This attribute is included from L<MooX::File::ConfigDir|MooX::File::ConfigDir/config_dirs>.
-It might be unclever to override - but possible. Use with caution.
+This attribute is consumed from L<MooX::File::ConfigDir|MooX::File::ConfigDir/config_dirs>.
+It might not be smart to override - but possible. Use with caution.
+
+=head2 config_extensions
+
+This attribute defaults to list of extensions from L<Config::Any|Config::Any/extensions>.
 
 =head2 config_files
 
 This attribute contains the list of existing files in I<config_dirs> matching
-I<config_prefix> . L<Config::Any-E<gt>extensions|Config::Any/extensions>.
-Search is operated by L<File::Find::Rule>.
+I<config_prefix> . I<config_extensions>.  Search is operated by L<File::Find::Rule>.
 
 =head1 AUTHOR
 
@@ -3,11 +3,13 @@ package MooX::ConfigFromFile;
 use strict;
 use warnings FATAL => 'all';
 
-our $VERSION = '0.003';
+our $VERSION = '0.004';
+
+my %loaded_configs;
 
 sub import
 {
-    my ( undef, @import ) = @_;
+    my ( undef, %import_options ) = @_;
     my $target = caller;
     my @target_isa;
     { no strict 'refs'; @target_isa = @{"${target}::ISA"} };
@@ -24,6 +26,35 @@ sub import
     };
     $apply_modifiers->();
 
+    my $around;
+    defined $import_options{config_singleton} and $import_options{config_singleton} and do
+    {
+        $around or $around = $target->can('around');
+        $around->(
+            _build_loaded_config => sub {
+                my $orig  = shift;
+                my $self  = shift;
+                my $class = ref $self ? ref $self : $self;
+                defined $loaded_configs{$class} or $loaded_configs{$class} = $self->$orig(@_);
+                return $loaded_configs{$class};
+            }
+        );
+    };
+
+    my %default_modifiers = (
+        config_prefix     => '_build_config_prefix',
+        config_extensions => '_build_config_extensions',
+        config_dirs       => '_build_config_dirs',
+        config_files      => '_build_config_files',
+    );
+
+    foreach my $opt_key ( keys %default_modifiers )
+    {
+        exists $import_options{$opt_key} or next;
+        $around or $around = $target->can('around');
+        $around->( $default_modifiers{$opt_key} => sub { $import_options{$opt_key} } );
+    }
+
     return;
 }
 
@@ -68,7 +99,8 @@ MooX::ConfigFromFile - Moo eXtension for initializing objects from config file
 
    use Moo;
 
-   use MooX::ConfigFromFile; # imports the MooX::ConfigFromFile::Role
+   # consumes the MooX::ConfigFromFile::Role but load config only once
+   use MooX::ConfigFromFile config_singleton => 1;
 
    with "Role::Action";
 
@@ -93,6 +125,41 @@ on object construction from an appropriate config file. The building is
 done in L<MooX::ConfigFromFile::Role> - using MooX::ConfigFromFile ensures
 the role is applied.
 
+For easier usage, with 0.004, several options can be passed via I<use> resulting
+in default initializers for appropriate role attributes:
+
+=over 4
+
+=item C<config_prefix>
+
+Default for L<MooX::ConfigFromFile::Role/config_prefix>.
+
+=item C<config_extensions>
+
+Default for L<MooX::ConfigFromFile::Role/config_extensions>.
+
+=item C<config_dirs>
+
+Default for L<MooX::ConfigFromFile::Role/config_dirs>.
+Same warning regarding modifying this attribute applies here:
+Possible, but use with caution!
+
+=item C<config_files>
+
+Default for L<MooX::ConfigFromFile::Role/config_files>.
+
+Reasonable when you want exactly one config file in development mode.
+For production code it is highly recommended to override the builder.
+
+=item C<config_singleton>
+
+Flag adding a wrapper L<< around|Class::Method::Modifiers/around method(s) => sub { ... }; >>
+the I<builder> of L<MooX::ConfigFromFile::Role/loaded_config> to ensure a
+config is loaded only once per class. The I<per class> restriction results
+from applicable modifiers per class (and singletons are per class).
+
+=back
+
 =head1 AUTHOR
 
 Jens Rehsack, C<< <rehsack at cpan.org> >>
@@ -133,10 +200,8 @@ L<http://search.cpan.org/dist/MooX-ConfigFromFile/>
 
 =back
 
-
 =head1 ACKNOWLEDGEMENTS
 
-
 =head1 LICENSE AND COPYRIGHT
 
 Copyright 2013-2014 Jens Rehsack.
@@ -0,0 +1,12 @@
+#!perl
+
+use 5.008003;
+
+use strict;
+use warnings FATAL => 'all';
+
+use Test::More;
+
+do "t/testlib.pm";
+
+done_testing;
@@ -1,49 +0,0 @@
-#!perl
-
-use 5.008003;
-
-use strict;
-use warnings FATAL => 'all';
-
-use Test::More;
-
-my $dist_basedir =
-  Cwd::abs_path( File::Spec->catdir( File::Basename::dirname($0), File::Spec->updir() ) );
-
-{
-    package #
-	Calc::Role::BinaryOperation;
-    use Moo::Role;
-    
-    has a => (
-        is             => 'ro',
-        required       => 1,
-    );
-    
-    has b => (
-        is             => 'ro',
-        required       => 1,
-    );
-}
-
-
-{
-    package #
-        Calc::add;
-    use Moo;
-    use MooX::ConfigFromFile;
-    
-    with 'Calc::Role::BinaryOperation';
-    
-    sub execute {
-        my $self = shift;
-        return $self->a + $self->b;
-    }
-}
-
-my $adder = Calc::add->new( config_prefix => 'calc-operands' );
-ok(defined($adder->a), "read 'a' from config");
-ok(defined($adder->b), "read 'b' from config");
-cmp_ok($adder->execute, "==", 5, "read right adder config");
-
-done_testing;
@@ -12,43 +12,6 @@ BEGIN {
     $@ and plan skip_all => "Moose test requires Moose being installed";
 }
 
-my $dist_basedir =
-  Cwd::abs_path( File::Spec->catdir( File::Basename::dirname($0), File::Spec->updir() ) );
-
-{
-    package #
-	Calc::Role::BinaryOperation;
-    use Moose::Role;
-    
-    has a => (
-        is             => 'ro',
-        required       => 1,
-    );
-    
-    has b => (
-        is             => 'ro',
-        required       => 1,
-    );
-}
-
-
-{
-    package #
-        Calc::add;
-    use Moose;
-    use MooX::ConfigFromFile;
-    
-    with 'Calc::Role::BinaryOperation';
-    
-    sub execute {
-        my $self = shift;
-        return $self->a + $self->b;
-    }
-}
-
-my $adder = Calc::add->new( config_prefix => 'calc-operands' );
-ok(defined($adder->a), "read 'a' from config");
-ok(defined($adder->b), "read 'b' from config");
-cmp_ok($adder->execute, "==", 5, "read right adder config");
+do "t/testlib.pm";
 
 done_testing;
@@ -0,0 +1,86 @@
+use strict;
+use warnings FATAL => 'all';
+
+{
+    package    #
+      Calc::Role::BinaryOperation;
+    use Moo::Role;
+
+    has a => (
+        is       => 'ro',
+        required => 1,
+    );
+
+    has b => (
+        is       => 'ro',
+        required => 1,
+    );
+}
+
+{
+    package    #
+      Calc::add;
+    use Moo;
+    use MooX::ConfigFromFile;
+
+    with 'Calc::Role::BinaryOperation';
+
+    sub execute
+    {
+        my $self = shift;
+        return $self->a + $self->b;
+    }
+}
+
+{
+    package    #
+      Calc::sub;
+    use Moo;
+    use MooX::ConfigFromFile config_prefix => 'calc-operands';
+
+    with 'Calc::Role::BinaryOperation';
+
+    sub execute
+    {
+        my $self = shift;
+        return $self->a - $self->b;
+    }
+}
+
+{
+    package    #
+      Calc::mul;
+    use Moo;
+    use MooX::ConfigFromFile
+      config_prefix    => 'calc-operands',
+      config_singleton => 1;
+
+    with 'Calc::Role::BinaryOperation';
+
+    sub execute
+    {
+        my $self = shift;
+        return $self->a * $self->b;
+    }
+}
+
+my $adder = Calc::add->new( config_prefix => 'calc-operands' );
+ok( defined( $adder->a ), "read 'a' from add config" );
+ok( defined( $adder->b ), "read 'b' from add config" );
+cmp_ok( $adder->execute, "==", 5, "read right adder config" );
+
+my $subber = Calc::sub->new;
+ok( defined( $subber->a ), "read 'a' from sub config" );
+ok( defined( $subber->b ), "read 'b' from sub config" );
+cmp_ok( $subber->execute, "==", -1, "read right subber config" );
+
+my $mul1 = Calc::mul->new;
+ok( defined( $mul1->a ), "read 'a' from mul1 config" );
+ok( defined( $mul1->b ), "read 'b' from mul1 config" );
+cmp_ok( $mul1->execute, "==", 6, "read right mul config" );
+
+my $mul2 = Calc::mul->new( config_prefix => 'no-calc-operands' );
+ok( defined( $mul2->a ), "copy 'a' from mul1 config" );
+ok( defined( $mul2->b ), "copy 'b' from mul1 config" );
+cmp_ok( $mul2->execute, "==", 6, "right mul1 config duplicated" );
+