1 package MooseX::ConfigFromFile;
2 # ABSTRACT: An abstract Moose role for setting attributes from a configfile
5 use MooseX::Types::Path::Tiny 0.005 'Path';
6 use MooseX::Types::Moose 'Undef';
9 use namespace::autoclean;
11 requires 'get_config_from_file';
17 predicate => 'has_configfile',
18 do { try { require MooseX::Getopt; (traits => ['Getopt']) } },
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
24 $class->_get_default_configfile if $class->can('_get_default_configfile');
29 my ($class, %opts) = @_;
33 if(defined $opts{configfile}) {
34 $configfile = $opts{configfile}
37 # This would only succeed if the consumer had defined a new configfile
38 # sub to override the generated reader - as suggested in old
39 # documentation -- or if $class is an instance not a class name
40 $configfile = try { $class->configfile };
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');
46 $configfile = $cfmeta->default if not defined $configfile and $cfmeta->has_default;
48 if (ref $configfile eq 'CODE') {
49 $configfile = $configfile->($class);
52 my $init_arg = $cfmeta->init_arg;
53 $opts{$init_arg} = $configfile if defined $configfile and defined $init_arg;
56 if (defined $configfile) {
57 my $hash = $class->get_config_from_file($configfile);
59 no warnings 'uninitialized';
60 croak "get_config_from_file($configfile) did not return a hash (got $hash)"
61 unless ref $hash eq 'HASH';
63 %opts = (%$hash, %opts);
78 ## A real role based on this abstract role:
81 package MooseX::SomeSpecificConfigRole;
84 with 'MooseX::ConfigFromFile';
86 use Some::ConfigFile::Loader ();
88 sub get_config_from_file {
89 my ($class, $file) = @_;
91 my $options_hashref = Some::ConfigFile::Loader->load($file);
93 return $options_hashref;
98 ## A class that uses it:
102 with 'MooseX::SomeSpecificConfigRole';
104 # optionally, default the configfile:
105 sub _get_default_configfile { '/tmp/foo.yaml' }
107 # ... insert your stuff here ...
110 ## A script that uses the class with a configfile
113 my $obj = Foo->new_with_config(configfile => '/etc/foo.yaml', other_opt => 'foo');
117 This is an abstract role which provides an alternate constructor for creating
118 objects using parameters passed in from a configuration file. The
119 actual implementation of reading the configuration file is left to
122 It declares an attribute C<configfile> and a class method C<new_with_config>,
123 and requires that concrete roles derived from it implement the class method
124 C<get_config_from_file>.
126 =for stopwords configfile
128 Attributes specified directly as arguments to C<new_with_config> supersede those
131 L<MooseX::Getopt> knows about this abstract role, and will use it if available
132 to load attributes from the file specified by the command line flag C<--configfile>
133 during its normal C<new_with_options>.
139 This is a L<Path::Tiny> object which can be coerced from a regular path
140 string or any object that supports stringification.
141 This is the file your attributes are loaded from. You can add a default
142 configfile in the consuming class and it will be honored at the appropriate
143 time; see below at L</_get_default_configfile>.
145 If you have L<MooseX::Getopt> installed, this attribute will also have the
146 C<Getopt> trait supplied, so you can also set the configfile from the
151 =head2 new_with_config
153 This is an alternate constructor, which knows to look for the C<configfile> option
154 in its arguments and use that to set attributes. It is much like L<MooseX::Getopts>'s
155 C<new_with_options>. Example:
157 my $foo = SomeClass->new_with_config(configfile => '/etc/foo.yaml');
159 Explicit arguments will override anything set by the configfile.
161 =head2 get_config_from_file
163 This class method is not implemented in this role, but it is required of all
164 classes or roles that consume this role.
165 Its two arguments are the class name and the configfile, and it is expected to return
166 a hashref of arguments to pass to C<new()> which are sourced from the configfile.
168 =head2 _get_default_configfile
170 This class method is not implemented in this role, but can and should be defined
171 in a consuming class or role to return the default value of the configfile (if not
172 passed into the constructor explicitly).