use
constant
BUNDLED_PATH
=> path(path(__FILE__)->dirname,
'cache'
)->to_string;
use
constant
CASE_TOLERANT
=> File::Spec->case_tolerant;
die
$@
unless
eval
q(package JSON::Validator::Exception; use Mojo::Base 'Mojo::Exception'; 1)
;
has
cache_paths
=>
sub
{ [
split
(/:/,
$ENV
{JSON_VALIDATOR_CACHE_PATH} ||
''
), BUNDLED_PATH] };
has
schemas
=>
sub
{ +{} };
has
ua
=>
sub
{
my
$ua
= Mojo::UserAgent->new;
$ua
->proxy->detect;
return
$ua
->max_redirects(3);
};
sub
add {
my
(
$self
,
$id
,
$schema
) =
@_
;
$id
=~ s!(.)
$self
->schemas->{
$id
} =
$schema
;
return
$id
;
}
sub
exists
{
my
(
$self
,
$id
) =
@_
;
return
undef
unless
defined
$id
;
$id
=~ s!(.)
return
$self
->schemas->{
$id
} &&
$id
;
}
sub
get {
my
(
$self
,
$id
) =
@_
;
return
undef
unless
defined
$id
;
$id
=~ s!(.)
return
$self
->schemas->{
$id
};
}
sub
load {
return
$_
[0]->_load_from_url(
$_
[1])
||
$_
[0]->_load_from_data(
$_
[1])
||
$_
[0]->_load_from_text(
$_
[1])
||
$_
[0]->_load_from_file(
$_
[1])
||
$_
[0]->_load_from_app(
$_
[1])
||
$_
[0]->get(
$_
[1])
|| _raise(
"Unable to load schema $_[1]"
);
}
sub
_load_from_app {
return
undef
unless
$_
[1] =~ m!^/!;
my
(
$self
,
$url
,
$id
) =
@_
;
return
undef
unless
$self
->ua->server->app;
return
$id
if
$id
=
$self
->
exists
(
$url
);
my
$tx
=
$self
->ua->get(
$url
);
my
$err
=
$tx
->error &&
$tx
->error->{message};
_raise(
$err
)
if
$err
;
return
$self
->add(
$url
=> _parse(
$tx
->res->body));
}
sub
_load_from_data {
return
undef
unless
$_
[1] =~ m!^data://([^/]*)/(.*)!;
my
(
$self
,
$url
,
$id
) =
@_
;
return
$id
if
$id
=
$self
->
exists
(
$url
);
my
(
$class
,
$file
) = ($1, $2);
my
$text
= data_section
$class
,
$file
, {
encoding
=>
'UTF-8'
};
_raise(
"Could not find $url"
)
unless
$text
;
return
$self
->add(
$url
=> _parse(
$text
));
}
sub
_load_from_file {
my
(
$self
,
$file
) =
@_
;
$file
=~ s!
$file
= path(
split
'/'
, url_unescape
$file
);
return
undef
unless
-e
$file
;
$file
=
$file
->realpath;
my
$id
= Mojo::URL->new->scheme(
'file'
)->host(
''
)->path(CASE_TOLERANT ?
lc
$file
:
"$file"
);
return
$self
->
exists
(
$id
) ||
$self
->add(
$id
=> _parse(
$file
->slurp));
}
sub
_load_from_text {
my
(
$self
,
$text
) =
@_
;
my
$is_scalar_ref
=
ref
$text
eq
'SCALAR'
;
return
undef
unless
$is_scalar_ref
or
$text
=~ m!^\s*(?:---|\{)!s;
my
$id
=
sprintf
'urn:text:%s'
, Mojo::Util::md5_sum(
$is_scalar_ref
?
$$text
:
$text
);
return
$self
->
exists
(
$id
) ||
$self
->add(
$id
=> _parse(
$is_scalar_ref
?
$$text
:
$text
));
}
sub
_load_from_url {
return
undef
unless
$_
[1] =~ m!^https?://!;
my
(
$self
,
$url
,
$id
) =
@_
;
return
$id
if
$id
=
$self
->
exists
(
$url
);
$url
= Mojo::URL->new(
$url
)->fragment(
undef
);
return
$id
if
$id
=
$self
->
exists
(
$url
);
my
$cache_path
=
$self
->cache_paths->[0];
my
$cache_file
= Mojo::Util::md5_sum(
"$url"
);
for
(@{
$self
->cache_paths}) {
my
$path
= path
$_
,
$cache_file
;
return
$self
->add(
$url
=> _parse(
$path
->slurp))
if
-r
$path
;
}
my
$tx
=
$self
->ua->get(
$url
);
my
$err
=
$tx
->error &&
$tx
->error->{message};
_raise(
$err
)
if
$err
;
if
(
$cache_path
and
$cache_path
ne BUNDLED_PATH and -w
$cache_path
) {
$cache_file
= path
$cache_path
,
$cache_file
;
$cache_file
->spurt(
$tx
->res->body);
}
return
$self
->add(
$url
=> _parse(
$tx
->res->body));
}
sub
_parse {
return
Mojo::JSON::decode_json(
$_
[0])
if
$_
[0] =~ m!^\s*\{!s;
return
JSON::Validator::Util::_yaml_load(
$_
[0]);
}
sub
_raise {
die
JSON::Validator::Exception->new(
@_
)->trace }
1;