Config-GitLike
view release on metacpan or search on metacpan
lib/Config/GitLike.pm view on Meta::CPAN
use Moo;
use MooX::Types::MooseLike::Base qw(Bool HashRef ArrayRef Maybe Str Int);
use File::Spec;
use Cwd;
use Scalar::Util qw(openhandle);
use Fcntl qw(O_CREAT O_EXCL O_WRONLY);
use 5.008;
our $VERSION = '1.18';
has 'confname' => (
is => 'rw',
required => 1,
isa => Str,
);
# not defaulting to {} allows the predicate is_loaded
# to determine whether data has been loaded yet or not
has 'data' => (
is => 'rw',
predicate => 'is_loaded',
isa => HashRef,
);
# key => bool
has 'multiple' => (
is => 'rw',
isa => HashRef,
default => sub { +{} },
);
has 'casing' => (
is => 'rw',
isa => HashRef,
default => sub { +{} },
);
# filename where the definition of each key was loaded from
has 'origins' => (
is => 'rw',
isa => HashRef,
default => sub { +{} },
);
has 'config_files' => (
is => 'rw',
isa => ArrayRef,
default => sub { [] },
);
# default to being more relaxed than git, but allow enforcement
# of only-write-things-that-git-config-can-read if you want to
has 'compatible' => (
is => 'rw',
isa => Bool,
default => sub { 0 },
);
has 'cascade' => (
is => 'rw',
isa => Bool,
default => sub { 0 },
);
has 'encoding' => (
is => 'rw',
isa => Maybe[Str],
);
has 'newlines' => (
is => 'rw',
isa => HashRef,
default => sub { +{} },
);
has 'include' => (
is => 'rw',
isa => Str,
default => sub { "include.path" },
);
has 'max_depth' => (
is => 'rw',
isa => Int,
default => sub { 10 },
);
sub set_multiple {
my $self = shift;
my ($name, $mult) = (@_, 1);
$self->multiple->{ $self->canonical_case( $name ) } = $mult;
}
sub is_multiple {
my $self = shift;
my $name = shift;
return if !defined $name;
return $self->multiple->{ $self->canonical_case( $name ) };
}
sub load {
my $self = shift;
my $path = shift || Cwd::cwd;
$self->data({});
$self->multiple({});
$self->config_files([]);
$self->load_global;
$self->load_user;
$self->load_dirs( $path );
return wantarray ? %{$self->data} : \%{$self->data};
}
sub dir_file {
my $self = shift;
return "." . $self->confname;
}
sub load_dirs {
my $self = shift;
my $path = shift;
my($vol, $dirs, undef) = File::Spec->splitpath( $path, 1 );
my @dirs = File::Spec->splitdir( $dirs );
my @found;
while (@dirs) {
my $path = File::Spec->catpath(
$vol, File::Spec->catdir(@dirs), $self->dir_file
);
if (-f $path) {
push @found, $path;
last unless $self->cascade;
}
pop @dirs;
}
$self->load_file( $_ ) for reverse @found;
}
sub global_file {
my $self = shift;
return "/etc/" . $self->confname;
}
sub load_global {
my $self = shift;
return $self->load_file( $self->global_file );
}
sub user_file {
my $self = shift;
return
File::Spec->catfile( "~", "." . $self->confname );
}
sub load_user {
my $self = shift;
return $self->load_file( $self->user_file );
}
# returns undef if the file was unable to be opened
sub _read_config {
my $self = shift;
my $filename = shift;
return unless -f $filename and -r $filename;
open(my $fh, '<', $filename) or return;
if (my $encoding = $self->encoding) {
binmode $fh, ":encoding($encoding)";
}
my $c = do {local $/; <$fh>};
my $newlines = "\n";
if ($c =~ m/\r\n/) {
# Convert from DOS; `git` applies this on read always, and
# simply mangles files on write.
$newlines = "\r\n";
$c =~ s/\r\n/\n/g;
} elsif ($c !~ /\n/ and $c =~ /\r/) {
# Best-guess convert from Mac.
$newlines = "\r";
$c =~ s/\r/\n/g;
}
$self->newlines->{$filename} = $newlines;
$c =~ s/\n*$/\n/; # Ensure it ends with a newline
return $c;
}
sub load_file {
my $ref = shift;
lib/Config/GitLike.pm view on Meta::CPAN
values. To override this, pass in C<multiple =E<gt> 1>. If you want to replace
all instances of a multiple-valued key with a new value, you need to pass
in C<replace_all =E<gt> 1> as well.
=head2 group_set( $filename, $array_ref )
Same as L<"set">, but set a group of variables at the same time without
writing to disk separately for each.
C<$array_ref> contains a list of hash references which are essentially hashes
of arguments to C<set>, excluding the C<$filename> argument since that is
specified separately and the same file is used for all variables to be set at
once.
=head2 rename_section
Parameters:
from => 'name.subname'
to => 'new.subname'
filename => '/file/to/edit'
Rename the section existing in C<filename> given by C<from> to the section
given by C<to>.
Throws an exception C<No such section> if the section in C<from> doesn't exist
in C<filename>.
If no value is given for C<to>, the section is removed instead of renamed.
Returns true on success, false if C<filename> didn't exist and thus
the rename did nothing.
=head2 remove_section
Parameters:
section => 'section.subsection'
filename => '/file/to/edit'
Just a convenience wrapper around L<"rename_section"> for readability's sake.
Removes the given section (which you can do by renaming to nothing as well).
=head2 add_comment
Parameters:
comment => "Begin editing here\n and then stop",
filename => '/file/to/edit'
indented => 1,
semicolon => 0,
Add a comment to the specified configuration file. The C<comment> and
C<filename> parameters are required. Comments will be added to the file with
C<# > at the begnning of each line of the comment. Pass a true value to
C<semicolon> if you'd rather they start with C<; >. If your comments are
indented with leading white space, and you want that white space to appear in
front of the comment character, rather than after, pass a true value to
C<indented>.
=head2 cascade( $bool )
Gets or sets if only the B<deepest> configuration file in a directory
tree is loaded, or if all of them are loaded, shallowest to deepest.
Alternately, C<cascade =E<gt> 1> can be passed to C<new>.
=head2 origins
Returns a hash mapping each config key with the file it was loaded from.
=head1 METHODS YOU MAY WISH TO OVERRIDE
If your application's configuration layout is different from the default, e.g.
if its home directory config files are in a directory within the home
directory (like C<~/.git/config>) instead of just dot-prefixed, override these
methods to return the right directory names. For fancier things like altering
precedence, you'll need to override L<"load"> as well.
=head2 dir_file
Return a string containing the path to a configuration file with the
name C<confname> in a directory. Called with no arguments,
returns the path for a generic directory; if called with a
directory as an argument, returns the path for I<that> directory.
=head2 global_file
Return the string C</etc/confname>, the absolute name of the system-wide
configuration file with name C<confname>.
=head2 user_file
Return a string containing the path to a configuration file
in the current user's home directory with filename C<confname>.
=head2 load_dirs
Parameters:
'/path/to/look/in/'
Load the configuration file with the filename L<"dir_file"> in the current
working directory into the memory or, if there is no config matching
C<dir_file> in the current working directory, walk up the directory tree until
one is found. (No error is thrown if none is found.) If an optional path
is passed in, that directory will be used as the base directory instead
of the working directory.
You'll want to use L<"load_file"> to load config files from your overridden
version of this subroutine.
Returns nothing of note.
=head1 OTHER METHODS
These are mostly used internally in other methods, but could be useful anyway.
=head2 load_global
If a global configuration file with the absolute name given by
L<"global_file"> exists, load its configuration variables into memory.
Returns the current contents of all the loaded configuration variables
after the file has been loaded, or undef if no global config file is found.
( run in 0.489 second using v1.01-cache-2.11-cpan-bbe5e583499 )