switch to my pluginbundle, weaving pod and authors moved to contributors
[gitmo/MooseX-ConfigFromFile.git] / lib / MooseX / ConfigFromFile.pm
CommitLineData
c35b639e 1package MooseX::ConfigFromFile;
23fca8d5 2# ABSTRACT: An abstract Moose role for setting attributes from a configfile
c35b639e 3
4use Moose::Role;
23fca8d5 5use MooseX::Types::Path::Tiny 0.005 'Path';
28d69363 6use MooseX::Types::Moose 'Undef';
7use Try::Tiny;
870afbfa 8use Carp qw(croak);
bc5ab785 9use namespace::autoclean;
10
c35b639e 11requires 'get_config_from_file';
12
13has configfile => (
14 is => 'ro',
28d69363 15 isa => Path|Undef,
c35b639e 16 coerce => 1,
17 predicate => 'has_configfile',
742b859c 18 do { try { require MooseX::Getopt; (traits => ['Getopt']) } },
28d69363 19 lazy => 1,
20 # it sucks that we have to do this rather than using a builder, but some old code
21 # simply swaps in a new default sub into the attr definition
eb27f7e7 22 default => sub {
23 my $class = shift;
24 $class->_get_default_configfile if $class->can('_get_default_configfile');
25 },
c35b639e 26);
27
28sub new_with_config {
29 my ($class, %opts) = @_;
30
fc44be6f 31 my $configfile;
32
33 if(defined $opts{configfile}) {
34 $configfile = $opts{configfile}
35 }
36 else {
7acf2fe9 37 # This would only succeed if the consumer had defined a new configfile
28d69363 38 # sub to override the generated reader - as suggested in old
74e7e627 39 # documentation -- or if $class is an instance not a class name
f1040206 40 $configfile = try { $class->configfile };
7acf2fe9 41
42 # this is gross, but since a lot of users have swapped in their own
43 # default subs, we have to keep calling it rather than calling a
44 # builder sub directly - and it might not even be a coderef either
45 my $cfmeta = $class->meta->find_attribute_by_name('configfile');
a665bd30 46 $configfile = $cfmeta->default if not defined $configfile and $cfmeta->has_default;
7acf2fe9 47
56e4351b 48 if (ref $configfile eq 'CODE') {
83f0ce54 49 $configfile = $configfile->($class);
56e4351b 50 }
8b4a3e76 51
52 my $init_arg = $cfmeta->init_arg;
53 $opts{$init_arg} = $configfile if defined $configfile and defined $init_arg;
fc44be6f 54 }
55
0e88ec88 56 if (defined $configfile) {
870afbfa 57 my $hash = $class->get_config_from_file($configfile);
58
59 no warnings 'uninitialized';
60 croak "get_config_from_file($configfile) did not return a hash (got $hash)"
61 unless ref $hash eq 'HASH';
62
63 %opts = (%$hash, %opts);
c35b639e 64 }
fc44be6f 65
c35b639e 66 $class->new(%opts);
67}
68
69no Moose::Role; 1;
70
71__END__
72
73=pod
74
c35b639e 75=head1 SYNOPSIS
76
77 ########
78 ## A real role based on this abstract role:
79 ########
80
81 package MooseX::SomeSpecificConfigRole;
82 use Moose::Role;
2b11fcd0 83
c35b639e 84 with 'MooseX::ConfigFromFile';
2b11fcd0 85
c35b639e 86 use Some::ConfigFile::Loader ();
87
88 sub get_config_from_file {
89 my ($class, $file) = @_;
90
91 my $options_hashref = Some::ConfigFile::Loader->load($file);
92
93 return $options_hashref;
94 }
95
96
97 ########
98 ## A class that uses it:
99 ########
100 package Foo;
101 use Moose;
102 with 'MooseX::SomeSpecificConfigRole';
103
fc44be6f 104 # optionally, default the configfile:
28d69363 105 sub _get_default_configfile { '/tmp/foo.yaml' }
fc44be6f 106
c35b639e 107 # ... insert your stuff here ...
108
109 ########
110 ## A script that uses the class with a configfile
111 ########
112
113 my $obj = Foo->new_with_config(configfile => '/etc/foo.yaml', other_opt => 'foo');
114
115=head1 DESCRIPTION
116
2b11fcd0 117This is an abstract role which provides an alternate constructor for creating
c35b639e 118objects using parameters passed in from a configuration file. The
119actual implementation of reading the configuration file is left to
491a1083 120concrete sub-roles.
c35b639e 121
122It declares an attribute C<configfile> and a class method C<new_with_config>,
123and requires that concrete roles derived from it implement the class method
124C<get_config_from_file>.
125
23fca8d5 126=for stopwords configfile
127
491a1083 128Attributes specified directly as arguments to C<new_with_config> supersede those
fc44be6f 129in the configfile.
130
c35b639e 131L<MooseX::Getopt> knows about this abstract role, and will use it if available
491a1083 132to load attributes from the file specified by the command line flag C<--configfile>
c35b639e 133during its normal C<new_with_options>.
134
135=head1 Attributes
136
137=head2 configfile
138
491a1083 139This is a L<Path::Tiny> object which can be coerced from a regular path
2f049fc1 140string or any object that supports stringification.
141This is the file your attributes are loaded from. You can add a default
28d69363 142configfile in the consuming class and it will be honored at the appropriate
143time; see below at L</_get_default_configfile>.
4d0a4f55 144
742b859c 145If you have L<MooseX::Getopt> installed, this attribute will also have the
146C<Getopt> trait supplied, so you can also set the configfile from the
147command line.
148
c35b639e 149=head1 Class Methods
150
151=head2 new_with_config
152
153This is an alternate constructor, which knows to look for the C<configfile> option
154in its arguments and use that to set attributes. It is much like L<MooseX::Getopts>'s
155C<new_with_options>. Example:
156
157 my $foo = SomeClass->new_with_config(configfile => '/etc/foo.yaml');
158
491a1083 159Explicit arguments will override anything set by the configfile.
c35b639e 160
161=head2 get_config_from_file
162
491a1083 163This class method is not implemented in this role, but it is required of all
164classes or roles that consume this role.
165Its two arguments are the class name and the configfile, and it is expected to return
fc44be6f 166a hashref of arguments to pass to C<new()> which are sourced from the configfile.
c35b639e 167
28d69363 168=head2 _get_default_configfile
169
eb27f7e7 170This class method is not implemented in this role, but can and should be defined
171in a consuming class or role to return the default value of the configfile (if not
28d69363 172passed into the constructor explicitly).
173
c35b639e 174=cut