45ad927a24ad30d6590c86033bdf418a2ffd74e8
[catagits/Reaction.git] / lib / Reaction / Role / Meta / Attribute.pm
1 package Reaction::Role::Meta::Attribute;
2
3 use Moose::Role;
4
5 #is => 'Bool' ? or leave it open
6 has lazy_fail  =>
7     (is => 'ro', reader => 'is_lazy_fail',  required => 1, default => 0);
8
9 if ( $Moose::VERSION < 1.09 ) { 
10   around legal_options_for_inheritance => sub {
11     return (shift->(@_), qw/valid_values/);
12   };
13 }
14
15 around _process_options => sub {
16     my $super = shift;
17     my ($class, $name, $options) = @_;
18
19     my $fail  = $options->{lazy_fail};
20
21     if ( $fail ) {
22       confess("You may not use both lazy_build and lazy_fail for one attribute")
23         if $fail && $options->{lazy_build};
24
25       $options->{lazy} = 1;
26       $options->{required} = 1;
27       $options->{default} = sub { confess "${name} must be provided before calling reader" };
28     }
29
30     #we are using this everywhere so might as well move it here.
31     $options->{predicate} ||= ($name =~ /^_/) ? "_has${name}" : "has_${name}"
32       if !$options->{required} || $options->{lazy};
33
34     $super->($class, $name, $options);
35 };
36
37 foreach my $type (qw(clearer predicate)) {
38
39   my $value_meth = do {
40     if ($type eq 'clearer') {
41       'clear_value'
42     } elsif ($type eq 'predicate') {
43       'has_value'
44     } else {
45       confess "NOTREACHED";
46     }
47   };
48
49   __PACKAGE__->meta->add_method("get_${type}_method" => sub {
50     my $self = shift;
51     my $info = $self->$type;
52     return $info unless ref $info;
53     my ($name) = %$info;
54     return $name;
55   });
56
57   __PACKAGE__->meta->add_method("get_${type}_method_ref" => sub {
58     my $self = shift;
59     if ((my $name = $self->${\"get_${type}_method"}) && $self->associated_class) {
60         return $self->associated_class->get_method($name);
61     } else {
62         return sub { $self->$value_meth(@_); }
63     }
64   });
65 }
66
67 1;
68
69 __END__;
70
71 =head1 NAME
72
73 Reaction::Meta::Attribute
74
75 =head1 SYNOPSIS
76
77     has description => (is => 'rw', isa => 'Str', lazy_fail => 1);
78
79 =head1 Method-naming conventions
80
81 Reaction::Meta::Attribute will never override the values you set for method names,
82 but if you do not it will follow these basic rules:
83
84 Attributes with a name that starts with an underscore will default to using
85 builder and predicate method names in the form of the attribute name preceeded by
86 either "_has" or "_build". Otherwise the method names will be in the form of the
87 attribute names preceeded by "has_" or "build_". e.g.
88
89    #auto generates "_has_description" and expects "_build_description"
90    has _description => (is => 'rw', isa => 'Str', lazy_fail => 1);
91
92    #auto generates "has_description" and expects "build_description"
93    has description => (is => 'rw', isa => 'Str', lazy_fail => 1);
94
95 =head2 Predicate generation
96
97 All non-required or lazy attributes will have a predicate automatically
98 generated for them if one is not already specified.
99
100 =head2 lazy_fail
101
102 lazy_fail will fail if it is called without first having set the value.
103
104 =head1 AUTHORS
105
106 See L<Reaction::Class> for authors.
107
108 =head1 LICENSE
109
110 See L<Reaction::Class> for the license.
111
112 =cut