BEGIN
{
use
vars
qw( $VERSION $DEBUG $KEY_OBJECT )
;
'eq'
=>
sub
{ _obj_eq(
@_
) },
'ne'
=>
sub
{ !_obj_eq(
@_
) },
'<'
=>
sub
{ _obj_comp(
@_
,
'<'
) },
'>'
=>
sub
{ _obj_comp(
@_
,
'>'
) },
'<='
=>
sub
{ _obj_comp(
@_
,
'<='
) },
'>='
=>
sub
{ _obj_comp(
@_
,
'>='
) },
'=='
=>
sub
{ _obj_comp(
@_
,
'>='
) },
'!='
=>
sub
{ _obj_comp(
@_
,
'>='
) },
'lt'
=>
sub
{ _obj_comp(
@_
,
'lt'
) },
'gt'
=>
sub
{ _obj_comp(
@_
,
'gt'
) },
'le'
=>
sub
{ _obj_comp(
@_
,
'le'
) },
'ge'
=>
sub
{ _obj_comp(
@_
,
'ge'
) },
'bool'
=>
sub
{
$_
[0]},
fallback
=> 1,
);
our
$KEY_OBJECT
= 0;
our
(
$VERSION
) =
'v1.4.0'
;
};
no
warnings
'redefine'
;
sub
new
{
my
$that
=
shift
(
@_
);
my
$class
=
ref
(
$that
) ||
$that
;
my
%hash
= ();
my
$obj
=
tie
(
%hash
,
'Module::Generic::TieHash'
, {
debug
=>
$DEBUG
,
enable
=> 0,
key_object
=>
$KEY_OBJECT
,
});
my
$self
=
bless
( \
%hash
=>
$class
);
$obj
->enable(1);
if
(
scalar
(
@_
) == 1 )
{
my
$data
=
shift
(
@_
);
return
(
$that
->error(
"I was expecting an hash, but instead got '"
, (
$data
// '
undef
' ), "'
." ) )
if
( Scalar::Util::reftype(
$data
//
''
) ne
'HASH'
);
my
$tied
=
tied
(
%$data
);
return
(
$that
->error(
"Hash provided is already tied to "
,
ref
(
$tied
),
" and our package $class cannot use it, or it would disrupt the tie."
) )
if
(
$tied
);
my
@keys
= CORE::
keys
(
%$data
);
@hash
{
@keys
} =
@$data
{
@keys
};
}
elsif
(
scalar
(
@_
) > 1 &&
!(
@_
% 2 ) )
{
while
(
@_
)
{
$hash
{
shift
(
@_
) } =
shift
(
@_
);
}
}
elsif
(
scalar
(
@_
) )
{
return
(
$self
->error(
"Odd number ("
,
scalar
(
@_
),
") of hash keys and values provided."
) );
}
$obj
->enable(0);
$self
->SUPER::init(
@_
);
$obj
->enable(1);
return
(
$self
);
}
sub
as_hash
{
my
$self
=
shift
(
@_
);
if
(
@_
)
{
my
$opts
=
$self
->_get_args_as_hash(
@_
);
if
(
$opts
->{strict} )
{
my
$ref
= {
%$self
};
return
(
$ref
);
}
}
return
(
$self
);
}
sub
as_json {
return
(
shift
->json(
@_
)->
scalar
); }
sub
as_string {
return
(
shift
->
dump
); }
sub
chomp
{
my
$self
= CORE::
shift
(
@_
);
CORE::
chomp
(
%$self
);
return
(
$self
);
}
sub
clone
{
my
$self
=
shift
(
@_
);
$self
->_tie_object->enable(0);
my
$data
=
$self
->{data};
my
$clone
= Clone::clone(
$data
);
$self
->_tie_object->enable(1);
return
(
$self
->new(
$clone
) );
}
sub
debug {
return
(
shift
->_internal(
'debug'
,
'_set_get_number'
,
@_
) ); }
sub
defined
{ CORE::
defined
(
$_
[0]->{
$_
[1] } ); }
sub
delete
{
return
( CORE::
delete
(
shift
->{
shift
(
@_
) } ) ); }
sub
dump
{
my
$self
=
shift
(
@_
);
return
(
$self
->_dumper(
$self
) );
}
sub
each
{
my
$self
=
shift
(
@_
);
my
$code
=
shift
(
@_
) ||
return
(
$self
->error(
"No subroutine callback as provided for each"
) );
return
(
$self
->error(
"I was expecting a reference to a subroutine for the callback to each, but got '$code' instead."
) )
if
(
ref
(
$code
) ne
'CODE'
);
while
(
my
(
$k
,
$v
) = CORE::
each
(
%$self
) )
{
CORE::
defined
(
$code
->(
$k
,
$v
) ) || CORE::
last
;
}
return
(
$self
);
}
sub
exists
{
return
( CORE::
exists
(
shift
->{
shift
(
@_
) } ) ); }
sub
for
{
return
(
shift
->
foreach
(
@_
) ); }
sub
foreach
{
my
$self
=
shift
(
@_
);
my
$code
=
shift
(
@_
) ||
return
(
$self
->error(
"No subroutine callback as provided for each"
) );
return
(
$self
->error(
"I was expecting a reference to a subroutine for the callback to each, but got '$code' instead."
) )
if
(
ref
(
$code
) ne
'CODE'
);
CORE::
foreach
my
$k
( CORE::
keys
(
%$self
) )
{
local
$_
=
$self
->{
$k
};
CORE::
defined
(
$code
->(
$k
,
$self
->{
$k
} ) ) || CORE::
last
;
}
return
(
$self
);
}
sub
get {
return
(
$_
[0]->{
$_
[1] } ); }
sub
has
{
return
(
shift
->
exists
(
@_
) ); }
sub
is_empty {
return
(
scalar
( CORE::
keys
( %{
$_
[0]} ) ) ? 0 : 1 ); }
sub
json
{
my
$self
=
shift
(
@_
);
my
$opts
= {};
if
(
ref
(
$_
[-1] ) eq
'HASH'
)
{
$opts
=
pop
(
@_
);
}
elsif
(
@_
&& !(
@_
% 2 ) )
{
$opts
= {
@_
};
}
$self
->_tie_object->enable(0);
my
$data
=
$self
->{data};
if
( CORE::
exists
(
$opts
->{order} ) )
{
$opts
->{canonical} = CORE::
delete
(
$opts
->{order} );
}
elsif
( CORE::
exists
(
$opts
->{ordered} ) )
{
$opts
->{canonical} = CORE::
delete
(
$opts
->{ordered} );
}
elsif
( CORE::
exists
(
$opts
->{
sort
} ) )
{
$opts
->{canonical} = CORE::
delete
(
$opts
->{
sort
} );
}
elsif
( CORE::
exists
(
$opts
->{sorted} ) )
{
$opts
->{canonical} = CORE::
delete
(
$opts
->{sorted} );
}
if
( !CORE::
exists
(
$opts
->{canonical} ) &&
$opts
->{pretty} )
{
$opts
->{canonical} = 1;
}
if
( !CORE::
exists
(
$opts
->{indent} ) &&
$opts
->{pretty} )
{
$opts
->{indent} = 1;
}
if
( !CORE::
exists
(
$opts
->{relaxed} ) &&
$opts
->{pretty} )
{
$opts
->{relaxed} = 1;
}
my
$j
= JSON->new->allow_nonref;
my
@keys
=
qw(
ascii latin1 utf8 pretty indent space_before space_after relaxed
canonical allow_nonref allow_unknown allow_blessed convert_blessed allow_tags
boolean_values filter_json_object filter_json_single_key_object max_depth max_size
)
;
foreach
my
$k
(
@keys
)
{
next
unless
( CORE::
exists
(
$opts
->{
$k
} ) );
my
$code
=
$j
->can(
$k
);
if
(
defined
(
$code
) )
{
$code
->(
$j
,
$opts
->{
$k
} );
}
}
my
$json
=
$j
->encode(
$data
);
$self
->_tie_object->enable(1);
return
( Module::Generic::Scalar->new(
$json
) );
}
sub
key_object
{
my
$self
=
shift
(
@_
);
if
(
@_
)
{
$self
->_tie_object->key_object(
shift
(
@_
) );
}
return
(
$self
->_tie_object->key_object );
}
sub
keys
{
my
$self
=
shift
(
@_
);
$self
->_tie_object->enable(1);
return
( Module::Generic::Array->new( [ CORE::
keys
(
%$self
) ] ) );
}
sub
length
{
return
( Module::Generic::Number->new( CORE::
scalar
( CORE::
keys
( %{
$_
[0]} ) ) ) ); }
sub
map
{
my
$self
=
shift
(
@_
);
my
$code
= CORE::
shift
(
@_
);
return
if
(
ref
(
$code
) ne
'CODE'
);
return
( CORE::
map
(
$code
->(
$_
,
$self
->{
$_
} ), CORE::
keys
(
%$self
) ) );
}
sub
map_array
{
my
$self
=
shift
(
@_
);
my
$code
= CORE::
shift
(
@_
);
return
if
(
ref
(
$code
) ne
'CODE'
);
return
( Module::Generic::Array->new( [CORE::
map
(
$code
->(
$_
,
$self
->{
$_
} ), CORE::
keys
(
%$self
) )] ) );
}
sub
map_hash
{
my
$self
=
shift
(
@_
);
my
$code
= CORE::
shift
(
@_
);
return
if
(
ref
(
$code
) ne
'CODE'
);
return
(
$self
->new( {CORE::
map
(
$code
->(
$_
,
$self
->{
$_
} ), CORE::
keys
(
%$self
) )} ) );
}
sub
merge
{
my
$self
=
shift
(
@_
);
my
$hash
= {};
$hash
=
shift
(
@_
);
return
(
$self
->error(
"No valid hash provided."
) )
if
( !
$hash
|| ( Scalar::Util::reftype(
$hash
) //
''
) ne
'HASH'
);
my
$opts
= {};
$opts
=
pop
(
@_
)
if
(
@_
&&
ref
(
$_
[-1] ) eq
'HASH'
);
$opts
->{overwrite} = 1
unless
( CORE::
exists
(
$opts
->{overwrite} ) );
$self
->_tie_object->enable(0);
my
$data
=
$self
->{data};
my
$seen
= {};
my
$copy
;
$copy
=
sub
{
my
$this
=
shift
(
@_
);
my
$to
=
shift
(
@_
);
my
$p
= {};
$p
=
shift
(
@_
)
if
(
@_
&&
ref
(
$_
[-1] ) eq
'HASH'
);
CORE::
foreach
my
$k
( CORE::
keys
(
%$this
) )
{
next
if
( CORE::
exists
(
$to
->{
$k
} ) && !
$p
->{overwrite} );
if
(
ref
(
$this
->{
$k
} ) eq
'HASH'
||
( Scalar::Util::blessed(
$this
->{
$k
} ) &&
$this
->{
$k
}->isa(
'Module::Generic::Hash'
) ) )
{
my
$addr
= Scalar::Util::refaddr(
$this
->{
$k
} );
if
( CORE::
exists
(
$seen
->{
$addr
} ) )
{
$to
->{
$k
} =
$seen
->{
$addr
};
next
;
}
else
{
$to
->{
$k
} = {}
unless
( CORE::
defined
(
$to
->{
$k
} ) && ( Scalar::Util::reftype(
$to
->{
$k
} ) //
''
) eq
'HASH'
);
$copy
->(
$this
->{
$k
},
$to
->{
$k
} );
}
$seen
->{
$addr
} =
$this
->{
$k
};
}
else
{
$to
->{
$k
} =
$this
->{
$k
};
}
}
};
$copy
->(
$hash
,
$data
,
$opts
);
$self
->_tie_object->enable(1);
return
(
$self
);
}
sub
remove {
return
(
shift
->
delete
(
@_
) ); }
sub
reset
{ %{
$_
[0]} = () };
sub
set {
$_
[0]->{
$_
[1] } =
$_
[2]; }
sub
size {
return
(
shift
->
length
); }
sub
undef
{ %{
$_
[0]} = () };
sub
values
{
my
$self
=
shift
(
@_
);
my
$code
;
$code
=
shift
(
@_
)
if
(
@_
&&
ref
(
$_
[0] ) eq
'CODE'
);
my
$opts
= {};
$opts
=
pop
(
@_
)
if
( ( Scalar::Util::reftype(
$_
[-1] ) //
''
) eq
'HASH'
);
if
(
$code
)
{
if
(
$opts
->{
sort
} )
{
return
( Module::Generic::Array->new( [ CORE::
map
(
$code
->(
$_
), CORE::
sort
( CORE::
values
(
%$self
) ) ) ] ) );
}
else
{
return
( Module::Generic::Array->new( [ CORE::
map
(
$code
->(
$_
), CORE::
values
(
%$self
) ) ] ) );
}
}
else
{
if
(
$opts
->{
sort
} )
{
return
( Module::Generic::Array->new( [ CORE::
sort
( CORE::
values
(
%$self
) ) ] ) );
}
else
{
return
( Module::Generic::Array->new( [ CORE::
values
(
%$self
) ] ) );
}
}
}
sub
_dumper
{
my
$self
=
shift
(
@_
);
$self
->_tie_object->enable(0);
my
$data
=
$self
->{data};
my
$d
= Data::Dumper->new( [
$data
] );
$d
->Indent(1);
$d
->Useqq(1);
$d
->Terse(1);
$d
->Sortkeys(1);
$d
->Bless(
''
);
my
$str
=
$d
->Dump;
$self
->_tie_object->enable(1);
return
(
$str
);
}
sub
_internal
{
my
$self
=
shift
(
@_
);
my
$field
=
shift
(
@_
);
my
$meth
=
shift
(
@_
);
$self
->_tie_object->enable(0);
my
(
@resA
,
$resB
);
if
(
wantarray
)
{
@resA
=
$self
->
$meth
(
$field
,
@_
);
}
else
{
$resB
=
$self
->
$meth
(
$field
,
@_
);
}
$self
->_tie_object->enable(1);
return
(
wantarray
?
@resA
:
$resB
);
}
sub
_obj_comp
{
my
(
$self
,
$other
,
$swap
,
$op
) =
@_
;
my
(
$lA
,
$lB
);
$lA
=
$self
->
length
;
if
( Scalar::Util::blessed(
$other
) &&
$other
->isa(
'Module::Generic::Hash'
) )
{
$lB
=
$other
->
length
;
}
elsif
(
$other
=~ /^
$RE
{num}{real}$/ )
{
$lB
=
$other
;
}
else
{
return
;
}
my
$expr
=
$swap
?
"$lB $op $lA"
:
"$lA $op $lB"
;
return
(
eval
(
$expr
) );
}
sub
_printer {
return
(
shift
->printer(
@_
) ); }
sub
_obj_eq
{
no
overloading;
my
$self
=
shift
(
@_
);
my
$other
=
shift
(
@_
);
my
$strA
=
$self
->_dumper(
$self
);
my
$strB
;
if
( Scalar::Util::blessed(
$other
) &&
$other
->isa(
'Module::Generic::Hash'
) )
{
$strB
=
$other
->
dump
;
}
elsif
( ( Scalar::Util::reftype(
$other
) //
''
) eq
'HASH'
)
{
$strB
=
$self
->_dumper(
$other
)
}
else
{
return
(0);
}
return
(
$strA
eq
$strB
);
}
sub
_tie_object
{
my
$self
=
shift
(
@_
);
return
(
tied
(
%$self
) );
}
sub
FREEZE
{
my
$self
= CORE::
shift
(
@_
);
my
$serialiser
= CORE::
shift
(
@_
) //
''
;
my
$class
= CORE::
ref
(
$self
);
my
$clone
=
$self
->clone;
$clone
->_tie_object->enable(0);
my
%data
= %{
$clone
->{data}};
$clone
->_tie_object->enable(1);
CORE::
return
( [
$class
, \
%data
] )
if
(
$serialiser
eq
'Sereal'
&& Sereal::Encoder->VERSION <= version->parse(
'4.023'
) );
CORE::
return
(
$class
, \
%data
);
}
sub
STORABLE_freeze { CORE::
return
( CORE::
shift
->FREEZE(
@_
) ); }
sub
STORABLE_thaw { CORE::
return
( CORE::
shift
->THAW(
@_
) ); }
sub
THAW
{
my
(
$self
,
undef
,
@args
) =
@_
;
my
$ref
= ( CORE::
scalar
(
@args
) == 1 && CORE::
ref
(
$args
[0] ) eq
'ARRAY'
) ? CORE::
shift
(
@args
) : \
@args
;
my
$class
= ( CORE::
defined
(
$ref
) && CORE::
ref
(
$ref
) eq
'ARRAY'
&& CORE::
scalar
(
@$ref
) > 1 ) ? CORE::
shift
(
@$ref
) : ( CORE::
ref
(
$self
) ||
$self
);
my
$hash
= CORE::
ref
(
$ref
) eq
'ARRAY'
? CORE::
shift
(
@$ref
) : {};
my
$new
;
if
( CORE::
ref
(
$self
) )
{
foreach
( CORE::
keys
(
%$hash
) )
{
$self
->{
$_
} = CORE::
delete
(
$hash
->{
$_
} );
}
$new
=
$self
;
}
else
{
$new
=
$class
->new(
$hash
);
}
CORE::
return
(
$new
);
}
sub
TO_JSON
{
my
$self
= CORE::
shift
(
@_
);
my
$ref
= {
%$self
};
CORE::
return
(
$ref
);
}
1;