Reputation: 186
I have an arbitrary data structure and I'd like to treat it as an object. I get this as a response from a REST app. Example below. There are some modules on CPAN which promise to do this. Data::Object looks best to me, but it's last updated 2011. Am I missing something? Is there perhaps an easy Moose way to do this? Thanks!
$o=$class->new($response);
$s=$o->success;
@i=$o->items;
{
'success' => bless( do{\(my $o = 1)}, 'JSON::XS::Boolean' ),
'requestNumber' => 5,
'itemsCount' => 1,
'action' => 'search.json',
'totalResults' => 161,
'items' => [
{
'link' => 'http://europeana.eu/api//v2/record/15503/E627F23EF13FA8E6584AF8706A95DB85908413BE.json?wskey=NpXXXX',
'provider' => [
'Kulturpool'
],
'europeanaCollectionName' => [
'15503_Ag_AT_Kulturpool_khm_fs'
],
# more fields omitted
}
],
'apikey' => 'Npxxxx'
};
Upvotes: 3
Views: 291
Reputation: 107040
Although I don't like using it, defining an AUTOLOAD
subroutine is a way to create arbitrary classes on the fly. It's been a while since I used it, but it should look something like this:
package Local::Foo;
sub new {
my $class = shift;
my $self = {};
bless $self, $class;
return $self;
}
sub AUTOLOAD {
my $self = shift;
my $value = shift;
our $AUTOLOAD;
(my $method = $AUTOLOAD) = s/.*:://;
if ( defined $value ) {
$self->{$method} = $value;
}
return $self->{$method};
}
This class Local::Foo
has an infinite amount of methods. For example, if I said
$foo->bar("fubar");
This would be the same as:
$foo->{bar} = "foobar";
If i called $foo->bar;
, it will return the value of $foo->{bar};
.
You probably want something to limit your method's style, and their values. For example, with this:
$foo->BAR;
$foo->Bar;
$foo->bar;
are all three valid and completely different methods. You probably want something to make sure your methods match a particular pattern (i.e., they're all lowercase, or the first letter is uppercase and the rest are lowercase. You probably want to make sure they start with a letter so, $foo->23diba;
isn't a valid method.
One little problem: Once you define an AUTOLOAD
subroutine, you also define DESTROY
subroutine too. Perl calls the DESTROY
subroutine before an object is destroyed. You need to handle the issue if $AUTOLOAD =~ /.*::DESTROY$/
too. You may need to add:
return if $AUTOLOAD =~ /.*::DESTROY$/;
somewhere in the AUTOLOAD
subroutine, so you don't accidentally do something when DESTROY
is called. Remember, it's automatically called whenever a class object falls out of scope if one exists, and with AUTOLOAD
, you've defined one anyway.
Upvotes: 1
Reputation: 4335
I am not saying this is necessarily a good idea, the best way to do the idea, or gotcha-free. I never tried it till 15 minutes ago. But it is fun and it is terse, so–
#!/usr/bin/env perl
BEGIN {
package Role::AutoVacca;
use Moo::Role;
use Scalar::Util "blessed";
sub BUILD {
my $self = shift;
for my $attr ( grep /\A[^_]/, keys %{$self} )
{
Method::Generate::Accessor
->generate_method( blessed($self),
$attr,
{ is => "rw" } );
}
}
package Fakey;
use Moo;
with "Role::AutoVacca";
}
my $fake = Fakey->new({
success => bless( do{\(my $o = 1)}, "JSON::XS::Boolean" ),
items => [ { link => "http://europeana.eu/o/haipi",
provider => [ "mememememe" ] } ],
apikey => "3k437" });
print "I CAN HAZ KEE? ", $fake->apikey, $/;
print "IZ GUD? ", $fake->success ? "YAH" : "ONOES", $/;
print "WUT DIZZYING? ", $fake->items, $/;
Upvotes: 0
Reputation: 13792
This is an example:
use strict;
package Foo;
#define a simple Foo class with 3 properties
use base qw(Class::Accessor);
Foo->mk_accessors(qw(name role salary));
package main;
#define a perl hash with the same keys
my $hr = {'name'=>'john doe', 'role'=>'admin', 'salary'=>2500 };
#bless the object
my $obj = bless $hr, 'Foo';
print $obj->name, "\n"; #<-- prints: john doe
Upvotes: 0