90 |
rodolico |
1 |
use 5.008001; # sane UTF-8 support
|
|
|
2 |
use strict;
|
|
|
3 |
use warnings;
|
|
|
4 |
package YAML::Tiny; # git description: v1.72-7-g8682f63
|
|
|
5 |
# XXX-INGY is 5.8.1 too old/broken for utf8?
|
|
|
6 |
# XXX-XDG Lancaster consensus was that it was sufficient until
|
|
|
7 |
# proven otherwise
|
|
|
8 |
|
|
|
9 |
our $VERSION = '1.73';
|
|
|
10 |
|
|
|
11 |
#####################################################################
|
|
|
12 |
# The YAML::Tiny API.
|
|
|
13 |
#
|
|
|
14 |
# These are the currently documented API functions/methods and
|
|
|
15 |
# exports:
|
|
|
16 |
|
|
|
17 |
use Exporter;
|
|
|
18 |
our @ISA = qw{ Exporter };
|
|
|
19 |
our @EXPORT = qw{ Load Dump };
|
|
|
20 |
our @EXPORT_OK = qw{ LoadFile DumpFile freeze thaw };
|
|
|
21 |
|
|
|
22 |
###
|
|
|
23 |
# Functional/Export API:
|
|
|
24 |
|
|
|
25 |
sub Dump {
|
|
|
26 |
return YAML::Tiny->new(@_)->_dump_string;
|
|
|
27 |
}
|
|
|
28 |
|
|
|
29 |
# XXX-INGY Returning last document seems a bad behavior.
|
|
|
30 |
# XXX-XDG I think first would seem more natural, but I don't know
|
|
|
31 |
# that it's worth changing now
|
|
|
32 |
sub Load {
|
|
|
33 |
my $self = YAML::Tiny->_load_string(@_);
|
|
|
34 |
if ( wantarray ) {
|
|
|
35 |
return @$self;
|
|
|
36 |
} else {
|
|
|
37 |
# To match YAML.pm, return the last document
|
|
|
38 |
return $self->[-1];
|
|
|
39 |
}
|
|
|
40 |
}
|
|
|
41 |
|
|
|
42 |
# XXX-INGY Do we really need freeze and thaw?
|
|
|
43 |
# XXX-XDG I don't think so. I'd support deprecating them.
|
|
|
44 |
BEGIN {
|
|
|
45 |
*freeze = \&Dump;
|
|
|
46 |
*thaw = \&Load;
|
|
|
47 |
}
|
|
|
48 |
|
|
|
49 |
sub DumpFile {
|
|
|
50 |
my $file = shift;
|
|
|
51 |
return YAML::Tiny->new(@_)->_dump_file($file);
|
|
|
52 |
}
|
|
|
53 |
|
|
|
54 |
sub LoadFile {
|
|
|
55 |
my $file = shift;
|
|
|
56 |
my $self = YAML::Tiny->_load_file($file);
|
|
|
57 |
if ( wantarray ) {
|
|
|
58 |
return @$self;
|
|
|
59 |
} else {
|
|
|
60 |
# Return only the last document to match YAML.pm,
|
|
|
61 |
return $self->[-1];
|
|
|
62 |
}
|
|
|
63 |
}
|
|
|
64 |
|
|
|
65 |
|
|
|
66 |
###
|
|
|
67 |
# Object Oriented API:
|
|
|
68 |
|
|
|
69 |
# Create an empty YAML::Tiny object
|
|
|
70 |
# XXX-INGY Why do we use ARRAY object?
|
|
|
71 |
# NOTE: I get it now, but I think it's confusing and not needed.
|
|
|
72 |
# Will change it on a branch later, for review.
|
|
|
73 |
#
|
|
|
74 |
# XXX-XDG I don't support changing it yet. It's a very well-documented
|
|
|
75 |
# "API" of YAML::Tiny. I'd support deprecating it, but Adam suggested
|
|
|
76 |
# we not change it until YAML.pm's own OO API is established so that
|
|
|
77 |
# users only have one API change to digest, not two
|
|
|
78 |
sub new {
|
|
|
79 |
my $class = shift;
|
|
|
80 |
bless [ @_ ], $class;
|
|
|
81 |
}
|
|
|
82 |
|
|
|
83 |
# XXX-INGY It probably doesn't matter, and it's probably too late to
|
|
|
84 |
# change, but 'read/write' are the wrong names. Read and Write
|
|
|
85 |
# are actions that take data from storage to memory
|
|
|
86 |
# characters/strings. These take the data to/from storage to native
|
|
|
87 |
# Perl objects, which the terms dump and load are meant. As long as
|
|
|
88 |
# this is a legacy quirk to YAML::Tiny it's ok, but I'd prefer not
|
|
|
89 |
# to add new {read,write}_* methods to this API.
|
|
|
90 |
|
|
|
91 |
sub read_string {
|
|
|
92 |
my $self = shift;
|
|
|
93 |
$self->_load_string(@_);
|
|
|
94 |
}
|
|
|
95 |
|
|
|
96 |
sub write_string {
|
|
|
97 |
my $self = shift;
|
|
|
98 |
$self->_dump_string(@_);
|
|
|
99 |
}
|
|
|
100 |
|
|
|
101 |
sub read {
|
|
|
102 |
my $self = shift;
|
|
|
103 |
$self->_load_file(@_);
|
|
|
104 |
}
|
|
|
105 |
|
|
|
106 |
sub write {
|
|
|
107 |
my $self = shift;
|
|
|
108 |
$self->_dump_file(@_);
|
|
|
109 |
}
|
|
|
110 |
|
|
|
111 |
|
|
|
112 |
|
|
|
113 |
|
|
|
114 |
#####################################################################
|
|
|
115 |
# Constants
|
|
|
116 |
|
|
|
117 |
# Printed form of the unprintable characters in the lowest range
|
|
|
118 |
# of ASCII characters, listed by ASCII ordinal position.
|
|
|
119 |
my @UNPRINTABLE = qw(
|
|
|
120 |
|
|
|
121 |
b t n v f r x0E x0F
|
|
|
122 |
x10 x11 x12 x13 x14 x15 x16 x17
|
|
|
123 |
x18 x19 x1A e x1C x1D x1E x1F
|
|
|
124 |
);
|
|
|
125 |
|
|
|
126 |
# Printable characters for escapes
|
|
|
127 |
my %UNESCAPES = (
|
|
|
128 |
|
|
|
129 |
a => "\x07", b => "\x08", t => "\x09",
|
|
|
130 |
n => "\x0a", v => "\x0b", f => "\x0c",
|
|
|
131 |
r => "\x0d", e => "\x1b", '\\' => '\\',
|
|
|
132 |
);
|
|
|
133 |
|
|
|
134 |
# XXX-INGY
|
|
|
135 |
# I(ngy) need to decide if these values should be quoted in
|
|
|
136 |
# YAML::Tiny or not. Probably yes.
|
|
|
137 |
|
|
|
138 |
# These 3 values have special meaning when unquoted and using the
|
|
|
139 |
# default YAML schema. They need quotes if they are strings.
|
|
|
140 |
my %QUOTE = map { $_ => 1 } qw{
|
|
|
141 |
null true false
|
|
|
142 |
};
|
|
|
143 |
|
|
|
144 |
# The commented out form is simpler, but overloaded the Perl regex
|
|
|
145 |
# engine due to recursion and backtracking problems on strings
|
|
|
146 |
# larger than 32,000ish characters. Keep it for reference purposes.
|
|
|
147 |
# qr/\"((?:\\.|[^\"])*)\"/
|
|
|
148 |
my $re_capture_double_quoted = qr/\"([^\\"]*(?:\\.[^\\"]*)*)\"/;
|
|
|
149 |
my $re_capture_single_quoted = qr/\'([^\']*(?:\'\'[^\']*)*)\'/;
|
|
|
150 |
# unquoted re gets trailing space that needs to be stripped
|
|
|
151 |
my $re_capture_unquoted_key = qr/([^:]+(?::+\S(?:[^:]*|.*?(?=:)))*)(?=\s*\:(?:\s+|$))/;
|
|
|
152 |
my $re_trailing_comment = qr/(?:\s+\#.*)?/;
|
|
|
153 |
my $re_key_value_separator = qr/\s*:(?:\s+(?:\#.*)?|$)/;
|
|
|
154 |
|
|
|
155 |
|
|
|
156 |
|
|
|
157 |
|
|
|
158 |
|
|
|
159 |
#####################################################################
|
|
|
160 |
# YAML::Tiny Implementation.
|
|
|
161 |
#
|
|
|
162 |
# These are the private methods that do all the work. They may change
|
|
|
163 |
# at any time.
|
|
|
164 |
|
|
|
165 |
|
|
|
166 |
###
|
|
|
167 |
# Loader functions:
|
|
|
168 |
|
|
|
169 |
# Create an object from a file
|
|
|
170 |
sub _load_file {
|
|
|
171 |
my $class = ref $_[0] ? ref shift : shift;
|
|
|
172 |
|
|
|
173 |
# Check the file
|
|
|
174 |
my $file = shift or $class->_error( 'You did not specify a file name' );
|
|
|
175 |
$class->_error( "File '$file' does not exist" )
|
|
|
176 |
unless -e $file;
|
|
|
177 |
$class->_error( "'$file' is a directory, not a file" )
|
|
|
178 |
unless -f _;
|
|
|
179 |
$class->_error( "Insufficient permissions to read '$file'" )
|
|
|
180 |
unless -r _;
|
|
|
181 |
|
|
|
182 |
# Open unbuffered with strict UTF-8 decoding and no translation layers
|
|
|
183 |
open( my $fh, "<:unix:encoding(UTF-8)", $file );
|
|
|
184 |
unless ( $fh ) {
|
|
|
185 |
$class->_error("Failed to open file '$file': $!");
|
|
|
186 |
}
|
|
|
187 |
|
|
|
188 |
# flock if available (or warn if not possible for OS-specific reasons)
|
|
|
189 |
if ( _can_flock() ) {
|
|
|
190 |
flock( $fh, Fcntl::LOCK_SH() )
|
|
|
191 |
or warn "Couldn't lock '$file' for reading: $!";
|
|
|
192 |
}
|
|
|
193 |
|
|
|
194 |
# slurp the contents
|
|
|
195 |
my $contents = eval {
|
|
|
196 |
use warnings FATAL => 'utf8';
|
|
|
197 |
local $/;
|
|
|
198 |
<$fh>
|
|
|
199 |
};
|
|
|
200 |
if ( my $err = $@ ) {
|
|
|
201 |
$class->_error("Error reading from file '$file': $err");
|
|
|
202 |
}
|
|
|
203 |
|
|
|
204 |
# close the file (release the lock)
|
|
|
205 |
unless ( close $fh ) {
|
|
|
206 |
$class->_error("Failed to close file '$file': $!");
|
|
|
207 |
}
|
|
|
208 |
|
|
|
209 |
$class->_load_string( $contents );
|
|
|
210 |
}
|
|
|
211 |
|
|
|
212 |
# Create an object from a string
|
|
|
213 |
sub _load_string {
|
|
|
214 |
my $class = ref $_[0] ? ref shift : shift;
|
|
|
215 |
my $self = bless [], $class;
|
|
|
216 |
my $string = $_[0];
|
|
|
217 |
eval {
|
|
|
218 |
unless ( defined $string ) {
|
|
|
219 |
die \"Did not provide a string to load";
|
|
|
220 |
}
|
|
|
221 |
|
|
|
222 |
# Check if Perl has it marked as characters, but it's internally
|
|
|
223 |
# inconsistent. E.g. maybe latin1 got read on a :utf8 layer
|
|
|
224 |
if ( utf8::is_utf8($string) && ! utf8::valid($string) ) {
|
|
|
225 |
die \<<'...';
|
|
|
226 |
Read an invalid UTF-8 string (maybe mixed UTF-8 and 8-bit character set).
|
|
|
227 |
Did you decode with lax ":utf8" instead of strict ":encoding(UTF-8)"?
|
|
|
228 |
...
|
|
|
229 |
}
|
|
|
230 |
|
|
|
231 |
# Ensure Unicode character semantics, even for 0x80-0xff
|
|
|
232 |
utf8::upgrade($string);
|
|
|
233 |
|
|
|
234 |
# Check for and strip any leading UTF-8 BOM
|
|
|
235 |
$string =~ s/^\x{FEFF}//;
|
|
|
236 |
|
|
|
237 |
# Check for some special cases
|
|
|
238 |
return $self unless length $string;
|
|
|
239 |
|
|
|
240 |
# Split the file into lines
|
|
|
241 |
my @lines = grep { ! /^\s*(?:\#.*)?\z/ }
|
|
|
242 |
split /(?:\015{1,2}\012|\015|\012)/, $string;
|
|
|
243 |
|
|
|
244 |
# Strip the initial YAML header
|
|
|
245 |
@lines and $lines[0] =~ /^\%YAML[: ][\d\.]+.*\z/ and shift @lines;
|
|
|
246 |
|
|
|
247 |
# A nibbling parser
|
|
|
248 |
my $in_document = 0;
|
|
|
249 |
while ( @lines ) {
|
|
|
250 |
# Do we have a document header?
|
|
|
251 |
if ( $lines[0] =~ /^---\s*(?:(.+)\s*)?\z/ ) {
|
|
|
252 |
# Handle scalar documents
|
|
|
253 |
shift @lines;
|
|
|
254 |
if ( defined $1 and $1 !~ /^(?:\#.+|\%YAML[: ][\d\.]+)\z/ ) {
|
|
|
255 |
push @$self,
|
|
|
256 |
$self->_load_scalar( "$1", [ undef ], \@lines );
|
|
|
257 |
next;
|
|
|
258 |
}
|
|
|
259 |
$in_document = 1;
|
|
|
260 |
}
|
|
|
261 |
|
|
|
262 |
if ( ! @lines or $lines[0] =~ /^(?:---|\.\.\.)/ ) {
|
|
|
263 |
# A naked document
|
|
|
264 |
push @$self, undef;
|
|
|
265 |
while ( @lines and $lines[0] !~ /^---/ ) {
|
|
|
266 |
shift @lines;
|
|
|
267 |
}
|
|
|
268 |
$in_document = 0;
|
|
|
269 |
|
|
|
270 |
# XXX The final '-+$' is to look for -- which ends up being an
|
|
|
271 |
# error later.
|
|
|
272 |
} elsif ( ! $in_document && @$self ) {
|
|
|
273 |
# only the first document can be explicit
|
|
|
274 |
die \"YAML::Tiny failed to classify the line '$lines[0]'";
|
|
|
275 |
} elsif ( $lines[0] =~ /^\s*\-(?:\s|$|-+$)/ ) {
|
|
|
276 |
# An array at the root
|
|
|
277 |
my $document = [ ];
|
|
|
278 |
push @$self, $document;
|
|
|
279 |
$self->_load_array( $document, [ 0 ], \@lines );
|
|
|
280 |
|
|
|
281 |
} elsif ( $lines[0] =~ /^(\s*)\S/ ) {
|
|
|
282 |
# A hash at the root
|
|
|
283 |
my $document = { };
|
|
|
284 |
push @$self, $document;
|
|
|
285 |
$self->_load_hash( $document, [ length($1) ], \@lines );
|
|
|
286 |
|
|
|
287 |
} else {
|
|
|
288 |
# Shouldn't get here. @lines have whitespace-only lines
|
|
|
289 |
# stripped, and previous match is a line with any
|
|
|
290 |
# non-whitespace. So this clause should only be reachable via
|
|
|
291 |
# a perlbug where \s is not symmetric with \S
|
|
|
292 |
|
|
|
293 |
# uncoverable statement
|
|
|
294 |
die \"YAML::Tiny failed to classify the line '$lines[0]'";
|
|
|
295 |
}
|
|
|
296 |
}
|
|
|
297 |
};
|
|
|
298 |
my $err = $@;
|
|
|
299 |
if ( ref $err eq 'SCALAR' ) {
|
|
|
300 |
$self->_error(${$err});
|
|
|
301 |
} elsif ( $err ) {
|
|
|
302 |
$self->_error($err);
|
|
|
303 |
}
|
|
|
304 |
|
|
|
305 |
return $self;
|
|
|
306 |
}
|
|
|
307 |
|
|
|
308 |
sub _unquote_single {
|
|
|
309 |
my ($self, $string) = @_;
|
|
|
310 |
return '' unless length $string;
|
|
|
311 |
$string =~ s/\'\'/\'/g;
|
|
|
312 |
return $string;
|
|
|
313 |
}
|
|
|
314 |
|
|
|
315 |
sub _unquote_double {
|
|
|
316 |
my ($self, $string) = @_;
|
|
|
317 |
return '' unless length $string;
|
|
|
318 |
$string =~ s/\\"/"/g;
|
|
|
319 |
$string =~
|
|
|
320 |
s{\\([Nnever\\fartz0b]|x([0-9a-fA-F]{2}))}
|
|
|
321 |
{(length($1)>1)?pack("H2",$2):$UNESCAPES{$1}}gex;
|
|
|
322 |
return $string;
|
|
|
323 |
}
|
|
|
324 |
|
|
|
325 |
# Load a YAML scalar string to the actual Perl scalar
|
|
|
326 |
sub _load_scalar {
|
|
|
327 |
my ($self, $string, $indent, $lines) = @_;
|
|
|
328 |
|
|
|
329 |
# Trim trailing whitespace
|
|
|
330 |
$string =~ s/\s*\z//;
|
|
|
331 |
|
|
|
332 |
# Explitic null/undef
|
|
|
333 |
return undef if $string eq '~';
|
|
|
334 |
|
|
|
335 |
# Single quote
|
|
|
336 |
if ( $string =~ /^$re_capture_single_quoted$re_trailing_comment\z/ ) {
|
|
|
337 |
return $self->_unquote_single($1);
|
|
|
338 |
}
|
|
|
339 |
|
|
|
340 |
# Double quote.
|
|
|
341 |
if ( $string =~ /^$re_capture_double_quoted$re_trailing_comment\z/ ) {
|
|
|
342 |
return $self->_unquote_double($1);
|
|
|
343 |
}
|
|
|
344 |
|
|
|
345 |
# Special cases
|
|
|
346 |
if ( $string =~ /^[\'\"!&]/ ) {
|
|
|
347 |
die \"YAML::Tiny does not support a feature in line '$string'";
|
|
|
348 |
}
|
|
|
349 |
return {} if $string =~ /^{}(?:\s+\#.*)?\z/;
|
|
|
350 |
return [] if $string =~ /^\[\](?:\s+\#.*)?\z/;
|
|
|
351 |
|
|
|
352 |
# Regular unquoted string
|
|
|
353 |
if ( $string !~ /^[>|]/ ) {
|
|
|
354 |
die \"YAML::Tiny found illegal characters in plain scalar: '$string'"
|
|
|
355 |
if $string =~ /^(?:-(?:\s|$)|[\@\%\`])/ or
|
|
|
356 |
$string =~ /:(?:\s|$)/;
|
|
|
357 |
$string =~ s/\s+#.*\z//;
|
|
|
358 |
return $string;
|
|
|
359 |
}
|
|
|
360 |
|
|
|
361 |
# Error
|
|
|
362 |
die \"YAML::Tiny failed to find multi-line scalar content" unless @$lines;
|
|
|
363 |
|
|
|
364 |
# Check the indent depth
|
|
|
365 |
$lines->[0] =~ /^(\s*)/;
|
|
|
366 |
$indent->[-1] = length("$1");
|
|
|
367 |
if ( defined $indent->[-2] and $indent->[-1] <= $indent->[-2] ) {
|
|
|
368 |
die \"YAML::Tiny found bad indenting in line '$lines->[0]'";
|
|
|
369 |
}
|
|
|
370 |
|
|
|
371 |
# Pull the lines
|
|
|
372 |
my @multiline = ();
|
|
|
373 |
while ( @$lines ) {
|
|
|
374 |
$lines->[0] =~ /^(\s*)/;
|
|
|
375 |
last unless length($1) >= $indent->[-1];
|
|
|
376 |
push @multiline, substr(shift(@$lines), $indent->[-1]);
|
|
|
377 |
}
|
|
|
378 |
|
|
|
379 |
my $j = (substr($string, 0, 1) eq '>') ? ' ' : "\n";
|
|
|
380 |
my $t = (substr($string, 1, 1) eq '-') ? '' : "\n";
|
|
|
381 |
return join( $j, @multiline ) . $t;
|
|
|
382 |
}
|
|
|
383 |
|
|
|
384 |
# Load an array
|
|
|
385 |
sub _load_array {
|
|
|
386 |
my ($self, $array, $indent, $lines) = @_;
|
|
|
387 |
|
|
|
388 |
while ( @$lines ) {
|
|
|
389 |
# Check for a new document
|
|
|
390 |
if ( $lines->[0] =~ /^(?:---|\.\.\.)/ ) {
|
|
|
391 |
while ( @$lines and $lines->[0] !~ /^---/ ) {
|
|
|
392 |
shift @$lines;
|
|
|
393 |
}
|
|
|
394 |
return 1;
|
|
|
395 |
}
|
|
|
396 |
|
|
|
397 |
# Check the indent level
|
|
|
398 |
$lines->[0] =~ /^(\s*)/;
|
|
|
399 |
if ( length($1) < $indent->[-1] ) {
|
|
|
400 |
return 1;
|
|
|
401 |
} elsif ( length($1) > $indent->[-1] ) {
|
|
|
402 |
die \"YAML::Tiny found bad indenting in line '$lines->[0]'";
|
|
|
403 |
}
|
|
|
404 |
|
|
|
405 |
if ( $lines->[0] =~ /^(\s*\-\s+)[^\'\"]\S*\s*:(?:\s+|$)/ ) {
|
|
|
406 |
# Inline nested hash
|
|
|
407 |
my $indent2 = length("$1");
|
|
|
408 |
$lines->[0] =~ s/-/ /;
|
|
|
409 |
push @$array, { };
|
|
|
410 |
$self->_load_hash( $array->[-1], [ @$indent, $indent2 ], $lines );
|
|
|
411 |
|
|
|
412 |
} elsif ( $lines->[0] =~ /^\s*\-\s*\z/ ) {
|
|
|
413 |
shift @$lines;
|
|
|
414 |
unless ( @$lines ) {
|
|
|
415 |
push @$array, undef;
|
|
|
416 |
return 1;
|
|
|
417 |
}
|
|
|
418 |
if ( $lines->[0] =~ /^(\s*)\-/ ) {
|
|
|
419 |
my $indent2 = length("$1");
|
|
|
420 |
if ( $indent->[-1] == $indent2 ) {
|
|
|
421 |
# Null array entry
|
|
|
422 |
push @$array, undef;
|
|
|
423 |
} else {
|
|
|
424 |
# Naked indenter
|
|
|
425 |
push @$array, [ ];
|
|
|
426 |
$self->_load_array(
|
|
|
427 |
$array->[-1], [ @$indent, $indent2 ], $lines
|
|
|
428 |
);
|
|
|
429 |
}
|
|
|
430 |
|
|
|
431 |
} elsif ( $lines->[0] =~ /^(\s*)\S/ ) {
|
|
|
432 |
push @$array, { };
|
|
|
433 |
$self->_load_hash(
|
|
|
434 |
$array->[-1], [ @$indent, length("$1") ], $lines
|
|
|
435 |
);
|
|
|
436 |
|
|
|
437 |
} else {
|
|
|
438 |
die \"YAML::Tiny failed to classify line '$lines->[0]'";
|
|
|
439 |
}
|
|
|
440 |
|
|
|
441 |
} elsif ( $lines->[0] =~ /^\s*\-(\s*)(.+?)\s*\z/ ) {
|
|
|
442 |
# Array entry with a value
|
|
|
443 |
shift @$lines;
|
|
|
444 |
push @$array, $self->_load_scalar(
|
|
|
445 |
"$2", [ @$indent, undef ], $lines
|
|
|
446 |
);
|
|
|
447 |
|
|
|
448 |
} elsif ( defined $indent->[-2] and $indent->[-1] == $indent->[-2] ) {
|
|
|
449 |
# This is probably a structure like the following...
|
|
|
450 |
# ---
|
|
|
451 |
# foo:
|
|
|
452 |
# - list
|
|
|
453 |
# bar: value
|
|
|
454 |
#
|
|
|
455 |
# ... so lets return and let the hash parser handle it
|
|
|
456 |
return 1;
|
|
|
457 |
|
|
|
458 |
} else {
|
|
|
459 |
die \"YAML::Tiny failed to classify line '$lines->[0]'";
|
|
|
460 |
}
|
|
|
461 |
}
|
|
|
462 |
|
|
|
463 |
return 1;
|
|
|
464 |
}
|
|
|
465 |
|
|
|
466 |
# Load a hash
|
|
|
467 |
sub _load_hash {
|
|
|
468 |
my ($self, $hash, $indent, $lines) = @_;
|
|
|
469 |
|
|
|
470 |
while ( @$lines ) {
|
|
|
471 |
# Check for a new document
|
|
|
472 |
if ( $lines->[0] =~ /^(?:---|\.\.\.)/ ) {
|
|
|
473 |
while ( @$lines and $lines->[0] !~ /^---/ ) {
|
|
|
474 |
shift @$lines;
|
|
|
475 |
}
|
|
|
476 |
return 1;
|
|
|
477 |
}
|
|
|
478 |
|
|
|
479 |
# Check the indent level
|
|
|
480 |
$lines->[0] =~ /^(\s*)/;
|
|
|
481 |
if ( length($1) < $indent->[-1] ) {
|
|
|
482 |
return 1;
|
|
|
483 |
} elsif ( length($1) > $indent->[-1] ) {
|
|
|
484 |
die \"YAML::Tiny found bad indenting in line '$lines->[0]'";
|
|
|
485 |
}
|
|
|
486 |
|
|
|
487 |
# Find the key
|
|
|
488 |
my $key;
|
|
|
489 |
|
|
|
490 |
# Quoted keys
|
|
|
491 |
if ( $lines->[0] =~
|
|
|
492 |
s/^\s*$re_capture_single_quoted$re_key_value_separator//
|
|
|
493 |
) {
|
|
|
494 |
$key = $self->_unquote_single($1);
|
|
|
495 |
}
|
|
|
496 |
elsif ( $lines->[0] =~
|
|
|
497 |
s/^\s*$re_capture_double_quoted$re_key_value_separator//
|
|
|
498 |
) {
|
|
|
499 |
$key = $self->_unquote_double($1);
|
|
|
500 |
}
|
|
|
501 |
elsif ( $lines->[0] =~
|
|
|
502 |
s/^\s*$re_capture_unquoted_key$re_key_value_separator//
|
|
|
503 |
) {
|
|
|
504 |
$key = $1;
|
|
|
505 |
$key =~ s/\s+$//;
|
|
|
506 |
}
|
|
|
507 |
elsif ( $lines->[0] =~ /^\s*\?/ ) {
|
|
|
508 |
die \"YAML::Tiny does not support a feature in line '$lines->[0]'";
|
|
|
509 |
}
|
|
|
510 |
else {
|
|
|
511 |
die \"YAML::Tiny failed to classify line '$lines->[0]'";
|
|
|
512 |
}
|
|
|
513 |
|
|
|
514 |
if ( exists $hash->{$key} ) {
|
|
|
515 |
warn "YAML::Tiny found a duplicate key '$key' in line '$lines->[0]'";
|
|
|
516 |
}
|
|
|
517 |
|
|
|
518 |
# Do we have a value?
|
|
|
519 |
if ( length $lines->[0] ) {
|
|
|
520 |
# Yes
|
|
|
521 |
$hash->{$key} = $self->_load_scalar(
|
|
|
522 |
shift(@$lines), [ @$indent, undef ], $lines
|
|
|
523 |
);
|
|
|
524 |
} else {
|
|
|
525 |
# An indent
|
|
|
526 |
shift @$lines;
|
|
|
527 |
unless ( @$lines ) {
|
|
|
528 |
$hash->{$key} = undef;
|
|
|
529 |
return 1;
|
|
|
530 |
}
|
|
|
531 |
if ( $lines->[0] =~ /^(\s*)-/ ) {
|
|
|
532 |
$hash->{$key} = [];
|
|
|
533 |
$self->_load_array(
|
|
|
534 |
$hash->{$key}, [ @$indent, length($1) ], $lines
|
|
|
535 |
);
|
|
|
536 |
} elsif ( $lines->[0] =~ /^(\s*)./ ) {
|
|
|
537 |
my $indent2 = length("$1");
|
|
|
538 |
if ( $indent->[-1] >= $indent2 ) {
|
|
|
539 |
# Null hash entry
|
|
|
540 |
$hash->{$key} = undef;
|
|
|
541 |
} else {
|
|
|
542 |
$hash->{$key} = {};
|
|
|
543 |
$self->_load_hash(
|
|
|
544 |
$hash->{$key}, [ @$indent, length($1) ], $lines
|
|
|
545 |
);
|
|
|
546 |
}
|
|
|
547 |
}
|
|
|
548 |
}
|
|
|
549 |
}
|
|
|
550 |
|
|
|
551 |
return 1;
|
|
|
552 |
}
|
|
|
553 |
|
|
|
554 |
|
|
|
555 |
###
|
|
|
556 |
# Dumper functions:
|
|
|
557 |
|
|
|
558 |
# Save an object to a file
|
|
|
559 |
sub _dump_file {
|
|
|
560 |
my $self = shift;
|
|
|
561 |
|
|
|
562 |
require Fcntl;
|
|
|
563 |
|
|
|
564 |
# Check the file
|
|
|
565 |
my $file = shift or $self->_error( 'You did not specify a file name' );
|
|
|
566 |
|
|
|
567 |
my $fh;
|
|
|
568 |
# flock if available (or warn if not possible for OS-specific reasons)
|
|
|
569 |
if ( _can_flock() ) {
|
|
|
570 |
# Open without truncation (truncate comes after lock)
|
|
|
571 |
my $flags = Fcntl::O_WRONLY()|Fcntl::O_CREAT();
|
|
|
572 |
sysopen( $fh, $file, $flags )
|
|
|
573 |
or $self->_error("Failed to open file '$file' for writing: $!");
|
|
|
574 |
|
|
|
575 |
# Use no translation and strict UTF-8
|
|
|
576 |
binmode( $fh, ":raw:encoding(UTF-8)");
|
|
|
577 |
|
|
|
578 |
flock( $fh, Fcntl::LOCK_EX() )
|
|
|
579 |
or warn "Couldn't lock '$file' for reading: $!";
|
|
|
580 |
|
|
|
581 |
# truncate and spew contents
|
|
|
582 |
truncate $fh, 0;
|
|
|
583 |
seek $fh, 0, 0;
|
|
|
584 |
}
|
|
|
585 |
else {
|
|
|
586 |
open $fh, ">:unix:encoding(UTF-8)", $file;
|
|
|
587 |
}
|
|
|
588 |
|
|
|
589 |
# serialize and spew to the handle
|
|
|
590 |
print {$fh} $self->_dump_string;
|
|
|
591 |
|
|
|
592 |
# close the file (release the lock)
|
|
|
593 |
unless ( close $fh ) {
|
|
|
594 |
$self->_error("Failed to close file '$file': $!");
|
|
|
595 |
}
|
|
|
596 |
|
|
|
597 |
return 1;
|
|
|
598 |
}
|
|
|
599 |
|
|
|
600 |
# Save an object to a string
|
|
|
601 |
sub _dump_string {
|
|
|
602 |
my $self = shift;
|
|
|
603 |
return '' unless ref $self && @$self;
|
|
|
604 |
|
|
|
605 |
# Iterate over the documents
|
|
|
606 |
my $indent = 0;
|
|
|
607 |
my @lines = ();
|
|
|
608 |
|
|
|
609 |
eval {
|
|
|
610 |
foreach my $cursor ( @$self ) {
|
|
|
611 |
push @lines, '---';
|
|
|
612 |
|
|
|
613 |
# An empty document
|
|
|
614 |
if ( ! defined $cursor ) {
|
|
|
615 |
# Do nothing
|
|
|
616 |
|
|
|
617 |
# A scalar document
|
|
|
618 |
} elsif ( ! ref $cursor ) {
|
|
|
619 |
$lines[-1] .= ' ' . $self->_dump_scalar( $cursor );
|
|
|
620 |
|
|
|
621 |
# A list at the root
|
|
|
622 |
} elsif ( ref $cursor eq 'ARRAY' ) {
|
|
|
623 |
unless ( @$cursor ) {
|
|
|
624 |
$lines[-1] .= ' []';
|
|
|
625 |
next;
|
|
|
626 |
}
|
|
|
627 |
push @lines, $self->_dump_array( $cursor, $indent, {} );
|
|
|
628 |
|
|
|
629 |
# A hash at the root
|
|
|
630 |
} elsif ( ref $cursor eq 'HASH' ) {
|
|
|
631 |
unless ( %$cursor ) {
|
|
|
632 |
$lines[-1] .= ' {}';
|
|
|
633 |
next;
|
|
|
634 |
}
|
|
|
635 |
push @lines, $self->_dump_hash( $cursor, $indent, {} );
|
|
|
636 |
|
|
|
637 |
} else {
|
|
|
638 |
die \("Cannot serialize " . ref($cursor));
|
|
|
639 |
}
|
|
|
640 |
}
|
|
|
641 |
};
|
|
|
642 |
if ( ref $@ eq 'SCALAR' ) {
|
|
|
643 |
$self->_error(${$@});
|
|
|
644 |
} elsif ( $@ ) {
|
|
|
645 |
$self->_error($@);
|
|
|
646 |
}
|
|
|
647 |
|
|
|
648 |
join '', map { "$_\n" } @lines;
|
|
|
649 |
}
|
|
|
650 |
|
|
|
651 |
sub _has_internal_string_value {
|
|
|
652 |
my $value = shift;
|
|
|
653 |
my $b_obj = B::svref_2object(\$value); # for round trip problem
|
|
|
654 |
return $b_obj->FLAGS & B::SVf_POK();
|
|
|
655 |
}
|
|
|
656 |
|
|
|
657 |
sub _dump_scalar {
|
|
|
658 |
my $string = $_[1];
|
|
|
659 |
my $is_key = $_[2];
|
|
|
660 |
# Check this before checking length or it winds up looking like a string!
|
|
|
661 |
my $has_string_flag = _has_internal_string_value($string);
|
|
|
662 |
return '~' unless defined $string;
|
|
|
663 |
return "''" unless length $string;
|
|
|
664 |
if (Scalar::Util::looks_like_number($string)) {
|
|
|
665 |
# keys and values that have been used as strings get quoted
|
|
|
666 |
if ( $is_key || $has_string_flag ) {
|
|
|
667 |
return qq['$string'];
|
|
|
668 |
}
|
|
|
669 |
else {
|
|
|
670 |
return $string;
|
|
|
671 |
}
|
|
|
672 |
}
|
|
|
673 |
if ( $string =~ /[\x00-\x09\x0b-\x0d\x0e-\x1f\x7f-\x9f\'\n]/ ) {
|
|
|
674 |
$string =~ s/\\/\\\\/g;
|
|
|
675 |
$string =~ s/"/\\"/g;
|
|
|
676 |
$string =~ s/\n/\\n/g;
|
|
|
677 |
$string =~ s/[\x85]/\\N/g;
|
|
|
678 |
$string =~ s/([\x00-\x1f])/\\$UNPRINTABLE[ord($1)]/g;
|
|
|
679 |
$string =~ s/([\x7f-\x9f])/'\x' . sprintf("%X",ord($1))/ge;
|
|
|
680 |
return qq|"$string"|;
|
|
|
681 |
}
|
|
|
682 |
if ( $string =~ /(?:^[~!@#%&*|>?:,'"`{}\[\]]|^-+$|\s|:\z)/ or
|
|
|
683 |
$QUOTE{$string}
|
|
|
684 |
) {
|
|
|
685 |
return "'$string'";
|
|
|
686 |
}
|
|
|
687 |
return $string;
|
|
|
688 |
}
|
|
|
689 |
|
|
|
690 |
sub _dump_array {
|
|
|
691 |
my ($self, $array, $indent, $seen) = @_;
|
|
|
692 |
if ( $seen->{refaddr($array)}++ ) {
|
|
|
693 |
die \"YAML::Tiny does not support circular references";
|
|
|
694 |
}
|
|
|
695 |
my @lines = ();
|
|
|
696 |
foreach my $el ( @$array ) {
|
|
|
697 |
my $line = (' ' x $indent) . '-';
|
|
|
698 |
my $type = ref $el;
|
|
|
699 |
if ( ! $type ) {
|
|
|
700 |
$line .= ' ' . $self->_dump_scalar( $el );
|
|
|
701 |
push @lines, $line;
|
|
|
702 |
|
|
|
703 |
} elsif ( $type eq 'ARRAY' ) {
|
|
|
704 |
if ( @$el ) {
|
|
|
705 |
push @lines, $line;
|
|
|
706 |
push @lines, $self->_dump_array( $el, $indent + 1, $seen );
|
|
|
707 |
} else {
|
|
|
708 |
$line .= ' []';
|
|
|
709 |
push @lines, $line;
|
|
|
710 |
}
|
|
|
711 |
|
|
|
712 |
} elsif ( $type eq 'HASH' ) {
|
|
|
713 |
if ( keys %$el ) {
|
|
|
714 |
push @lines, $line;
|
|
|
715 |
push @lines, $self->_dump_hash( $el, $indent + 1, $seen );
|
|
|
716 |
} else {
|
|
|
717 |
$line .= ' {}';
|
|
|
718 |
push @lines, $line;
|
|
|
719 |
}
|
|
|
720 |
|
|
|
721 |
} else {
|
|
|
722 |
die \"YAML::Tiny does not support $type references";
|
|
|
723 |
}
|
|
|
724 |
}
|
|
|
725 |
|
|
|
726 |
@lines;
|
|
|
727 |
}
|
|
|
728 |
|
|
|
729 |
sub _dump_hash {
|
|
|
730 |
my ($self, $hash, $indent, $seen) = @_;
|
|
|
731 |
if ( $seen->{refaddr($hash)}++ ) {
|
|
|
732 |
die \"YAML::Tiny does not support circular references";
|
|
|
733 |
}
|
|
|
734 |
my @lines = ();
|
|
|
735 |
foreach my $name ( sort keys %$hash ) {
|
|
|
736 |
my $el = $hash->{$name};
|
|
|
737 |
my $line = (' ' x $indent) . $self->_dump_scalar($name, 1) . ":";
|
|
|
738 |
my $type = ref $el;
|
|
|
739 |
if ( ! $type ) {
|
|
|
740 |
$line .= ' ' . $self->_dump_scalar( $el );
|
|
|
741 |
push @lines, $line;
|
|
|
742 |
|
|
|
743 |
} elsif ( $type eq 'ARRAY' ) {
|
|
|
744 |
if ( @$el ) {
|
|
|
745 |
push @lines, $line;
|
|
|
746 |
push @lines, $self->_dump_array( $el, $indent + 1, $seen );
|
|
|
747 |
} else {
|
|
|
748 |
$line .= ' []';
|
|
|
749 |
push @lines, $line;
|
|
|
750 |
}
|
|
|
751 |
|
|
|
752 |
} elsif ( $type eq 'HASH' ) {
|
|
|
753 |
if ( keys %$el ) {
|
|
|
754 |
push @lines, $line;
|
|
|
755 |
push @lines, $self->_dump_hash( $el, $indent + 1, $seen );
|
|
|
756 |
} else {
|
|
|
757 |
$line .= ' {}';
|
|
|
758 |
push @lines, $line;
|
|
|
759 |
}
|
|
|
760 |
|
|
|
761 |
} else {
|
|
|
762 |
die \"YAML::Tiny does not support $type references";
|
|
|
763 |
}
|
|
|
764 |
}
|
|
|
765 |
|
|
|
766 |
@lines;
|
|
|
767 |
}
|
|
|
768 |
|
|
|
769 |
|
|
|
770 |
|
|
|
771 |
#####################################################################
|
|
|
772 |
# DEPRECATED API methods:
|
|
|
773 |
|
|
|
774 |
# Error storage (DEPRECATED as of 1.57)
|
|
|
775 |
our $errstr = '';
|
|
|
776 |
|
|
|
777 |
# Set error
|
|
|
778 |
sub _error {
|
|
|
779 |
require Carp;
|
|
|
780 |
$errstr = $_[1];
|
|
|
781 |
$errstr =~ s/ at \S+ line \d+.*//;
|
|
|
782 |
Carp::croak( $errstr );
|
|
|
783 |
}
|
|
|
784 |
|
|
|
785 |
# Retrieve error
|
|
|
786 |
my $errstr_warned;
|
|
|
787 |
sub errstr {
|
|
|
788 |
require Carp;
|
|
|
789 |
Carp::carp( "YAML::Tiny->errstr and \$YAML::Tiny::errstr is deprecated" )
|
|
|
790 |
unless $errstr_warned++;
|
|
|
791 |
$errstr;
|
|
|
792 |
}
|
|
|
793 |
|
|
|
794 |
|
|
|
795 |
|
|
|
796 |
|
|
|
797 |
#####################################################################
|
|
|
798 |
# Helper functions. Possibly not needed.
|
|
|
799 |
|
|
|
800 |
|
|
|
801 |
# Use to detect nv or iv
|
|
|
802 |
use B;
|
|
|
803 |
|
|
|
804 |
# XXX-INGY Is flock YAML::Tiny's responsibility?
|
|
|
805 |
# Some platforms can't flock :-(
|
|
|
806 |
# XXX-XDG I think it is. When reading and writing files, we ought
|
|
|
807 |
# to be locking whenever possible. People (foolishly) use YAML
|
|
|
808 |
# files for things like session storage, which has race issues.
|
|
|
809 |
my $HAS_FLOCK;
|
|
|
810 |
sub _can_flock {
|
|
|
811 |
if ( defined $HAS_FLOCK ) {
|
|
|
812 |
return $HAS_FLOCK;
|
|
|
813 |
}
|
|
|
814 |
else {
|
|
|
815 |
require Config;
|
|
|
816 |
my $c = \%Config::Config;
|
|
|
817 |
$HAS_FLOCK = grep { $c->{$_} } qw/d_flock d_fcntl_can_lock d_lockf/;
|
|
|
818 |
require Fcntl if $HAS_FLOCK;
|
|
|
819 |
return $HAS_FLOCK;
|
|
|
820 |
}
|
|
|
821 |
}
|
|
|
822 |
|
|
|
823 |
|
|
|
824 |
# XXX-INGY Is this core in 5.8.1? Can we remove this?
|
|
|
825 |
# XXX-XDG Scalar::Util 1.18 didn't land until 5.8.8, so we need this
|
|
|
826 |
#####################################################################
|
|
|
827 |
# Use Scalar::Util if possible, otherwise emulate it
|
|
|
828 |
|
|
|
829 |
use Scalar::Util ();
|
|
|
830 |
BEGIN {
|
|
|
831 |
local $@;
|
|
|
832 |
if ( eval { Scalar::Util->VERSION(1.18); } ) {
|
|
|
833 |
*refaddr = *Scalar::Util::refaddr;
|
|
|
834 |
}
|
|
|
835 |
else {
|
|
|
836 |
eval <<'END_PERL';
|
|
|
837 |
# Scalar::Util failed to load or too old
|
|
|
838 |
sub refaddr {
|
|
|
839 |
my $pkg = ref($_[0]) or return undef;
|
|
|
840 |
if ( !! UNIVERSAL::can($_[0], 'can') ) {
|
|
|
841 |
bless $_[0], 'Scalar::Util::Fake';
|
|
|
842 |
} else {
|
|
|
843 |
$pkg = undef;
|
|
|
844 |
}
|
|
|
845 |
"$_[0]" =~ /0x(\w+)/;
|
|
|
846 |
my $i = do { no warnings 'portable'; hex $1 };
|
|
|
847 |
bless $_[0], $pkg if defined $pkg;
|
|
|
848 |
$i;
|
|
|
849 |
}
|
|
|
850 |
END_PERL
|
|
|
851 |
}
|
|
|
852 |
}
|
|
|
853 |
|
|
|
854 |
delete $YAML::Tiny::{refaddr};
|
|
|
855 |
|
|
|
856 |
1;
|
|
|
857 |
|
|
|
858 |
# XXX-INGY Doc notes I'm putting up here. Changing the doc when it's wrong
|
|
|
859 |
# but leaving grey area stuff up here.
|
|
|
860 |
#
|
|
|
861 |
# I would like to change Read/Write to Load/Dump below without
|
|
|
862 |
# changing the actual API names.
|
|
|
863 |
#
|
|
|
864 |
# It might be better to put Load/Dump API in the SYNOPSIS instead of the
|
|
|
865 |
# dubious OO API.
|
|
|
866 |
#
|
|
|
867 |
# null and bool explanations may be outdated.
|
|
|
868 |
|
|
|
869 |
__END__
|
|
|
870 |
|
|
|
871 |
=pod
|
|
|
872 |
|
|
|
873 |
=head1 NAME
|
|
|
874 |
|
|
|
875 |
YAML::Tiny - Read/Write YAML files with as little code as possible
|
|
|
876 |
|
|
|
877 |
=head1 VERSION
|
|
|
878 |
|
|
|
879 |
version 1.73
|
|
|
880 |
|
|
|
881 |
=head1 PREAMBLE
|
|
|
882 |
|
|
|
883 |
The YAML specification is huge. Really, B<really> huge. It contains all the
|
|
|
884 |
functionality of XML, except with flexibility and choice, which makes it
|
|
|
885 |
easier to read, but with a formal specification that is more complex than
|
|
|
886 |
XML.
|
|
|
887 |
|
|
|
888 |
The original pure-Perl implementation L<YAML> costs just over 4 megabytes
|
|
|
889 |
of memory to load. Just like with Windows F<.ini> files (3 meg to load) and
|
|
|
890 |
CSS (3.5 meg to load) the situation is just asking for a B<YAML::Tiny>
|
|
|
891 |
module, an incomplete but correct and usable subset of the functionality,
|
|
|
892 |
in as little code as possible.
|
|
|
893 |
|
|
|
894 |
Like the other C<::Tiny> modules, YAML::Tiny has no non-core dependencies,
|
|
|
895 |
does not require a compiler to install, is back-compatible to Perl v5.8.1,
|
|
|
896 |
and can be inlined into other modules if needed.
|
|
|
897 |
|
|
|
898 |
In exchange for this adding this extreme flexibility, it provides support
|
|
|
899 |
for only a limited subset of YAML. But the subset supported contains most
|
|
|
900 |
of the features for the more common uses of YAML.
|
|
|
901 |
|
|
|
902 |
=head1 SYNOPSIS
|
|
|
903 |
|
|
|
904 |
Assuming F<file.yml> like this:
|
|
|
905 |
|
|
|
906 |
---
|
|
|
907 |
rootproperty: blah
|
|
|
908 |
section:
|
|
|
909 |
one: two
|
|
|
910 |
three: four
|
|
|
911 |
Foo: Bar
|
|
|
912 |
empty: ~
|
|
|
913 |
|
|
|
914 |
|
|
|
915 |
Read and write F<file.yml> like this:
|
|
|
916 |
|
|
|
917 |
use YAML::Tiny;
|
|
|
918 |
|
|
|
919 |
# Open the config
|
|
|
920 |
my $yaml = YAML::Tiny->read( 'file.yml' );
|
|
|
921 |
|
|
|
922 |
# Get a reference to the first document
|
|
|
923 |
my $config = $yaml->[0];
|
|
|
924 |
|
|
|
925 |
# Or read properties directly
|
|
|
926 |
my $root = $yaml->[0]->{rootproperty};
|
|
|
927 |
my $one = $yaml->[0]->{section}->{one};
|
|
|
928 |
my $Foo = $yaml->[0]->{section}->{Foo};
|
|
|
929 |
|
|
|
930 |
# Change data directly
|
|
|
931 |
$yaml->[0]->{newsection} = { this => 'that' }; # Add a section
|
|
|
932 |
$yaml->[0]->{section}->{Foo} = 'Not Bar!'; # Change a value
|
|
|
933 |
delete $yaml->[0]->{section}; # Delete a value
|
|
|
934 |
|
|
|
935 |
# Save the document back to the file
|
|
|
936 |
$yaml->write( 'file.yml' );
|
|
|
937 |
|
|
|
938 |
To create a new YAML file from scratch:
|
|
|
939 |
|
|
|
940 |
# Create a new object with a single hashref document
|
|
|
941 |
my $yaml = YAML::Tiny->new( { wibble => "wobble" } );
|
|
|
942 |
|
|
|
943 |
# Add an arrayref document
|
|
|
944 |
push @$yaml, [ 'foo', 'bar', 'baz' ];
|
|
|
945 |
|
|
|
946 |
# Save both documents to a file
|
|
|
947 |
$yaml->write( 'data.yml' );
|
|
|
948 |
|
|
|
949 |
Then F<data.yml> will contain:
|
|
|
950 |
|
|
|
951 |
---
|
|
|
952 |
wibble: wobble
|
|
|
953 |
---
|
|
|
954 |
- foo
|
|
|
955 |
- bar
|
|
|
956 |
- baz
|
|
|
957 |
|
|
|
958 |
=head1 DESCRIPTION
|
|
|
959 |
|
|
|
960 |
B<YAML::Tiny> is a perl class for reading and writing YAML-style files,
|
|
|
961 |
written with as little code as possible, reducing load time and memory
|
|
|
962 |
overhead.
|
|
|
963 |
|
|
|
964 |
Most of the time it is accepted that Perl applications use a lot
|
|
|
965 |
of memory and modules. The B<::Tiny> family of modules is specifically
|
|
|
966 |
intended to provide an ultralight and zero-dependency alternative to
|
|
|
967 |
many more-thorough standard modules.
|
|
|
968 |
|
|
|
969 |
This module is primarily for reading human-written files (like simple
|
|
|
970 |
config files) and generating very simple human-readable files. Note that
|
|
|
971 |
I said B<human-readable> and not B<geek-readable>. The sort of files that
|
|
|
972 |
your average manager or secretary should be able to look at and make
|
|
|
973 |
sense of.
|
|
|
974 |
|
|
|
975 |
=for stopwords normalise
|
|
|
976 |
|
|
|
977 |
L<YAML::Tiny> does not generate comments, it won't necessarily preserve the
|
|
|
978 |
order of your hashes, and it will normalise if reading in and writing out
|
|
|
979 |
again.
|
|
|
980 |
|
|
|
981 |
It only supports a very basic subset of the full YAML specification.
|
|
|
982 |
|
|
|
983 |
=for stopwords embeddable
|
|
|
984 |
|
|
|
985 |
Usage is targeted at files like Perl's META.yml, for which a small and
|
|
|
986 |
easily-embeddable module is extremely attractive.
|
|
|
987 |
|
|
|
988 |
Features will only be added if they are human readable, and can be written
|
|
|
989 |
in a few lines of code. Please don't be offended if your request is
|
|
|
990 |
refused. Someone has to draw the line, and for YAML::Tiny that someone
|
|
|
991 |
is me.
|
|
|
992 |
|
|
|
993 |
If you need something with more power move up to L<YAML> (7 megabytes of
|
|
|
994 |
memory overhead) or L<YAML::XS> (6 megabytes memory overhead and requires
|
|
|
995 |
a C compiler).
|
|
|
996 |
|
|
|
997 |
To restate, L<YAML::Tiny> does B<not> preserve your comments, whitespace,
|
|
|
998 |
or the order of your YAML data. But it should round-trip from Perl
|
|
|
999 |
structure to file and back again just fine.
|
|
|
1000 |
|
|
|
1001 |
=head1 METHODS
|
|
|
1002 |
|
|
|
1003 |
=for Pod::Coverage HAVE_UTF8 refaddr
|
|
|
1004 |
|
|
|
1005 |
=head2 new
|
|
|
1006 |
|
|
|
1007 |
The constructor C<new> creates a C<YAML::Tiny> object as a blessed array
|
|
|
1008 |
reference. Any arguments provided are taken as separate documents
|
|
|
1009 |
to be serialized.
|
|
|
1010 |
|
|
|
1011 |
=head2 read $filename
|
|
|
1012 |
|
|
|
1013 |
The C<read> constructor reads a YAML file from a file name,
|
|
|
1014 |
and returns a new C<YAML::Tiny> object containing the parsed content.
|
|
|
1015 |
|
|
|
1016 |
Returns the object on success or throws an error on failure.
|
|
|
1017 |
|
|
|
1018 |
=head2 read_string $string;
|
|
|
1019 |
|
|
|
1020 |
The C<read_string> constructor reads YAML data from a character string, and
|
|
|
1021 |
returns a new C<YAML::Tiny> object containing the parsed content. If you have
|
|
|
1022 |
read the string from a file yourself, be sure that you have correctly decoded
|
|
|
1023 |
it into characters first.
|
|
|
1024 |
|
|
|
1025 |
Returns the object on success or throws an error on failure.
|
|
|
1026 |
|
|
|
1027 |
=head2 write $filename
|
|
|
1028 |
|
|
|
1029 |
The C<write> method generates the file content for the properties, and
|
|
|
1030 |
writes it to disk using UTF-8 encoding to the filename specified.
|
|
|
1031 |
|
|
|
1032 |
Returns true on success or throws an error on failure.
|
|
|
1033 |
|
|
|
1034 |
=head2 write_string
|
|
|
1035 |
|
|
|
1036 |
Generates the file content for the object and returns it as a character
|
|
|
1037 |
string. This may contain non-ASCII characters and should be encoded
|
|
|
1038 |
before writing it to a file.
|
|
|
1039 |
|
|
|
1040 |
Returns true on success or throws an error on failure.
|
|
|
1041 |
|
|
|
1042 |
=for stopwords errstr
|
|
|
1043 |
|
|
|
1044 |
=head2 errstr (DEPRECATED)
|
|
|
1045 |
|
|
|
1046 |
Prior to version 1.57, some errors were fatal and others were available only
|
|
|
1047 |
via the C<$YAML::Tiny::errstr> variable, which could be accessed via the
|
|
|
1048 |
C<errstr()> method.
|
|
|
1049 |
|
|
|
1050 |
Starting with version 1.57, all errors are fatal and throw exceptions.
|
|
|
1051 |
|
|
|
1052 |
The C<$errstr> variable is still set when exceptions are thrown, but
|
|
|
1053 |
C<$errstr> and the C<errstr()> method are deprecated and may be removed in a
|
|
|
1054 |
future release. The first use of C<errstr()> will issue a deprecation
|
|
|
1055 |
warning.
|
|
|
1056 |
|
|
|
1057 |
=head1 FUNCTIONS
|
|
|
1058 |
|
|
|
1059 |
YAML::Tiny implements a number of functions to add compatibility with
|
|
|
1060 |
the L<YAML> API. These should be a drop-in replacement.
|
|
|
1061 |
|
|
|
1062 |
=head2 Dump
|
|
|
1063 |
|
|
|
1064 |
my $string = Dump(list-of-Perl-data-structures);
|
|
|
1065 |
|
|
|
1066 |
Turn Perl data into YAML. This function works very much like
|
|
|
1067 |
Data::Dumper::Dumper().
|
|
|
1068 |
|
|
|
1069 |
It takes a list of Perl data structures and dumps them into a serialized
|
|
|
1070 |
form.
|
|
|
1071 |
|
|
|
1072 |
It returns a character string containing the YAML stream. Be sure to encode
|
|
|
1073 |
it as UTF-8 before serializing to a file or socket.
|
|
|
1074 |
|
|
|
1075 |
The structures can be references or plain scalars.
|
|
|
1076 |
|
|
|
1077 |
Dies on any error.
|
|
|
1078 |
|
|
|
1079 |
=head2 Load
|
|
|
1080 |
|
|
|
1081 |
my @data_structures = Load(string-containing-a-YAML-stream);
|
|
|
1082 |
|
|
|
1083 |
Turn YAML into Perl data. This is the opposite of Dump.
|
|
|
1084 |
|
|
|
1085 |
Just like L<Storable>'s thaw() function or the eval() function in relation
|
|
|
1086 |
to L<Data::Dumper>.
|
|
|
1087 |
|
|
|
1088 |
It parses a character string containing a valid YAML stream into a list of
|
|
|
1089 |
Perl data structures representing the individual YAML documents. Be sure to
|
|
|
1090 |
decode the character string correctly if the string came from a file or
|
|
|
1091 |
socket.
|
|
|
1092 |
|
|
|
1093 |
my $last_data_structure = Load(string-containing-a-YAML-stream);
|
|
|
1094 |
|
|
|
1095 |
For consistency with YAML.pm, when Load is called in scalar context, it
|
|
|
1096 |
returns the data structure corresponding to the last of the YAML documents
|
|
|
1097 |
found in the input stream.
|
|
|
1098 |
|
|
|
1099 |
Dies on any error.
|
|
|
1100 |
|
|
|
1101 |
=head2 freeze() and thaw()
|
|
|
1102 |
|
|
|
1103 |
Aliases to Dump() and Load() for L<Storable> fans. This will also allow
|
|
|
1104 |
YAML::Tiny to be plugged directly into modules like POE.pm, that use the
|
|
|
1105 |
freeze/thaw API for internal serialization.
|
|
|
1106 |
|
|
|
1107 |
=head2 DumpFile(filepath, list)
|
|
|
1108 |
|
|
|
1109 |
Writes the YAML stream to a file with UTF-8 encoding instead of just
|
|
|
1110 |
returning a string.
|
|
|
1111 |
|
|
|
1112 |
Dies on any error.
|
|
|
1113 |
|
|
|
1114 |
=head2 LoadFile(filepath)
|
|
|
1115 |
|
|
|
1116 |
Reads the YAML stream from a UTF-8 encoded file instead of a string.
|
|
|
1117 |
|
|
|
1118 |
Dies on any error.
|
|
|
1119 |
|
|
|
1120 |
=head1 YAML TINY SPECIFICATION
|
|
|
1121 |
|
|
|
1122 |
This section of the documentation provides a specification for "YAML Tiny",
|
|
|
1123 |
a subset of the YAML specification.
|
|
|
1124 |
|
|
|
1125 |
It is based on and described comparatively to the YAML 1.1 Working Draft
|
|
|
1126 |
2004-12-28 specification, located at L<http://yaml.org/spec/current.html>.
|
|
|
1127 |
|
|
|
1128 |
Terminology and chapter numbers are based on that specification.
|
|
|
1129 |
|
|
|
1130 |
=head2 1. Introduction and Goals
|
|
|
1131 |
|
|
|
1132 |
The purpose of the YAML Tiny specification is to describe a useful subset
|
|
|
1133 |
of the YAML specification that can be used for typical document-oriented
|
|
|
1134 |
use cases such as configuration files and simple data structure dumps.
|
|
|
1135 |
|
|
|
1136 |
=for stopwords extensibility
|
|
|
1137 |
|
|
|
1138 |
Many specification elements that add flexibility or extensibility are
|
|
|
1139 |
intentionally removed, as is support for complex data structures, class
|
|
|
1140 |
and object-orientation.
|
|
|
1141 |
|
|
|
1142 |
In general, the YAML Tiny language targets only those data structures
|
|
|
1143 |
available in JSON, with the additional limitation that only simple keys
|
|
|
1144 |
are supported.
|
|
|
1145 |
|
|
|
1146 |
As a result, all possible YAML Tiny documents should be able to be
|
|
|
1147 |
transformed into an equivalent JSON document, although the reverse is
|
|
|
1148 |
not necessarily true (but will be true in simple cases).
|
|
|
1149 |
|
|
|
1150 |
=for stopwords PCRE
|
|
|
1151 |
|
|
|
1152 |
As a result of these simplifications the YAML Tiny specification should
|
|
|
1153 |
be implementable in a (relatively) small amount of code in any language
|
|
|
1154 |
that supports Perl Compatible Regular Expressions (PCRE).
|
|
|
1155 |
|
|
|
1156 |
=head2 2. Introduction
|
|
|
1157 |
|
|
|
1158 |
YAML Tiny supports three data structures. These are scalars (in a variety
|
|
|
1159 |
of forms), block-form sequences and block-form mappings. Flow-style
|
|
|
1160 |
sequences and mappings are not supported, with some minor exceptions
|
|
|
1161 |
detailed later.
|
|
|
1162 |
|
|
|
1163 |
The use of three dashes "---" to indicate the start of a new document is
|
|
|
1164 |
supported, and multiple documents per file/stream is allowed.
|
|
|
1165 |
|
|
|
1166 |
Both line and inline comments are supported.
|
|
|
1167 |
|
|
|
1168 |
Scalars are supported via the plain style, single quote and double quote,
|
|
|
1169 |
as well as literal-style and folded-style multi-line scalars.
|
|
|
1170 |
|
|
|
1171 |
The use of explicit tags is not supported.
|
|
|
1172 |
|
|
|
1173 |
The use of "null" type scalars is supported via the ~ character.
|
|
|
1174 |
|
|
|
1175 |
The use of "bool" type scalars is not supported.
|
|
|
1176 |
|
|
|
1177 |
=for stopwords serializer
|
|
|
1178 |
|
|
|
1179 |
However, serializer implementations should take care to explicitly escape
|
|
|
1180 |
strings that match a "bool" keyword in the following set to prevent other
|
|
|
1181 |
implementations that do support "bool" accidentally reading a string as a
|
|
|
1182 |
boolean
|
|
|
1183 |
|
|
|
1184 |
y|Y|yes|Yes|YES|n|N|no|No|NO
|
|
|
1185 |
|true|True|TRUE|false|False|FALSE
|
|
|
1186 |
|on|On|ON|off|Off|OFF
|
|
|
1187 |
|
|
|
1188 |
The use of anchors and aliases is not supported.
|
|
|
1189 |
|
|
|
1190 |
The use of directives is supported only for the %YAML directive.
|
|
|
1191 |
|
|
|
1192 |
=head2 3. Processing YAML Tiny Information
|
|
|
1193 |
|
|
|
1194 |
B<Processes>
|
|
|
1195 |
|
|
|
1196 |
=for stopwords deserialization
|
|
|
1197 |
|
|
|
1198 |
The YAML specification dictates three-phase serialization and three-phase
|
|
|
1199 |
deserialization.
|
|
|
1200 |
|
|
|
1201 |
The YAML Tiny specification does not mandate any particular methodology
|
|
|
1202 |
or mechanism for parsing.
|
|
|
1203 |
|
|
|
1204 |
Any compliant parser is only required to parse a single document at a
|
|
|
1205 |
time. The ability to support streaming documents is optional and most
|
|
|
1206 |
likely non-typical.
|
|
|
1207 |
|
|
|
1208 |
=for stopwords acyclic
|
|
|
1209 |
|
|
|
1210 |
Because anchors and aliases are not supported, the resulting representation
|
|
|
1211 |
graph is thus directed but (unlike the main YAML specification) B<acyclic>.
|
|
|
1212 |
|
|
|
1213 |
Circular references/pointers are not possible, and any YAML Tiny serializer
|
|
|
1214 |
detecting a circular reference should error with an appropriate message.
|
|
|
1215 |
|
|
|
1216 |
B<Presentation Stream>
|
|
|
1217 |
|
|
|
1218 |
=for stopwords unicode
|
|
|
1219 |
|
|
|
1220 |
YAML Tiny reads and write UTF-8 encoded files. Operations on strings expect
|
|
|
1221 |
or produce Unicode characters not UTF-8 encoded bytes.
|
|
|
1222 |
|
|
|
1223 |
B<Loading Failure Points>
|
|
|
1224 |
|
|
|
1225 |
=for stopwords modality
|
|
|
1226 |
|
|
|
1227 |
=for stopwords parsers
|
|
|
1228 |
|
|
|
1229 |
YAML Tiny parsers and emitters are not expected to recover from, or
|
|
|
1230 |
adapt to, errors. The specific error modality of any implementation is
|
|
|
1231 |
not dictated (return codes, exceptions, etc.) but is expected to be
|
|
|
1232 |
consistent.
|
|
|
1233 |
|
|
|
1234 |
=head2 4. Syntax
|
|
|
1235 |
|
|
|
1236 |
B<Character Set>
|
|
|
1237 |
|
|
|
1238 |
YAML Tiny streams are processed in memory as Unicode characters and
|
|
|
1239 |
read/written with UTF-8 encoding.
|
|
|
1240 |
|
|
|
1241 |
The escaping and unescaping of the 8-bit YAML escapes is required.
|
|
|
1242 |
|
|
|
1243 |
The escaping and unescaping of 16-bit and 32-bit YAML escapes is not
|
|
|
1244 |
required.
|
|
|
1245 |
|
|
|
1246 |
B<Indicator Characters>
|
|
|
1247 |
|
|
|
1248 |
Support for the "~" null/undefined indicator is required.
|
|
|
1249 |
|
|
|
1250 |
Implementations may represent this as appropriate for the underlying
|
|
|
1251 |
language.
|
|
|
1252 |
|
|
|
1253 |
Support for the "-" block sequence indicator is required.
|
|
|
1254 |
|
|
|
1255 |
Support for the "?" mapping key indicator is B<not> required.
|
|
|
1256 |
|
|
|
1257 |
Support for the ":" mapping value indicator is required.
|
|
|
1258 |
|
|
|
1259 |
Support for the "," flow collection indicator is B<not> required.
|
|
|
1260 |
|
|
|
1261 |
Support for the "[" flow sequence indicator is B<not> required, with
|
|
|
1262 |
one exception (detailed below).
|
|
|
1263 |
|
|
|
1264 |
Support for the "]" flow sequence indicator is B<not> required, with
|
|
|
1265 |
one exception (detailed below).
|
|
|
1266 |
|
|
|
1267 |
Support for the "{" flow mapping indicator is B<not> required, with
|
|
|
1268 |
one exception (detailed below).
|
|
|
1269 |
|
|
|
1270 |
Support for the "}" flow mapping indicator is B<not> required, with
|
|
|
1271 |
one exception (detailed below).
|
|
|
1272 |
|
|
|
1273 |
Support for the "#" comment indicator is required.
|
|
|
1274 |
|
|
|
1275 |
Support for the "&" anchor indicator is B<not> required.
|
|
|
1276 |
|
|
|
1277 |
Support for the "*" alias indicator is B<not> required.
|
|
|
1278 |
|
|
|
1279 |
Support for the "!" tag indicator is B<not> required.
|
|
|
1280 |
|
|
|
1281 |
Support for the "|" literal block indicator is required.
|
|
|
1282 |
|
|
|
1283 |
Support for the ">" folded block indicator is required.
|
|
|
1284 |
|
|
|
1285 |
Support for the "'" single quote indicator is required.
|
|
|
1286 |
|
|
|
1287 |
Support for the """ double quote indicator is required.
|
|
|
1288 |
|
|
|
1289 |
Support for the "%" directive indicator is required, but only
|
|
|
1290 |
for the special case of a %YAML version directive before the
|
|
|
1291 |
"---" document header, or on the same line as the document header.
|
|
|
1292 |
|
|
|
1293 |
For example:
|
|
|
1294 |
|
|
|
1295 |
%YAML 1.1
|
|
|
1296 |
---
|
|
|
1297 |
- A sequence with a single element
|
|
|
1298 |
|
|
|
1299 |
Special Exception:
|
|
|
1300 |
|
|
|
1301 |
To provide the ability to support empty sequences
|
|
|
1302 |
and mappings, support for the constructs [] (empty sequence) and {}
|
|
|
1303 |
(empty mapping) are required.
|
|
|
1304 |
|
|
|
1305 |
For example,
|
|
|
1306 |
|
|
|
1307 |
%YAML 1.1
|
|
|
1308 |
# A document consisting of only an empty mapping
|
|
|
1309 |
--- {}
|
|
|
1310 |
# A document consisting of only an empty sequence
|
|
|
1311 |
--- []
|
|
|
1312 |
# A document consisting of an empty mapping within a sequence
|
|
|
1313 |
- foo
|
|
|
1314 |
- {}
|
|
|
1315 |
- bar
|
|
|
1316 |
|
|
|
1317 |
B<Syntax Primitives>
|
|
|
1318 |
|
|
|
1319 |
Other than the empty sequence and mapping cases described above, YAML Tiny
|
|
|
1320 |
supports only the indentation-based block-style group of contexts.
|
|
|
1321 |
|
|
|
1322 |
All five scalar contexts are supported.
|
|
|
1323 |
|
|
|
1324 |
Indentation spaces work as per the YAML specification in all cases.
|
|
|
1325 |
|
|
|
1326 |
Comments work as per the YAML specification in all simple cases.
|
|
|
1327 |
Support for indented multi-line comments is B<not> required.
|
|
|
1328 |
|
|
|
1329 |
Separation spaces work as per the YAML specification in all cases.
|
|
|
1330 |
|
|
|
1331 |
B<YAML Tiny Character Stream>
|
|
|
1332 |
|
|
|
1333 |
The only directive supported by the YAML Tiny specification is the
|
|
|
1334 |
%YAML language/version identifier. Although detected, this directive
|
|
|
1335 |
will have no control over the parsing itself.
|
|
|
1336 |
|
|
|
1337 |
=for stopwords recognise
|
|
|
1338 |
|
|
|
1339 |
The parser must recognise both the YAML 1.0 and YAML 1.1+ formatting
|
|
|
1340 |
of this directive (as well as the commented form, although no explicit
|
|
|
1341 |
code should be needed to deal with this case, being a comment anyway)
|
|
|
1342 |
|
|
|
1343 |
That is, all of the following should be supported.
|
|
|
1344 |
|
|
|
1345 |
--- #YAML:1.0
|
|
|
1346 |
- foo
|
|
|
1347 |
|
|
|
1348 |
%YAML:1.0
|
|
|
1349 |
---
|
|
|
1350 |
- foo
|
|
|
1351 |
|
|
|
1352 |
% YAML 1.1
|
|
|
1353 |
---
|
|
|
1354 |
- foo
|
|
|
1355 |
|
|
|
1356 |
Support for the %TAG directive is B<not> required.
|
|
|
1357 |
|
|
|
1358 |
Support for additional directives is B<not> required.
|
|
|
1359 |
|
|
|
1360 |
Support for the document boundary marker "---" is required.
|
|
|
1361 |
|
|
|
1362 |
Support for the document boundary market "..." is B<not> required.
|
|
|
1363 |
|
|
|
1364 |
If necessary, a document boundary should simply by indicated with a
|
|
|
1365 |
"---" marker, with not preceding "..." marker.
|
|
|
1366 |
|
|
|
1367 |
Support for empty streams (containing no documents) is required.
|
|
|
1368 |
|
|
|
1369 |
Support for implicit document starts is required.
|
|
|
1370 |
|
|
|
1371 |
That is, the following must be equivalent.
|
|
|
1372 |
|
|
|
1373 |
# Full form
|
|
|
1374 |
%YAML 1.1
|
|
|
1375 |
---
|
|
|
1376 |
foo: bar
|
|
|
1377 |
|
|
|
1378 |
# Implicit form
|
|
|
1379 |
foo: bar
|
|
|
1380 |
|
|
|
1381 |
B<Nodes>
|
|
|
1382 |
|
|
|
1383 |
Support for nodes optional anchor and tag properties is B<not> required.
|
|
|
1384 |
|
|
|
1385 |
Support for node anchors is B<not> required.
|
|
|
1386 |
|
|
|
1387 |
Support for node tags is B<not> required.
|
|
|
1388 |
|
|
|
1389 |
Support for alias nodes is B<not> required.
|
|
|
1390 |
|
|
|
1391 |
Support for flow nodes is B<not> required.
|
|
|
1392 |
|
|
|
1393 |
Support for block nodes is required.
|
|
|
1394 |
|
|
|
1395 |
B<Scalar Styles>
|
|
|
1396 |
|
|
|
1397 |
Support for all five scalar styles is required as per the YAML
|
|
|
1398 |
specification, although support for quoted scalars spanning more
|
|
|
1399 |
than one line is B<not> required.
|
|
|
1400 |
|
|
|
1401 |
Support for multi-line scalar documents starting on the header
|
|
|
1402 |
is not required.
|
|
|
1403 |
|
|
|
1404 |
Support for the chomping indicators on multi-line scalar styles
|
|
|
1405 |
is required.
|
|
|
1406 |
|
|
|
1407 |
B<Collection Styles>
|
|
|
1408 |
|
|
|
1409 |
Support for block-style sequences is required.
|
|
|
1410 |
|
|
|
1411 |
Support for flow-style sequences is B<not> required.
|
|
|
1412 |
|
|
|
1413 |
Support for block-style mappings is required.
|
|
|
1414 |
|
|
|
1415 |
Support for flow-style mappings is B<not> required.
|
|
|
1416 |
|
|
|
1417 |
Both sequences and mappings should be able to be arbitrarily
|
|
|
1418 |
nested.
|
|
|
1419 |
|
|
|
1420 |
Support for plain-style mapping keys is required.
|
|
|
1421 |
|
|
|
1422 |
Support for quoted keys in mappings is B<not> required.
|
|
|
1423 |
|
|
|
1424 |
Support for "?"-indicated explicit keys is B<not> required.
|
|
|
1425 |
|
|
|
1426 |
=for stopwords endeth
|
|
|
1427 |
|
|
|
1428 |
Here endeth the specification.
|
|
|
1429 |
|
|
|
1430 |
=head2 Additional Perl-Specific Notes
|
|
|
1431 |
|
|
|
1432 |
For some Perl applications, it's important to know if you really have a
|
|
|
1433 |
number and not a string.
|
|
|
1434 |
|
|
|
1435 |
That is, in some contexts is important that 3 the number is distinctive
|
|
|
1436 |
from "3" the string.
|
|
|
1437 |
|
|
|
1438 |
Because even Perl itself is not trivially able to understand the difference
|
|
|
1439 |
(certainly without XS-based modules) Perl implementations of the YAML Tiny
|
|
|
1440 |
specification are not required to retain the distinctiveness of 3 vs "3".
|
|
|
1441 |
|
|
|
1442 |
=head1 SUPPORT
|
|
|
1443 |
|
|
|
1444 |
Bugs should be reported via the CPAN bug tracker at
|
|
|
1445 |
|
|
|
1446 |
L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=YAML-Tiny>
|
|
|
1447 |
|
|
|
1448 |
=begin html
|
|
|
1449 |
|
|
|
1450 |
For other issues, or commercial enhancement or support, please contact
|
|
|
1451 |
<a href="http://ali.as/">Adam Kennedy</a> directly.
|
|
|
1452 |
|
|
|
1453 |
=end html
|
|
|
1454 |
|
|
|
1455 |
=head1 AUTHOR
|
|
|
1456 |
|
|
|
1457 |
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
|
|
|
1458 |
|
|
|
1459 |
=head1 SEE ALSO
|
|
|
1460 |
|
|
|
1461 |
=over 4
|
|
|
1462 |
|
|
|
1463 |
=item * L<YAML>
|
|
|
1464 |
|
|
|
1465 |
=item * L<YAML::Syck>
|
|
|
1466 |
|
|
|
1467 |
=item * L<Config::Tiny>
|
|
|
1468 |
|
|
|
1469 |
=item * L<CSS::Tiny>
|
|
|
1470 |
|
|
|
1471 |
=item * L<http://use.perl.org/use.perl.org/_Alias/journal/29427.html>
|
|
|
1472 |
|
|
|
1473 |
=item * L<http://ali.as/>
|
|
|
1474 |
|
|
|
1475 |
=back
|
|
|
1476 |
|
|
|
1477 |
=head1 COPYRIGHT
|
|
|
1478 |
|
|
|
1479 |
Copyright 2006 - 2013 Adam Kennedy.
|
|
|
1480 |
|
|
|
1481 |
This program is free software; you can redistribute
|
|
|
1482 |
it and/or modify it under the same terms as Perl itself.
|
|
|
1483 |
|
|
|
1484 |
The full text of the license can be found in the
|
|
|
1485 |
LICENSE file included with this module.
|
|
|
1486 |
|
|
|
1487 |
=cut
|