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