summaryrefslogtreecommitdiffstats
path: root/lib/Bread/Board/Declare.pm
blob: 5480c773704c9fd613f48ee9f6e79d9a705a044f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
package Bread::Board::Declare;
use Moose::Exporter;
# ABSTRACT: create Bread::Board containers as normal Moose objects

use Bread::Board ();

=head1 SYNOPSIS

  package MyApp;
  use Moose;
  use Bread::Board::Declare;

  has dsn => (
      is    => 'ro',
      isa   => 'Str',
      value => 'dbi:mysql:my_db',
  );

  has dbic => (
      is           => 'ro',
      isa          => 'MyApp::Model::DBIC',
      dependencies => ['dsn'],
      lifecycle    => 'Singleton',
  );

  has tt => (
      is  => 'ro',
      isa => 'MyApp::View::TT',
  );

  has controller => (
      is           => 'ro',
      isa          => 'MyApp::Controller',
      dependencies => {
          model => 'dbic',
          view  => 'tt',
      },
  );

  MyApp->new->controller; # new controller object with new model and view
  MyApp->new(
      model => MyApp::Model::KiokuDB->new,
  )->controller; # new controller object with new view and kioku model

=head1 DESCRIPTION

This module is a L<Moose> extension which allows for declaring L<Bread::Board>
container classes in a more straightforward and natural way. It sets up
L<Bread::Board::Container> as the superclass, and creates services associated
with each attribute that you create, according to these rules:

=over 4

=item

If the C<< service => 0 >> option is passed to C<has>, no service is created.

=item

If the C<value> option is passed to C<has>, a L<Bread::Board::Literal>
service is created, with the given value.

=item

If the C<block> option is passed to C<has>, a L<Bread::Board::BlockInjection>
service is created, with the given coderef as the block. In addition to
receiving the service object (as happens in Bread::Board), this coderef will
also be passed the container object.

=item

If the attribute has a type constraint corresponding to a class, a
L<Bread::Board::ConstructorInjection> service is created, with the class
corresponding to the type constraint.

=item

Otherwise, no service is created.

=back

Constructor parameters for services (C<dependencies>, C<lifecycle>, etc) can
also be passed into the attribute definition; these will be forwarded to the
service constructor.

In addition to creating the services, this module also modifies the attribute
reader generation, so that if the attribute has no value, a value will be
resolved from the associated service. It also modifies the C<get> method on
services so that if the associated attribute has a value, that value will be
returned immediately. This allows for overriding service values by passing
replacement values into the constructor, or by calling setter methods.

Note that C<default>/C<builder> doesn't make a lot of sense in this setting, so
they are explicitly disabled. In addition, multiple inheritance would just
cause a lot of problems, so it is also disabled (although single inheritance
and role application works properly).

NOTE: When using this module in roles with Moose versions prior to 2.0, the
attribute trait will need to be applied explicitly to attributes that should
become services, as in:

  has attr => (
      traits => ['Service'],
      is     => 'ro',
      isa    => 'Str',
      value  => 'value',
  )

=cut

my (undef, undef, $init_meta) = Moose::Exporter->build_import_methods(
    install => ['import', 'unimport'],
    class_metaroles => {
        attribute => ['Bread::Board::Declare::Meta::Role::Attribute'],
        class     => ['Bread::Board::Declare::Meta::Role::Class'],
        instance  => ['Bread::Board::Declare::Meta::Role::Instance'],
    },
    (Moose->VERSION >= 1.9900
        ? (role_metaroles => {
               applied_attribute =>
                   ['Bread::Board::Declare::Meta::Role::Attribute'],
           })
        : ()),
    base_class_roles => ['Bread::Board::Declare::Role::Object'],
);

sub init_meta {
    my $package = shift;
    my %options = @_;
    if (my $meta = Class::MOP::class_of($options{for_class})) {
        if ($meta->isa('Class::MOP::Class')) {
            my @supers = $meta->superclasses;
            $meta->superclasses('Bread::Board::Container')
                if @supers == 1 && $supers[0] eq 'Moose::Object';
        }
    }
    $package->$init_meta(%options);
}

=head1 BUGS

No known bugs.

Please report any bugs through RT: email
C<bug-bread-board-declare at rt.cpan.org>, or browse to
L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Bread-Board-Declare>.

=head1 SEE ALSO

L<Bread::Board>

=head1 SUPPORT

You can find this documentation for this module with the perldoc command.

    perldoc Bread::Board::Declare

You can also look for information at:

=over 4

=item * AnnoCPAN: Annotated CPAN documentation

L<http://annocpan.org/dist/Bread-Board-Declare>

=item * CPAN Ratings

L<http://cpanratings.perl.org/d/Bread-Board-Declare>

=item * RT: CPAN's request tracker

L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Bread-Board-Declare>

=item * Search CPAN

L<http://search.cpan.org/dist/Bread-Board-Declare>

=back

=begin Pod::Coverage

init_meta

=end Pod::Coverage

=cut

1;