Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

fields alternative?

by bannaN (Acolyte)
on Apr 04, 2011 at 14:25 UTC ( #897334=perlquestion: print w/ replies, xml ) Need Help??
bannaN has asked for the wisdom of the Perl Monks concerning the following question:

Hi,

The fields module on CPAN doesnt cooperate well with the JSON::XS module. So i need to find an alternative that prevents developers from setting a non-defined attribute on a blessed object.

A user should not be able to set $obj->{asdf} unless asdf is specified as an attribute.

I thought Moose \ Mouse provided such functionallity, but it turns out it doesnt.

Anyone have any suggestions on how to achive such functionallity?

Comment on fields alternative?
Re: fields alternative?
by BrowserUk (Pope) on Apr 04, 2011 at 14:37 UTC

    How about Inside-out Objects?


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: fields alternative?
by educated_foo (Vicar) on Apr 04, 2011 at 14:53 UTC
    Hash::Util gives you lock_keys, which should do the trick. IMHO it's just a way to annoy developers for little benefit, but...

      Hi,

      good suggestion, but unfortunately this gives me the same error as with fields.

      Here is a example of the problem:

      use Test::More; use_ok('Data::Dumper'); use_ok('JSON'); { package Wrapper; use fields qw ( _value ); sub new{ my $s = $_[0]; unless (ref $s) { $s = fields::new($s); } $s->{_value} = 42; return $s; } sub TO_JSON{ return $_[0]->{_value}; } } my $t = Wrapper->new(); my $jsonString = undef; eval{ my $json = JSON->new; $json->allow_nonref(1); $json->utf8(1); $json->convert_blessed(1); $json->allow_blessed(1); $jsonString = $json->encode($t); }; if($@){ print(Dumper($@)); fail("We got an error"); } print $jsonString; done_testing();
      And the output:
      ok 1 - use Data::Dumper; # Failed test 'We got an error' ok 2 - use JSON; $VAR1 = 'Modification of a read-only value attempted at /pathtofile/JS +ON::XS-Fields.t line 34. '; not ok 3 - We got an error 1..3
Re: fields alternative?
by stvn (Monsignor) on Apr 05, 2011 at 17:29 UTC

    Perhaps you would like to explain what it is you are actually trying to do, I suspect there is at least a few different ways to do it, after all, this is Perl.

    -stvn
      (I have spoken with bannaN and his problem seems to be:)
      • fields and JSON::XS don't work together. fields and JSON::PP do work toghether, but JSON::PP is too slow for his application.
      • fields has some nice properties he would like to preserve, such as to catch common programming errors, e.g. accessing a non-declared object variable (e.g. $p->{a})
      His strategy is:
      • Create objects in perl and dump object content with JSON::XS
      • Replace fields with another object system that detect access to a non-declared object variable (run-time or compile-time).
      Is there an perl object system / framework that supports the above requirements? Moose and Mouse have been considered and fail the last requirement.

      --
      No matter how great and destructive your problems may seem now, remember, you've probably only seen the tip of them. [1]

        You have explained what he is trying to accomplish, but I actually more was looking to know why he wants to do this. This kind of access control is, in my opinion, a social issue that should not be solved with technology. But since I actually don't know the motivation, I might be wrong on that.

        Replace fields with another object system that detect access to a non-declared object variable (run-time or compile-time).

        This being perl, there is always a way to access and manipulate object fields no matter what technique you use (yes, even Inside-Out objects can be reached with the right level of black magic). So no matter what features I show you, there is always a way around it for someone who is motivated enough.

        Is there an perl object system / framework that supports the above requirements? Moose and Mouse have been considered and fail the last requirement.

        Moose has read-only accessors and if you pass unknown attributes to the constructor they will not be added to the underlying object instance. There is also the MooseX::StrictConstructor module which makes passing those unknown attributes an error. Honestly, this, along with some tight type constraints should be enough to validate the data coming from JSON::XS and being passed to the Moose constructor, which seems to be what your goal is.

        That all said, none of this will protect the underlying instance structure. But I really think you need to ask yourself, is my environment so hostile that I actually need to protect the instance structure? Can I not enforce things via the accessors and object interface?

        Now, if you are really dead set on actually doing this, Moose does provide you the features you need (I suspect Mouse can do this too, but I don't know the actual incantations to do so). So here is the code that you would need in your class BUILD method (or in a common base class if you want, it will still work correctly).

        package LockedBox; use Moose; use MooseX::StrictConstructor; use Hash::Util qw[ lock_keys lock_value ]; has bar => ( is => 'ro' ); sub BUILD { my $self = shift; lock_keys( %$self ); foreach my $attr ( $self->meta->get_all_attributes ) { lock_value( %$self, $attr->name ) } }
        This basically just locks the instance, then locks each of the values that are defined via Moose. This means that when you create an instance with Moose like so...
        my $foo = LockedBox->new( bar => 10 );
        You will first get the normal Moose constructor behavior which is to only use the keys that are defined. But since I actually added MooseX::StrictConstructor into this, that means that if you were to do this ...
        my $foo = LockedBox->new( bar => 10, baz => 100 );
        The code would fail with an exception.

        Next, since we locked all the hash keys, if someone were to try and do this ..

        $foo->{baz} = 20;
        It would die with the error "Attempt to access disallowed key 'baz' in a restricted hash" which is because of Hash::Util::lock_keys.

        And lastly, since lock_keys only prevents you from adding new keys but not altering existing ones, we found all the valid keys in Moose and locked those values with Hash::Util::lock_values, which means that this code ...

        $foo->{bar} = 20;
        Will throw the following error "Modification of a read-only value attempted".

        Now, in my opinion, all this is overkill. If the users of your class are violating the encapsulation, then you have a real problem with those users and your documentation should point out such incorrect usage. If these other users are actually co-workers, then you have a social problem on your hands and one that needs to be fixed socially and not technically. In the end, all some jerk needs to do is use Hash::Util to unlock the keys/values and they can get at that, but this really is an explicit violation and should be dealt with.

        -stvn

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://897334]
Approved by chrestomanci
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others cooling their heels in the Monastery: (6)
As of 2014-12-26 22:47 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    Is guessing a good strategy for surviving in the IT business?





    Results (176 votes), past polls