Good idea. I don't know why I didn't think of initializers. The Moose cabal aren't especially big fans of initializers, and consider them to be a bit of a misfeature, but this is the kind of niche area they seem to come in handy.

Let's take your initializer technique and add some metaprogramming.

First some test cases...

use v5.14; use Test::More; package Foo { use Moose; has foo => ( traits => ['MooseX::IgnoreFalse::Trait::Attribute'], is => 'ro', default => 'whatever', ); } is(Foo->new(foo => 'xyz')->foo, 'xyz'); is(Foo->new(foo => '0')->foo, 'whatever'); is(Foo->new(foo => '')->foo, 'whatever'); is(Foo->new(foo => undef)->foo, 'whatever'); done_testing;

When those tests all pass, then we're done.

Now, a first stab at the metatrait. For attribute metatraits, after _process_options can be a very handy place to hook onto. It effectively allows you to rewrite a has declaration before the attribute gets created. If you're not doing anything to deeply modify how Moose internals work, then that's often sufficient.

Here we'll just hook after _process_options to add an initializer that does the following:

  1. If the value passed into the constructor was true, then set it.
  2. Otherwise, if there is a default, set the attribute value to the default value.
  3. Otherwise, if there is a builder, set the attribute value to the built value.
  4. Otherwise, don't set the attribute at all.

How does that look?

use v5.14; package MooseX::IgnoreFalse::Trait::Attribute { use Moose::Role; after _process_options => sub { my ($class, $name, $options) = @_; $options->{initializer} = sub { my ($instance, $value, $setter) = @_; my $meta = $instance->meta->get_attribute($name); $value ? $setter->( $value ) : $meta->has_default ? $setter->( $meta->default($instance) +) : $meta->has_builder ? $setter->( $meta->_call_builder($inst +ance) ) : (); } }; }

This is sufficient to pass our test case; yay! However, the keen eyed may have noticed that if the attribute already had an initializer, that will be replaced with our new initializer. So let's make our hook a little smarter to supplement any existing initializer...

use v5.14; package MooseX::IgnoreFalse::Trait::Attribute { use Moose::Role; after _process_options => sub { my ($class, $name, $options) = @_; if (my $orig = $options->{initializer}) { $options->{initializer} = sub { my ($instance, $value, $setter) = @_; my $meta = $instance->meta->get_attribute($name); $value ? $orig->( $instance, $value, $sett +er ) : $meta->has_default ? $orig->( $instance, $meta->defaul +t($instance), $setter ) : $meta->has_builder ? $orig->( $instance, $meta->_call_ +builder($instance), $setter ) : (); } } else { $options->{initializer} = sub { my ($instance, $value, $setter) = @_; my $meta = $instance->meta->get_attribute($name); $value ? $setter->( $value ) : $meta->has_default ? $setter->( $meta->default($instan +ce) ) : $meta->has_builder ? $setter->( $meta->_call_builder($ +instance) ) : (); } } }; }

That's a fairly gentle introduction to attribute metaprogramming.

For completeness, here's everything in one file.

package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name

In reply to Re^2: Moose and default values by tobyink
in thread Moose and default values by morgon

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.