Re: Trying to create an attribute trait that prompts for a value if none specified

[email protected] (Edward Allen)
Newsgroups perl.moose
Message-ID <CAPaaNjXXRucF0KmmuipK7U1_p3os8tci0XJj3vXWxKsGjnorrg@mail.gmail.com>
I had a similar problem.  Try this:

============== start

#!/usr/bin/perl -w


{
    package MooseX::Trait::PromptBuilder;
    use Moose::Role;

    after 'install_accessors' => sub {
        my ($attr, $inline, $class) = @_;
        $attr->create_builder($class);
    };

    # Replace this!  Add type checking, color, dancing bears, etc.
    sub ask_away {
        my ($attr) = @_;
        return sub {
            print "Value for " . $attr->name . ": ";
            my $resp = <STDIN>;
            chomp $resp;
            return ($resp eq "") ? undef : $resp;
        }
    }

    has __ask_builder_built => (
        isa => 'Bool',
        reader => '_has_ask_builder',
        writer => '_set_has_ask_builder'
    );


    sub create_builder {
        my $attr = shift;
        my $class = shift || $attr->associated_class;

        # To prevent recurssion
        return if  $attr->_has_ask_builder;
        $attr->_set_has_ask_builder(1);

        # You do not need this, but someday you may:
        if ($attr->associated_class->is_immutable) {
            Moose->throw_error(
            q{Class }, $attr->associated_class,
            q{is immutable. Run create_builder before make_immutable});
        }

        my $builder = $attr->ask_away();

        if ($attr->has_builder) {
            $attr->associated_class->add_around_method_modifier($attr->builder,
                sub {
                    my ($orig,$instance,@params) = @_;
                    for my $code ($builder,$orig) {
                        my $return = $instance->$code(@params);
                        if (defined $return) { return $return }
                    }
                    return;
                }
            );
        }
        else {
            # The real magic:
            $attr = $attr->clone_and_inherit_options(
                default => $builder,
                lazy => 1
            );
            $class->add_attribute($attr);
        }
    }

    1;
}

{
    package MyApp;
    use Moose;

    has toes => (
        is => "Int",
        is => "ro",
        traits => [ 'MooseX::Trait::PromptBuilder' ],
        # DO NOT USE default ATTRIBUTE
        builder => '_build_toes',
    );

    sub _build_toes { 12 }
}


my $app1 = MyApp->new();

print "I have: ", $app1->toes , "\n";



==========end
I reserve no rights to the above, if you have to ask.

---ealleniii

On Wed, Dec 21, 2011 at 8:45 PM, Buddy Burden <[email protected]> wrote:
>
> Guys,
>
> Okay, basically what I want is just what the subject says: you might
> declare your attribute something like so:
>
>    has value => ( traits => [qw< Promptable >], is => 'rw', isa =>
> 'Int', prompt => 'Please enter a value:' );
>
> then, for each instance, if you provide a value in new(), that's the
> value; if you don't, it prompts the user for it.  The basic idea is I
> can use this with MooseX::App::Cmd for switches that are not provided
> on the command-line.
>
> So, my first approach was to around attach_to_class and just stick a
> builder method in.  But you can't actually _set_ a builder, because
> builder is essentially a read-only attribute of the attribute
> metaclass.
>
> Okay, no problem: I'll just intercept BUILDARGS for the attribute
> metaclass and slip a builder in there as if the client had specified
> it.  No go:
>
> > The method 'BUILDARGS' was not found in the inheritance hierarchy for Moose::Meta::Class::__ANON__::SERIAL::15 at /usr/local/lib64/perl5/Class/MOP/Class.pm line 1048
>
> I guess roles can't mess with the BUILDARGS for the classes they're
> composed into ... ?  I could imagine that being a reasonable
> restriction.
>
> Okay, next try:  I'll around initialize_slot_instance for the
> attribute metaclass and prompt right there, and slip the user-supplied
> value into $params before passing it onto $orig (only if not already
> there and after validation, natch).  But my around method for
> initialize_slot_instance is never called, it seems ... not sure why
> ... because it's being supplied by a role? because the new() is being
> inlined? other?
>
> So now I'm out of ideas.  Can anyone tell me what's wrong with any of
> the approaches, or if there's a whole 'nother approach that I should
> be using? or maybe there's some helpful module I've missed in my CPAN
> searches?  Anything?  I'd really appreciate it.
>
> Thanx for all you do, Moosites.  Moose makes my job easier every day.
> Keep up the good work.
>
>
>             -- Buddy
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.