| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Role; |
|
2
|
|
|
|
|
|
|
|
|
3
|
30
|
|
|
30
|
|
857718
|
use strict; |
|
|
30
|
|
|
|
|
49
|
|
|
|
30
|
|
|
|
|
961
|
|
|
4
|
30
|
|
|
30
|
|
121
|
use warnings; |
|
|
30
|
|
|
|
|
62
|
|
|
|
30
|
|
|
|
|
1229
|
|
|
5
|
30
|
|
|
30
|
|
3842
|
use version; |
|
|
30
|
|
|
|
|
16971
|
|
|
|
30
|
|
|
|
|
139
|
|
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
our $VERSION = qv('v0.1.1'); |
|
8
|
|
|
|
|
|
|
our $AUTHORITY = 'cpan:MANWAR'; |
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
our %REQUIRED_METHODS; |
|
11
|
|
|
|
|
|
|
our %IS_ROLE; |
|
12
|
|
|
|
|
|
|
our %EXCLUDED_ROLES; |
|
13
|
|
|
|
|
|
|
our %APPLIED_ROLES; |
|
14
|
|
|
|
|
|
|
our %METHOD_ALIASES; |
|
15
|
|
|
|
|
|
|
our %ROLE_ATTRIBUTES; |
|
16
|
|
|
|
|
|
|
our %METHOD_ORIGIN_CACHE; |
|
17
|
|
|
|
|
|
|
our %ROLE_LOAD_CACHE; |
|
18
|
|
|
|
|
|
|
our %CAN_HANDLE_ATTR_CACHE; |
|
19
|
|
|
|
|
|
|
our %ROLE_METHODS_CACHE; |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
# Precomputed skip patterns |
|
22
|
|
|
|
|
|
|
my %SKIP_METHODS = map { $_ => 1 } qw( |
|
23
|
|
|
|
|
|
|
BEGIN END import DESTROY new requires |
|
24
|
|
|
|
|
|
|
excludes IS_ROLE with has does |
|
25
|
|
|
|
|
|
|
AUTOLOAD VERSION AUTHORITY INC |
|
26
|
|
|
|
|
|
|
); |
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
sub import { |
|
29
|
55
|
|
|
55
|
|
2703
|
my ($class, @args) = @_; |
|
30
|
55
|
|
|
|
|
121
|
my $caller = caller; |
|
31
|
30
|
|
|
30
|
|
6652
|
no strict 'refs'; |
|
|
30
|
|
|
|
|
62
|
|
|
|
30
|
|
|
|
|
21677
|
|
|
32
|
|
|
|
|
|
|
|
|
33
|
55
|
|
|
|
|
503
|
$IS_ROLE{$caller} = 1; |
|
34
|
|
|
|
|
|
|
|
|
35
|
55
|
50
|
|
|
|
182
|
if (@args == 0) { |
|
36
|
55
|
|
|
|
|
103
|
$REQUIRED_METHODS{$caller} = []; |
|
37
|
55
|
|
|
|
|
85
|
*{"${caller}::requires"} = \&requires; |
|
|
55
|
|
|
|
|
246
|
|
|
38
|
55
|
|
|
|
|
73
|
*{"${caller}::excludes"} = \&excludes; |
|
|
55
|
|
|
|
|
150
|
|
|
39
|
55
|
|
|
|
|
87
|
*{"${caller}::has"} = \&_role_has; |
|
|
55
|
|
|
|
|
134
|
|
|
40
|
|
|
|
|
|
|
} else { |
|
41
|
0
|
|
|
|
|
0
|
_setup_role_application($caller, @args); |
|
42
|
|
|
|
|
|
|
} |
|
43
|
|
|
|
|
|
|
|
|
44
|
55
|
|
|
|
|
250
|
strict->import; |
|
45
|
55
|
|
|
|
|
1079
|
warnings->import; |
|
46
|
55
|
|
|
|
|
119
|
_export_with($caller); |
|
47
|
|
|
|
|
|
|
} |
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
sub with { |
|
50
|
36
|
|
|
36
|
1
|
203977
|
my (@roles) = @_; |
|
51
|
36
|
|
|
|
|
94
|
my $caller = caller; |
|
52
|
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
# Called inside a ROLE |
|
54
|
36
|
100
|
|
|
|
278
|
if ($IS_ROLE{$caller}) { |
|
55
|
2
|
|
|
|
|
5
|
my ($clean_roles_ref, $aliases_by_role) |
|
56
|
|
|
|
|
|
|
= _process_role_arguments(@roles); |
|
57
|
2
|
|
|
|
|
5
|
$METHOD_ALIASES{$caller} = $aliases_by_role; |
|
58
|
|
|
|
|
|
|
|
|
59
|
2
|
|
|
|
|
3
|
foreach my $role (@$clean_roles_ref) { |
|
60
|
2
|
|
|
|
|
6
|
_ensure_role_loaded($role); |
|
61
|
2
|
|
50
|
|
|
3
|
push @{ $APPLIED_ROLES{$caller} ||= [] }, $role; |
|
|
2
|
|
|
|
|
10
|
|
|
62
|
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
# Merge required methods |
|
64
|
2
|
50
|
|
|
|
7
|
if (my $req = $REQUIRED_METHODS{$role}) { |
|
65
|
2
|
|
50
|
|
|
3
|
push @{ $REQUIRED_METHODS{$caller} ||= [] }, @$req; |
|
|
2
|
|
|
|
|
7
|
|
|
66
|
|
|
|
|
|
|
} |
|
67
|
|
|
|
|
|
|
} |
|
68
|
|
|
|
|
|
|
|
|
69
|
2
|
|
|
|
|
5
|
return; |
|
70
|
|
|
|
|
|
|
} |
|
71
|
|
|
|
|
|
|
|
|
72
|
34
|
|
|
|
|
79
|
apply_role($caller, @roles); |
|
73
|
|
|
|
|
|
|
} |
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
sub requires { |
|
76
|
13
|
|
|
13
|
1
|
799004
|
my (@methods) = @_; |
|
77
|
13
|
|
|
|
|
33
|
my $caller = caller; |
|
78
|
13
|
50
|
|
|
|
89
|
$REQUIRED_METHODS{$caller} = [] unless exists $REQUIRED_METHODS{$caller}; |
|
79
|
13
|
|
|
|
|
37
|
push @{$REQUIRED_METHODS{$caller}}, @methods; |
|
|
13
|
|
|
|
|
60
|
|
|
80
|
|
|
|
|
|
|
} |
|
81
|
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
sub excludes { |
|
83
|
5
|
|
|
5
|
1
|
686
|
my (@excluded_roles) = @_; |
|
84
|
5
|
|
|
|
|
13
|
my $caller = caller; |
|
85
|
5
|
50
|
|
|
|
68
|
$EXCLUDED_ROLES{$caller} = [] unless exists $EXCLUDED_ROLES{$caller}; |
|
86
|
5
|
|
|
|
|
8
|
push @{$EXCLUDED_ROLES{$caller}}, @excluded_roles; |
|
|
5
|
|
|
|
|
15
|
|
|
87
|
|
|
|
|
|
|
} |
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
sub apply_role { |
|
90
|
49
|
|
|
49
|
1
|
327865
|
my ($class, @roles) = @_; |
|
91
|
49
|
100
|
|
|
|
111
|
my $target_class = ref($class) ? ref($class) : $class; |
|
92
|
49
|
|
|
|
|
138
|
my ($clean_roles_ref, $aliases_by_role) = |
|
93
|
|
|
|
|
|
|
_process_role_arguments(@roles); |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
$METHOD_ALIASES{$target_class} = { |
|
96
|
49
|
100
|
|
|
|
69
|
%{$METHOD_ALIASES{$target_class} || {}}, |
|
|
49
|
|
|
|
|
289
|
|
|
97
|
|
|
|
|
|
|
%$aliases_by_role |
|
98
|
|
|
|
|
|
|
}; |
|
99
|
|
|
|
|
|
|
|
|
100
|
49
|
|
|
|
|
102
|
foreach my $role (@$clean_roles_ref) { |
|
101
|
57
|
|
|
|
|
141
|
_apply_single_role($target_class, $role); |
|
102
|
|
|
|
|
|
|
} |
|
103
|
|
|
|
|
|
|
|
|
104
|
34
|
|
|
|
|
66
|
_add_does_method($target_class); |
|
105
|
34
|
|
|
|
|
148
|
return 1; |
|
106
|
|
|
|
|
|
|
} |
|
107
|
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
sub get_applied_roles { |
|
109
|
1
|
|
|
1
|
1
|
644
|
my ($class) = @_; |
|
110
|
1
|
50
|
|
|
|
4
|
my $target_class = ref($class) ? ref($class) : $class; |
|
111
|
1
|
50
|
|
|
|
1
|
return @{$APPLIED_ROLES{$target_class} || []}; |
|
|
1
|
|
|
|
|
4
|
|
|
112
|
|
|
|
|
|
|
} |
|
113
|
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
sub is_role { |
|
115
|
2
|
|
|
2
|
1
|
9
|
my ($package) = @_; |
|
116
|
2
|
|
|
|
|
11
|
return $IS_ROLE{$package}; |
|
117
|
|
|
|
|
|
|
} |
|
118
|
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
sub Role::does { |
|
120
|
2
|
|
|
2
|
1
|
207
|
my ($class_or_obj, $role) = @_; |
|
121
|
2
|
|
33
|
|
|
9
|
return _class_does_role(ref($class_or_obj) || $class_or_obj, $role); |
|
122
|
|
|
|
|
|
|
} |
|
123
|
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
sub UNIVERSAL::does { |
|
125
|
0
|
|
|
0
|
0
|
0
|
my ($self, $role) = @_; |
|
126
|
0
|
|
0
|
|
|
0
|
return _class_does_role(ref($self) || $self, $role); |
|
127
|
|
|
|
|
|
|
} |
|
128
|
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
# |
|
130
|
|
|
|
|
|
|
# |
|
131
|
|
|
|
|
|
|
# PRIVATE FUNCTIONS |
|
132
|
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
sub _get_role_methods_directly { |
|
134
|
78
|
|
|
78
|
|
95
|
my ($role) = @_; |
|
135
|
30
|
|
|
30
|
|
186
|
no strict 'refs'; |
|
|
30
|
|
|
|
|
38
|
|
|
|
30
|
|
|
|
|
7499
|
|
|
136
|
78
|
|
|
|
|
83
|
my $role_stash = \%{"${role}::"}; |
|
|
78
|
|
|
|
|
174
|
|
|
137
|
78
|
|
|
|
|
87
|
my @methods; |
|
138
|
|
|
|
|
|
|
|
|
139
|
78
|
|
|
|
|
163
|
foreach my $name (keys %$role_stash) { |
|
140
|
473
|
100
|
|
|
|
659
|
next if $SKIP_METHODS{$name}; |
|
141
|
83
|
100
|
|
|
|
261
|
next if $name =~ /^[A-Z_]+$/; # skip constants |
|
142
|
81
|
|
|
|
|
172
|
my $glob = $role_stash->{$name}; |
|
143
|
81
|
100
|
|
|
|
77
|
next unless defined *{$glob}{CODE}; |
|
|
81
|
|
|
|
|
209
|
|
|
144
|
77
|
|
|
|
|
164
|
push @methods, $name; |
|
145
|
|
|
|
|
|
|
} |
|
146
|
|
|
|
|
|
|
|
|
147
|
78
|
|
|
|
|
258
|
return \@methods; |
|
148
|
|
|
|
|
|
|
} |
|
149
|
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
sub _class_can_handle_attributes { |
|
151
|
52
|
|
|
52
|
|
73
|
my ($class) = @_; |
|
152
|
|
|
|
|
|
|
return $CAN_HANDLE_ATTR_CACHE{$class} |
|
153
|
52
|
100
|
|
|
|
132
|
if exists $CAN_HANDLE_ATTR_CACHE{$class}; |
|
154
|
|
|
|
|
|
|
|
|
155
|
43
|
|
|
|
|
53
|
my $result = 0; |
|
156
|
43
|
50
|
66
|
|
|
452
|
if ($class->can('can_handle_attributes')) { |
|
|
|
100
|
|
|
|
|
|
|
157
|
0
|
0
|
|
|
|
0
|
$result = $class->can_handle_attributes ? 1 : 0; |
|
158
|
|
|
|
|
|
|
} |
|
159
|
|
|
|
|
|
|
elsif ($class->can('has') && $class->can('extends')) { |
|
160
|
11
|
|
|
|
|
12
|
$result = 1; |
|
161
|
|
|
|
|
|
|
} |
|
162
|
|
|
|
|
|
|
else { |
|
163
|
30
|
|
|
30
|
|
163
|
no strict 'refs'; |
|
|
30
|
|
|
|
|
41
|
|
|
|
30
|
|
|
|
|
8032
|
|
|
164
|
32
|
50
|
|
|
|
43
|
$result = (grep { $_ eq 'Class::More' } @{"${class}::ISA"}) ? 1 : 0; |
|
|
6
|
|
|
|
|
18
|
|
|
|
32
|
|
|
|
|
155
|
|
|
165
|
|
|
|
|
|
|
} |
|
166
|
|
|
|
|
|
|
|
|
167
|
43
|
|
|
|
|
98
|
return $CAN_HANDLE_ATTR_CACHE{$class} = $result; |
|
168
|
|
|
|
|
|
|
} |
|
169
|
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
sub _ensure_role_loaded { |
|
171
|
59
|
|
|
59
|
|
115
|
my ($role) = @_; |
|
172
|
59
|
100
|
|
|
|
223
|
return if $ROLE_LOAD_CACHE{$role}; |
|
173
|
|
|
|
|
|
|
|
|
174
|
51
|
100
|
|
|
|
187
|
unless ($IS_ROLE{$role}) { |
|
175
|
6
|
|
|
|
|
26
|
(my $role_file = "$role.pm") =~ s{::}{/}g; |
|
176
|
6
|
|
|
|
|
11
|
eval { require $role_file }; |
|
|
6
|
|
|
|
|
2329
|
|
|
177
|
6
|
100
|
|
|
|
40
|
if ($@) { |
|
178
|
2
|
|
|
|
|
31
|
die "Failed to load role '$role': $@\n" . |
|
179
|
|
|
|
|
|
|
"Make sure $role package uses 'use Role;' ". |
|
180
|
|
|
|
|
|
|
"and is properly defined"; |
|
181
|
|
|
|
|
|
|
} |
|
182
|
4
|
|
|
|
|
8
|
$ROLE_LOAD_CACHE{$role} = 1; |
|
183
|
4
|
|
|
|
|
4
|
$IS_ROLE{$role} = 1; |
|
184
|
4
|
|
|
|
|
11
|
_cache_role_methods($role); |
|
185
|
|
|
|
|
|
|
} |
|
186
|
|
|
|
|
|
|
} |
|
187
|
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
sub _cache_role_methods { |
|
189
|
4
|
|
|
4
|
|
6
|
my ($role) = @_; |
|
190
|
30
|
|
|
30
|
|
164
|
no strict 'refs'; |
|
|
30
|
|
|
|
|
38
|
|
|
|
30
|
|
|
|
|
5388
|
|
|
191
|
4
|
|
|
|
|
4
|
my $role_stash = \%{"${role}::"}; |
|
|
4
|
|
|
|
|
11
|
|
|
192
|
4
|
|
|
|
|
3
|
my @methods; |
|
193
|
|
|
|
|
|
|
|
|
194
|
4
|
|
|
|
|
12
|
foreach my $name (keys %$role_stash) { |
|
195
|
26
|
100
|
|
|
|
37
|
next if $SKIP_METHODS{$name}; |
|
196
|
6
|
50
|
|
|
|
23
|
next if $name =~ /^[A-Z_]+$/; # Skip constants |
|
197
|
6
|
|
|
|
|
15
|
my $glob = $role_stash->{$name}; |
|
198
|
6
|
50
|
|
|
|
6
|
next unless defined *{$glob}{CODE}; |
|
|
6
|
|
|
|
|
16
|
|
|
199
|
6
|
|
|
|
|
12
|
push @methods, $name; |
|
200
|
|
|
|
|
|
|
} |
|
201
|
|
|
|
|
|
|
|
|
202
|
4
|
|
|
|
|
12
|
$ROLE_METHODS_CACHE{$role} = \@methods; |
|
203
|
|
|
|
|
|
|
} |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
sub _export_with { |
|
206
|
55
|
|
|
55
|
|
97
|
my $caller = shift; |
|
207
|
30
|
|
|
30
|
|
183
|
no strict 'refs'; |
|
|
30
|
|
|
|
|
64
|
|
|
|
30
|
|
|
|
|
3698
|
|
|
208
|
55
|
100
|
|
|
|
58
|
*{"${caller}::with"} = \&with unless defined &{"${caller}::with"}; |
|
|
54
|
|
|
|
|
7119
|
|
|
|
55
|
|
|
|
|
398
|
|
|
209
|
|
|
|
|
|
|
} |
|
210
|
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
sub _ensure_class_base { |
|
212
|
57
|
|
|
57
|
|
86
|
my $class = shift; |
|
213
|
57
|
100
|
|
|
|
631
|
return if $class->can('new'); |
|
214
|
8
|
50
|
|
|
|
28
|
eval { require Class } unless $INC{'Class.pm'}; |
|
|
0
|
|
|
|
|
0
|
|
|
215
|
30
|
|
|
30
|
|
157
|
no strict 'refs'; |
|
|
30
|
|
|
|
|
46
|
|
|
|
30
|
|
|
|
|
7523
|
|
|
216
|
8
|
|
|
|
|
82
|
push @{"${class}::ISA"}, 'Class' |
|
217
|
8
|
50
|
|
|
|
10
|
unless grep { $_ eq 'Class' } @{"${class}::ISA"}; |
|
|
0
|
|
|
|
|
0
|
|
|
|
8
|
|
|
|
|
108
|
|
|
218
|
|
|
|
|
|
|
} |
|
219
|
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
sub _process_role_arguments { |
|
221
|
51
|
|
|
51
|
|
112
|
my (@args) = @_; |
|
222
|
51
|
|
|
|
|
75
|
my @roles; |
|
223
|
|
|
|
|
|
|
my %aliases_by_role; |
|
224
|
|
|
|
|
|
|
|
|
225
|
51
|
|
|
|
|
95
|
foreach my $arg (@args) { |
|
226
|
59
|
100
|
66
|
|
|
171
|
if (ref($arg) eq 'HASH' && $arg->{role}) { |
|
227
|
2
|
|
|
|
|
3
|
my $role = $arg->{role}; |
|
228
|
2
|
|
|
|
|
3
|
push @roles, $role; |
|
229
|
2
|
50
|
33
|
|
|
6
|
if ($arg->{alias} && ref($arg->{alias}) eq 'HASH') { |
|
230
|
2
|
|
|
|
|
5
|
$aliases_by_role{$role} = $arg->{alias}; |
|
231
|
|
|
|
|
|
|
} |
|
232
|
|
|
|
|
|
|
} else { |
|
233
|
57
|
|
|
|
|
108
|
push @roles, $arg; |
|
234
|
|
|
|
|
|
|
} |
|
235
|
|
|
|
|
|
|
} |
|
236
|
|
|
|
|
|
|
|
|
237
|
51
|
|
|
|
|
150
|
return \@roles, \%aliases_by_role; |
|
238
|
|
|
|
|
|
|
} |
|
239
|
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
sub _role_has { |
|
241
|
8
|
|
|
8
|
|
262876
|
my ($attr_name, %spec) = @_; |
|
242
|
8
|
|
|
|
|
13
|
my $caller = caller; |
|
243
|
8
|
|
|
|
|
26
|
$ROLE_ATTRIBUTES{$caller}{$attr_name} = \%spec; |
|
244
|
30
|
|
|
30
|
|
196
|
no strict 'refs'; |
|
|
30
|
|
|
|
|
52
|
|
|
|
30
|
|
|
|
|
19403
|
|
|
245
|
8
|
|
|
|
|
55
|
*{"${caller}::${attr_name}"} = sub { |
|
246
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
|
247
|
0
|
0
|
|
|
|
0
|
if (@_) { |
|
248
|
0
|
|
|
|
|
0
|
$self->{$attr_name} = shift; |
|
249
|
|
|
|
|
|
|
} |
|
250
|
0
|
|
|
|
|
0
|
return $self->{$attr_name}; |
|
251
|
8
|
|
|
|
|
24
|
}; |
|
252
|
|
|
|
|
|
|
} |
|
253
|
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
sub _apply_single_role { |
|
255
|
57
|
|
|
57
|
|
89
|
my ($class, $role) = @_; |
|
256
|
|
|
|
|
|
|
|
|
257
|
57
|
|
|
|
|
182
|
_ensure_class_base($class); |
|
258
|
57
|
|
|
|
|
169
|
_ensure_role_loaded($role); |
|
259
|
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
# Skip if already applied |
|
261
|
55
|
50
|
66
|
|
|
161
|
if ($APPLIED_ROLES{$class} |
|
262
|
12
|
|
|
|
|
49
|
&& grep { $_ eq $role } @{$APPLIED_ROLES{$class}}) { |
|
|
12
|
|
|
|
|
71
|
|
|
263
|
0
|
|
|
|
|
0
|
warn "Role '$role' is already applied to class '$class'"; |
|
264
|
0
|
|
|
|
|
0
|
return; |
|
265
|
|
|
|
|
|
|
} |
|
266
|
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
# Role exclusions |
|
268
|
55
|
100
|
|
|
|
145
|
if (my $excluded = $EXCLUDED_ROLES{$role}) { |
|
269
|
5
|
|
|
|
|
25
|
my @violated = grep { _class_does_role($class, $_) } @$excluded; |
|
|
5
|
|
|
|
|
17
|
|
|
270
|
5
|
100
|
|
|
|
13
|
if (@violated) { |
|
271
|
3
|
|
|
|
|
117
|
die "Role '$role' cannot be composed with role(s): @violated"; |
|
272
|
|
|
|
|
|
|
} |
|
273
|
|
|
|
|
|
|
} |
|
274
|
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
# Apply role attributes |
|
276
|
52
|
|
|
|
|
127
|
_apply_role_attributes($class, $role); |
|
277
|
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
# Merge applied roles metadata |
|
279
|
52
|
|
100
|
|
|
53
|
push @{ $APPLIED_ROLES{$class} ||= [] }, $role; |
|
|
52
|
|
|
|
|
242
|
|
|
280
|
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
# Validate required methods (classes only) |
|
282
|
52
|
50
|
|
|
|
128
|
unless ($IS_ROLE{$class}) { |
|
283
|
52
|
|
|
|
|
61
|
my @missing; |
|
284
|
52
|
|
50
|
|
|
114
|
my $required = $REQUIRED_METHODS{$role} || []; |
|
285
|
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
# Get methods provided by the role being applied |
|
287
|
|
|
|
|
|
|
# so we don't treat them as missing. |
|
288
|
52
|
100
|
|
|
|
56
|
my @role_provides = @{$ROLE_METHODS_CACHE{$role} |
|
|
52
|
|
|
|
|
135
|
|
|
289
|
|
|
|
|
|
|
|| _get_role_methods_directly($role)}; |
|
290
|
52
|
|
|
|
|
104
|
my %role_provides = map { $_ => 1 } @role_provides; |
|
|
60
|
|
|
|
|
135
|
|
|
291
|
|
|
|
|
|
|
|
|
292
|
52
|
|
|
|
|
86
|
foreach my $method (@$required) { |
|
293
|
|
|
|
|
|
|
# Check if the method is missing AND not provided |
|
294
|
|
|
|
|
|
|
# by the role itself. |
|
295
|
25
|
100
|
100
|
|
|
114
|
unless ($class->can($method) || $role_provides{$method}) { |
|
296
|
6
|
|
|
|
|
13
|
push @missing, $method; |
|
297
|
|
|
|
|
|
|
} |
|
298
|
|
|
|
|
|
|
} |
|
299
|
52
|
100
|
|
|
|
172
|
if (@missing) { |
|
300
|
5
|
|
|
|
|
68
|
die "Role '$role' requires method(s) that are missing in ". |
|
301
|
|
|
|
|
|
|
"class '$class': " . join(', ', @missing); |
|
302
|
|
|
|
|
|
|
} |
|
303
|
|
|
|
|
|
|
} |
|
304
|
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
# Prepare methods and aliases |
|
306
|
|
|
|
|
|
|
my $aliases_for_role = |
|
307
|
|
|
|
|
|
|
$METHOD_ALIASES{$class} |
|
308
|
47
|
50
|
100
|
|
|
185
|
? ($METHOD_ALIASES{$class}->{$role} || {}) : {}; |
|
309
|
|
|
|
|
|
|
my @methods_to_copy = |
|
310
|
47
|
100
|
|
|
|
79
|
@{$ROLE_METHODS_CACHE{$role} || _get_role_methods_directly($role)}; |
|
|
47
|
|
|
|
|
110
|
|
|
311
|
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
# Detect conflicts BEFORE installing |
|
313
|
47
|
|
|
|
|
59
|
my @conflicts; |
|
314
|
47
|
|
|
|
|
95
|
foreach my $name (@methods_to_copy) { |
|
315
|
56
|
|
66
|
|
|
147
|
my $install_name = $aliases_for_role->{$name} || $name; |
|
316
|
|
|
|
|
|
|
|
|
317
|
56
|
100
|
|
|
|
268
|
if ($class->can($install_name)) { |
|
318
|
15
|
|
|
|
|
35
|
my $origin = _find_method_origin($class, $install_name); |
|
319
|
|
|
|
|
|
|
# Skip class or same role |
|
320
|
15
|
100
|
66
|
|
|
58
|
next if $origin eq $class || $origin eq $role; |
|
321
|
6
|
|
|
|
|
23
|
my ($role1, $role2) = sort ($origin, $role); |
|
322
|
|
|
|
|
|
|
|
|
323
|
6
|
100
|
|
|
|
13
|
if ($install_name ne $name) { |
|
324
|
1
|
|
|
|
|
5
|
push @conflicts, { |
|
325
|
|
|
|
|
|
|
method => $name, |
|
326
|
|
|
|
|
|
|
alias => $install_name, |
|
327
|
|
|
|
|
|
|
existing_role => $role1, |
|
328
|
|
|
|
|
|
|
new_role => $role2, |
|
329
|
|
|
|
|
|
|
is_alias => 1 |
|
330
|
|
|
|
|
|
|
}; |
|
331
|
|
|
|
|
|
|
} else { |
|
332
|
5
|
|
|
|
|
29
|
push @conflicts, { |
|
333
|
|
|
|
|
|
|
method => $install_name, |
|
334
|
|
|
|
|
|
|
existing_role => $role1, |
|
335
|
|
|
|
|
|
|
new_role => $role2, |
|
336
|
|
|
|
|
|
|
is_alias => 0 |
|
337
|
|
|
|
|
|
|
}; |
|
338
|
|
|
|
|
|
|
} |
|
339
|
|
|
|
|
|
|
} |
|
340
|
|
|
|
|
|
|
} |
|
341
|
|
|
|
|
|
|
|
|
342
|
47
|
|
|
|
|
115
|
@conflicts = sort { $a->{method} cmp $b->{method} } @conflicts; |
|
|
1
|
|
|
|
|
4
|
|
|
343
|
|
|
|
|
|
|
|
|
344
|
47
|
100
|
|
|
|
99
|
if (@conflicts) { |
|
345
|
|
|
|
|
|
|
# Prefer alias conflicts first |
|
346
|
5
|
|
|
|
|
7
|
my ($first) = grep { $_->{is_alias} } @conflicts; |
|
|
6
|
|
|
|
|
14
|
|
|
347
|
5
|
|
66
|
|
|
18
|
$first ||= $conflicts[0]; |
|
348
|
|
|
|
|
|
|
|
|
349
|
5
|
100
|
|
|
|
9
|
if ($first->{is_alias}) { |
|
350
|
1
|
|
|
|
|
23
|
die "Method conflict: $first->{method} (aliased to " . |
|
351
|
|
|
|
|
|
|
"$first->{alias}) between $first->{existing_role} " . |
|
352
|
|
|
|
|
|
|
"and $first->{new_role}"; |
|
353
|
|
|
|
|
|
|
} else { |
|
354
|
4
|
|
|
|
|
58
|
die "Method conflict: method '$first->{method}' provided " . |
|
355
|
|
|
|
|
|
|
"by both '$first->{existing_role}' " . |
|
356
|
|
|
|
|
|
|
"and '$first->{new_role}'"; |
|
357
|
|
|
|
|
|
|
} |
|
358
|
|
|
|
|
|
|
} |
|
359
|
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
# Install methods |
|
361
|
42
|
50
|
|
|
|
77
|
unless ($IS_ROLE{$class}) { |
|
362
|
30
|
|
|
30
|
|
198
|
no strict 'refs'; |
|
|
30
|
|
|
|
|
39
|
|
|
|
30
|
|
|
|
|
1248
|
|
|
363
|
30
|
|
|
30
|
|
133
|
no warnings 'redefine'; |
|
|
30
|
|
|
|
|
98
|
|
|
|
30
|
|
|
|
|
3360
|
|
|
364
|
42
|
|
|
|
|
53
|
foreach my $name (@methods_to_copy) { |
|
365
|
49
|
|
66
|
|
|
107
|
my $install_name = $aliases_for_role->{$name} || $name; |
|
366
|
49
|
100
|
|
|
|
124
|
next if $class->can($install_name); # class method wins |
|
367
|
40
|
|
|
|
|
36
|
*{"${class}::${install_name}"} = *{"${role}::${name}"}{CODE}; |
|
|
40
|
|
|
|
|
121
|
|
|
|
40
|
|
|
|
|
110
|
|
|
368
|
|
|
|
|
|
|
} |
|
369
|
|
|
|
|
|
|
} |
|
370
|
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
# Add role to @ISA |
|
372
|
30
|
|
|
30
|
|
218
|
no strict 'refs'; |
|
|
30
|
|
|
|
|
87
|
|
|
|
30
|
|
|
|
|
8530
|
|
|
373
|
42
|
|
|
|
|
389
|
push @{"${class}::ISA"}, $role |
|
374
|
42
|
50
|
|
|
|
52
|
unless grep { $_ eq $role } @{"${class}::ISA"}; |
|
|
8
|
|
|
|
|
24
|
|
|
|
42
|
|
|
|
|
136
|
|
|
375
|
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
# Add does() method |
|
377
|
42
|
|
|
|
|
134
|
_add_does_method($class); |
|
378
|
|
|
|
|
|
|
} |
|
379
|
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
sub _apply_role_attributes { |
|
381
|
52
|
|
|
52
|
|
96
|
my ($class, $role) = @_; |
|
382
|
52
|
|
100
|
|
|
156
|
my $role_attrs = $ROLE_ATTRIBUTES{$role} || {}; |
|
383
|
52
|
|
|
|
|
144
|
my $can_handle_attributes = _class_can_handle_attributes($class); |
|
384
|
|
|
|
|
|
|
|
|
385
|
52
|
50
|
66
|
|
|
204
|
if (!$can_handle_attributes && %$role_attrs) { |
|
386
|
0
|
|
|
|
|
0
|
return; |
|
387
|
|
|
|
|
|
|
} |
|
388
|
|
|
|
|
|
|
|
|
389
|
52
|
|
|
|
|
68
|
eval { require Class::More }; |
|
|
52
|
|
|
|
|
4550
|
|
|
390
|
52
|
50
|
|
|
|
111
|
return if $@; |
|
391
|
|
|
|
|
|
|
|
|
392
|
30
|
|
|
30
|
|
186
|
no strict 'refs'; |
|
|
30
|
|
|
|
|
67
|
|
|
|
30
|
|
|
|
|
6912
|
|
|
393
|
52
|
|
|
|
|
159
|
foreach my $attr_name (keys %$role_attrs) { |
|
394
|
8
|
|
|
|
|
8
|
my $attr_spec = $role_attrs->{$attr_name}; |
|
395
|
|
|
|
|
|
|
$Class::More::ATTRIBUTES{$class} = {} |
|
396
|
8
|
100
|
|
|
|
16
|
unless exists $Class::More::ATTRIBUTES{$class}; |
|
397
|
8
|
|
|
|
|
10
|
$Class::More::ATTRIBUTES{$class}{$attr_name} = $attr_spec; |
|
398
|
|
|
|
|
|
|
|
|
399
|
8
|
50
|
|
|
|
6
|
if (!defined *{"${class}::${attr_name}"}{CODE}) { |
|
|
8
|
|
|
|
|
28
|
|
|
400
|
8
|
|
|
|
|
21
|
*{"${class}::${attr_name}"} = sub { |
|
401
|
10
|
|
|
10
|
|
3762
|
my $self = shift; |
|
402
|
10
|
50
|
|
|
|
17
|
if (@_) { |
|
403
|
0
|
|
|
|
|
0
|
$self->{$attr_name} = shift; |
|
404
|
|
|
|
|
|
|
} |
|
405
|
10
|
|
|
|
|
27
|
return $self->{$attr_name}; |
|
406
|
8
|
|
|
|
|
42
|
}; |
|
407
|
|
|
|
|
|
|
} |
|
408
|
|
|
|
|
|
|
} |
|
409
|
|
|
|
|
|
|
} |
|
410
|
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
sub _find_method_origin { |
|
412
|
15
|
|
|
15
|
|
21
|
my ($class, $method) = @_; |
|
413
|
15
|
|
|
|
|
26
|
my $cache_key = "$class|$method"; |
|
414
|
|
|
|
|
|
|
return $METHOD_ORIGIN_CACHE{$cache_key} |
|
415
|
15
|
100
|
|
|
|
38
|
if exists $METHOD_ORIGIN_CACHE{$cache_key}; |
|
416
|
|
|
|
|
|
|
|
|
417
|
30
|
|
|
30
|
|
208
|
no strict 'refs'; |
|
|
30
|
|
|
|
|
63
|
|
|
|
30
|
|
|
|
|
7671
|
|
|
418
|
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
# First check if method exists in the class itself |
|
420
|
14
|
50
|
|
|
|
13
|
if (defined &{"${class}::${method}"}) { |
|
|
14
|
|
|
|
|
40
|
|
|
421
|
|
|
|
|
|
|
# Check if it comes from an applied role |
|
422
|
14
|
50
|
|
|
|
28
|
if ($APPLIED_ROLES{$class}) { |
|
423
|
14
|
|
|
|
|
16
|
foreach my $role (@{$APPLIED_ROLES{$class}}) { |
|
|
14
|
|
|
|
|
41
|
|
|
424
|
16
|
|
50
|
|
|
42
|
my $aliases = $METHOD_ALIASES{$class}->{$role} || {}; |
|
425
|
16
|
|
|
|
|
30
|
my %reverse_aliases = reverse %$aliases; |
|
426
|
16
|
|
33
|
|
|
74
|
my $original_name = $reverse_aliases{$method} || $method; |
|
427
|
|
|
|
|
|
|
|
|
428
|
16
|
100
|
66
|
|
|
52
|
if (defined &{"${role}::${original_name}"} |
|
|
16
|
|
|
|
|
66
|
|
|
429
|
|
|
|
|
|
|
|| exists $reverse_aliases{$method}) { |
|
430
|
14
|
|
|
|
|
48
|
return $METHOD_ORIGIN_CACHE{$cache_key} = $role; |
|
431
|
|
|
|
|
|
|
} |
|
432
|
|
|
|
|
|
|
} |
|
433
|
|
|
|
|
|
|
} |
|
434
|
|
|
|
|
|
|
# If not from a role, it's from the class itself |
|
435
|
0
|
|
|
|
|
0
|
return $METHOD_ORIGIN_CACHE{$cache_key} = $class; |
|
436
|
|
|
|
|
|
|
} |
|
437
|
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
# Check inheritance chain |
|
439
|
0
|
|
|
|
|
0
|
for my $parent (@{"${class}::ISA"}) { |
|
|
0
|
|
|
|
|
0
|
|
|
440
|
0
|
0
|
|
|
|
0
|
if ($parent->can($method)) { |
|
441
|
0
|
|
|
|
|
0
|
return $METHOD_ORIGIN_CACHE{$cache_key} = $parent; |
|
442
|
|
|
|
|
|
|
} |
|
443
|
|
|
|
|
|
|
} |
|
444
|
|
|
|
|
|
|
|
|
445
|
0
|
|
|
|
|
0
|
return $METHOD_ORIGIN_CACHE{$cache_key} = ''; |
|
446
|
|
|
|
|
|
|
} |
|
447
|
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
sub _class_does_role { |
|
449
|
20
|
|
|
20
|
|
42
|
my ($class, $role) = @_; |
|
450
|
20
|
50
|
|
|
|
53
|
return 0 unless $IS_ROLE{$role}; |
|
451
|
30
|
|
|
30
|
|
166
|
no strict 'refs'; |
|
|
30
|
|
|
|
|
49
|
|
|
|
30
|
|
|
|
|
3895
|
|
|
452
|
20
|
100
|
|
|
|
29
|
return 1 if grep { $_ eq $role } @{"${class}::ISA"}; |
|
|
20
|
|
|
|
|
122
|
|
|
|
20
|
|
|
|
|
70
|
|
|
453
|
|
|
|
|
|
|
return 1 if ($APPLIED_ROLES{$class} |
|
454
|
4
|
50
|
33
|
|
|
15
|
&& grep { $_ eq $role } @{$APPLIED_ROLES{$class}}); |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
455
|
4
|
|
|
|
|
26
|
return 0; |
|
456
|
|
|
|
|
|
|
} |
|
457
|
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
sub _add_does_method { |
|
459
|
76
|
|
|
76
|
|
117
|
my ($class) = @_; |
|
460
|
30
|
|
|
30
|
|
150
|
no strict 'refs'; |
|
|
30
|
|
|
|
|
52
|
|
|
|
30
|
|
|
|
|
874
|
|
|
461
|
30
|
|
|
30
|
|
153
|
no warnings 'redefine'; |
|
|
30
|
|
|
|
|
44
|
|
|
|
30
|
|
|
|
|
6238
|
|
|
462
|
76
|
|
|
|
|
370
|
*{"${class}::does"} = sub { |
|
463
|
13
|
|
|
13
|
|
12836
|
my ($self, $role) = @_; |
|
464
|
13
|
|
33
|
|
|
94
|
return _class_does_role(ref($self) || $self, $role); |
|
465
|
76
|
|
|
|
|
254
|
}; |
|
466
|
|
|
|
|
|
|
} |
|
467
|
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
=head1 NAME |
|
469
|
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
Role - A simple role system for Perl |
|
471
|
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
=head1 VERSION |
|
473
|
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
Version v0.1.1 |
|
475
|
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
477
|
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
=head2 Creating Roles |
|
479
|
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
package Role::Printable; |
|
481
|
|
|
|
|
|
|
use Role; |
|
482
|
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
requires 'to_string'; # Classes must implement this |
|
484
|
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
sub print { |
|
486
|
|
|
|
|
|
|
my $self = shift; |
|
487
|
|
|
|
|
|
|
print $self->to_string . "\n"; |
|
488
|
|
|
|
|
|
|
} |
|
489
|
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
1; |
|
491
|
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
package Role::Serialisable; |
|
493
|
|
|
|
|
|
|
use Role; |
|
494
|
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
requires 'serialize', 'deserialize'; |
|
496
|
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
sub to_json { |
|
498
|
|
|
|
|
|
|
my $self = shift; |
|
499
|
|
|
|
|
|
|
# ... implementation |
|
500
|
|
|
|
|
|
|
} |
|
501
|
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
1; |
|
503
|
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
=head2 Using Roles in Classes |
|
505
|
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
package My::Class; |
|
507
|
|
|
|
|
|
|
use Class; |
|
508
|
|
|
|
|
|
|
with qw/Role::Printable Role::Serialisable/; |
|
509
|
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
sub to_string { |
|
511
|
|
|
|
|
|
|
my $self = shift; |
|
512
|
|
|
|
|
|
|
return "My::Class instance"; |
|
513
|
|
|
|
|
|
|
} |
|
514
|
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
sub serialize { ... } |
|
516
|
|
|
|
|
|
|
sub deserialize { ... } |
|
517
|
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
1; |
|
519
|
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
=head2 Applying Roles at Runtime |
|
521
|
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
package My::Class; |
|
523
|
|
|
|
|
|
|
use Class; |
|
524
|
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
# Later, apply roles dynamically |
|
526
|
|
|
|
|
|
|
Role::apply_role(__PACKAGE__, 'Role::Printable'); |
|
527
|
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
1; |
|
529
|
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
=head2 Role Aliasing |
|
531
|
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
package My::Class; |
|
533
|
|
|
|
|
|
|
use Class; |
|
534
|
|
|
|
|
|
|
use Role::Printable => { |
|
535
|
|
|
|
|
|
|
role => 'Role::Printable', |
|
536
|
|
|
|
|
|
|
alias => { print => 'display' } |
|
537
|
|
|
|
|
|
|
}; |
|
538
|
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
# Now use $obj->display() instead of $obj->print() |
|
540
|
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
=head2 Role Composition with Exclusions |
|
542
|
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
package Role::A; |
|
544
|
|
|
|
|
|
|
use Role; |
|
545
|
|
|
|
|
|
|
excludes 'Role::B'; # Cannot be used with Role::B |
|
546
|
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
package Role::B; |
|
548
|
|
|
|
|
|
|
use Role; |
|
549
|
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
package My::Class; |
|
551
|
|
|
|
|
|
|
use Class; |
|
552
|
|
|
|
|
|
|
use Role::A; # OK |
|
553
|
|
|
|
|
|
|
# use Role::B; # This would die |
|
554
|
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
556
|
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
Role provides a simple, efficient role system for Perl. Roles are reusable units |
|
558
|
|
|
|
|
|
|
of behavior that can be composed into classes. They support requirements, |
|
559
|
|
|
|
|
|
|
method conflicts detection, aliasing, and runtime application. |
|
560
|
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
This module is designed to work with any class system but integrates particularly |
|
562
|
|
|
|
|
|
|
well with L. |
|
563
|
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
=head1 FEATURES |
|
565
|
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
=head2 Core Features |
|
567
|
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
=over 4 |
|
569
|
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
=item * B: Roles can declare methods that consuming classes must implement |
|
571
|
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
=item * B: Automatic detection of method conflicts between roles |
|
573
|
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
=item * B: Rename methods when applying roles to avoid conflicts |
|
575
|
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
=item * B: Roles can declare incompatible roles |
|
577
|
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
=item * B: Apply roles to classes at runtime |
|
579
|
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
=item * B: Simple attribute storage with accessors |
|
581
|
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
=item * B: Method and role caching for better performance |
|
583
|
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
=item * B: Class methods silently override role methods |
|
585
|
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
=back |
|
587
|
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
=head2 Advanced Features |
|
589
|
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
=over 4 |
|
591
|
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
=item * B: Detects conflicts between multiple roles before application |
|
593
|
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
=item * B: Supports applying roles one at a time with proper conflict checking |
|
595
|
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
=item * B: Understands method inheritance chains |
|
597
|
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
=item * B: Tracks which roles are applied to each class |
|
599
|
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
=back |
|
601
|
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
=head1 METHODS |
|
603
|
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
=head2 Role Definition Methods |
|
605
|
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
These methods are available in packages that C |
|
607
|
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
=head3 requires |
|
609
|
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
requires 'method1', 'method2'; |
|
611
|
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
Declares that consuming classes must implement the specified methods. |
|
613
|
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
=head3 excludes |
|
615
|
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
excludes 'Role::Incompatible', 'Role::Conflicting'; |
|
617
|
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
Declares that this role cannot be composed with the specified roles. |
|
619
|
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
=head3 has |
|
621
|
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
has 'attribute_name'; |
|
623
|
|
|
|
|
|
|
has 'attribute_name' => ( default => 'value' ); |
|
624
|
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
Defines a simple attribute in the role. Creates a basic accessor method. |
|
626
|
|
|
|
|
|
|
The attribute specification can include: |
|
627
|
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
=over 4 |
|
629
|
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
=item * C - Default value for the attribute |
|
631
|
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
=back |
|
633
|
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
Note: This provides basic attribute storage. For advanced attribute features |
|
635
|
|
|
|
|
|
|
like type constraints, coercion, or lazy building, use a full-featured |
|
636
|
|
|
|
|
|
|
class system. |
|
637
|
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
=head2 Role Application Methods |
|
639
|
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
=head3 with |
|
641
|
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
package My::Class; |
|
643
|
|
|
|
|
|
|
use Class; |
|
644
|
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
with 'Role::A', 'Role::B'; |
|
646
|
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
# With aliasing |
|
648
|
|
|
|
|
|
|
with |
|
649
|
|
|
|
|
|
|
{ role => 'Role::A', alias => { method_a => 'new_name' } }, |
|
650
|
|
|
|
|
|
|
'Role::B'; |
|
651
|
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
Composes roles into a class. Can be called as a class method. |
|
653
|
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
=head3 apply_role |
|
655
|
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
Role::apply_role('My::Class', 'Role::Printable'); |
|
657
|
|
|
|
|
|
|
Role::apply_role($object, 'Role::Serialisable'); |
|
658
|
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
Applies a role to a class or object at runtime. Returns true on success. |
|
660
|
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
=head2 Query Methods |
|
662
|
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
=head3 does |
|
664
|
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
if ($object->does('Role::Printable')) { |
|
666
|
|
|
|
|
|
|
$object->print; |
|
667
|
|
|
|
|
|
|
} |
|
668
|
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
Checks if a class or object consumes a specific role. |
|
670
|
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
=head3 get_applied_roles |
|
672
|
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
my @roles = Role::get_applied_roles('My::Class'); |
|
674
|
|
|
|
|
|
|
my @roles = Role::get_applied_roles($object); |
|
675
|
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
Returns the list of roles applied to a class. |
|
677
|
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
=head3 is_role |
|
679
|
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
if (Role::is_role('Role::Printable')) { |
|
681
|
|
|
|
|
|
|
# It's a role |
|
682
|
|
|
|
|
|
|
} |
|
683
|
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
Checks if a package is a role. |
|
685
|
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
=head1 EXAMPLES |
|
687
|
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
=head2 Basic Role with Requirements |
|
689
|
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
package Role::Validator; |
|
691
|
|
|
|
|
|
|
use Role; |
|
692
|
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
requires 'validate', 'get_errors'; |
|
694
|
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
sub is_valid { |
|
696
|
|
|
|
|
|
|
my $self = shift; |
|
697
|
|
|
|
|
|
|
return $self->validate && !@{$self->get_errors}; |
|
698
|
|
|
|
|
|
|
} |
|
699
|
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
1; |
|
701
|
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
=head2 Role with Simple Attributes |
|
703
|
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
package Role::Timestamped; |
|
705
|
|
|
|
|
|
|
use Role; |
|
706
|
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
has 'created_at' => ( default => sub { time } ); |
|
708
|
|
|
|
|
|
|
has 'updated_at' => ( default => sub { time } ); |
|
709
|
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
sub update_timestamp { |
|
711
|
|
|
|
|
|
|
my $self = shift; |
|
712
|
|
|
|
|
|
|
$self->updated_at(time); |
|
713
|
|
|
|
|
|
|
} |
|
714
|
|
|
|
|
|
|
|
|
715
|
|
|
|
|
|
|
1; |
|
716
|
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
# Usage in class: |
|
718
|
|
|
|
|
|
|
package My::Class; |
|
719
|
|
|
|
|
|
|
use Class; |
|
720
|
|
|
|
|
|
|
with qw/Role::Timestamped/; |
|
721
|
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
sub new { |
|
723
|
|
|
|
|
|
|
my ($class, %args) = @_; |
|
724
|
|
|
|
|
|
|
my $self = bless \%args, $class; |
|
725
|
|
|
|
|
|
|
$self->created_at(time) unless $self->created_at; |
|
726
|
|
|
|
|
|
|
return $self; |
|
727
|
|
|
|
|
|
|
} |
|
728
|
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
1; |
|
730
|
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
=head2 Role with Aliasing |
|
732
|
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
package My::Class; |
|
734
|
|
|
|
|
|
|
use Class; |
|
735
|
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
# Avoid conflict by aliasing |
|
737
|
|
|
|
|
|
|
with |
|
738
|
|
|
|
|
|
|
{ role => 'Role::Logger', alias => { log => 'file_log' } }, |
|
739
|
|
|
|
|
|
|
{ role => 'Role::Debug', alias => { log => 'debug_log' } }; |
|
740
|
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
sub log { |
|
742
|
|
|
|
|
|
|
my ($self, $message) = @_; |
|
743
|
|
|
|
|
|
|
$self->file_log($message); |
|
744
|
|
|
|
|
|
|
$self->debug_log($message); |
|
745
|
|
|
|
|
|
|
} |
|
746
|
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
1; |
|
748
|
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
=head2 Runtime Role Application |
|
750
|
|
|
|
|
|
|
|
|
751
|
|
|
|
|
|
|
package PluginSystem; |
|
752
|
|
|
|
|
|
|
use Role; |
|
753
|
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
sub load_plugin { |
|
755
|
|
|
|
|
|
|
my ($self, $plugin_role) = @_; |
|
756
|
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
unless (Role::is_role($plugin_role)) { |
|
758
|
|
|
|
|
|
|
die "$plugin_role is not a role"; |
|
759
|
|
|
|
|
|
|
} |
|
760
|
|
|
|
|
|
|
|
|
761
|
|
|
|
|
|
|
# Apply the plugin role to this instance's class |
|
762
|
|
|
|
|
|
|
Role::apply_role($self, $plugin_role); |
|
763
|
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
return $self; |
|
765
|
|
|
|
|
|
|
} |
|
766
|
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
1; |
|
768
|
|
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
=head1 ATTRIBUTE SUPPORT |
|
770
|
|
|
|
|
|
|
|
|
771
|
|
|
|
|
|
|
The C method in roles provides basic attribute functionality: |
|
772
|
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
=over 4 |
|
774
|
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
=item * Creates a simple accessor method |
|
776
|
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
=item * Supports default values |
|
778
|
|
|
|
|
|
|
|
|
779
|
|
|
|
|
|
|
=item * Stores data in the object hash |
|
780
|
|
|
|
|
|
|
|
|
781
|
|
|
|
|
|
|
=back |
|
782
|
|
|
|
|
|
|
|
|
783
|
|
|
|
|
|
|
However, this is I attribute support. For advanced attribute features |
|
784
|
|
|
|
|
|
|
like: |
|
785
|
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
=over 4 |
|
787
|
|
|
|
|
|
|
|
|
788
|
|
|
|
|
|
|
=item * Read-only/read-write access control |
|
789
|
|
|
|
|
|
|
|
|
790
|
|
|
|
|
|
|
=item * Type constraints |
|
791
|
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
=item * Lazy evaluation |
|
793
|
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
=item * Triggers and coercion |
|
795
|
|
|
|
|
|
|
|
|
796
|
|
|
|
|
|
|
=item * Initialisation hooks |
|
797
|
|
|
|
|
|
|
|
|
798
|
|
|
|
|
|
|
=back |
|
799
|
|
|
|
|
|
|
|
|
800
|
|
|
|
|
|
|
You should use a full-featured class system like L, L, or |
|
801
|
|
|
|
|
|
|
L and apply roles from those systems instead. |
|
802
|
|
|
|
|
|
|
|
|
803
|
|
|
|
|
|
|
=head1 PERFORMANCE |
|
804
|
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
The module includes several performance optimisations: |
|
806
|
|
|
|
|
|
|
|
|
807
|
|
|
|
|
|
|
=over 4 |
|
808
|
|
|
|
|
|
|
|
|
809
|
|
|
|
|
|
|
=item * Method origin caching to avoid repeated lookups |
|
810
|
|
|
|
|
|
|
|
|
811
|
|
|
|
|
|
|
=item * Role loading caching to prevent redundant requires |
|
812
|
|
|
|
|
|
|
|
|
813
|
|
|
|
|
|
|
=item * Precomputed role method lists |
|
814
|
|
|
|
|
|
|
|
|
815
|
|
|
|
|
|
|
=item * Skip patterns for common non-method symbols |
|
816
|
|
|
|
|
|
|
|
|
817
|
|
|
|
|
|
|
=back |
|
818
|
|
|
|
|
|
|
|
|
819
|
|
|
|
|
|
|
For best performance, apply roles at compile time when possible. |
|
820
|
|
|
|
|
|
|
|
|
821
|
|
|
|
|
|
|
=head1 LIMITATIONS |
|
822
|
|
|
|
|
|
|
|
|
823
|
|
|
|
|
|
|
=head2 Known Limitations |
|
824
|
|
|
|
|
|
|
|
|
825
|
|
|
|
|
|
|
=over 4 |
|
826
|
|
|
|
|
|
|
|
|
827
|
|
|
|
|
|
|
=item * B: |
|
828
|
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
Only simple attributes with default values are supported. No advanced features like read-only, type constraints, or lazy building. |
|
830
|
|
|
|
|
|
|
|
|
831
|
|
|
|
|
|
|
=item * B: |
|
832
|
|
|
|
|
|
|
|
|
833
|
|
|
|
|
|
|
Deep inheritance hierarchies may have unexpected method resolution behavior. |
|
834
|
|
|
|
|
|
|
|
|
835
|
|
|
|
|
|
|
=item * B: |
|
836
|
|
|
|
|
|
|
|
|
837
|
|
|
|
|
|
|
Applying roles sequentially vs. batched can produce different conflict detection results. |
|
838
|
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
=item * B: |
|
840
|
|
|
|
|
|
|
|
|
841
|
|
|
|
|
|
|
Does not support method modifiers (before, after, around) like Moose roles. |
|
842
|
|
|
|
|
|
|
|
|
843
|
|
|
|
|
|
|
=item * B: |
|
844
|
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
Roles cannot take parameters at composition time. |
|
846
|
|
|
|
|
|
|
|
|
847
|
|
|
|
|
|
|
=item * B: |
|
848
|
|
|
|
|
|
|
|
|
849
|
|
|
|
|
|
|
Limited handling of diamond inheritance patterns in role composition. |
|
850
|
|
|
|
|
|
|
|
|
851
|
|
|
|
|
|
|
=item * B: |
|
852
|
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
No rich meta-object protocol for introspection. |
|
854
|
|
|
|
|
|
|
|
|
855
|
|
|
|
|
|
|
=back |
|
856
|
|
|
|
|
|
|
|
|
857
|
|
|
|
|
|
|
=head2 Attribute Limitations |
|
858
|
|
|
|
|
|
|
|
|
859
|
|
|
|
|
|
|
The attribute system is intentionally minimal: |
|
860
|
|
|
|
|
|
|
|
|
861
|
|
|
|
|
|
|
# Supported: |
|
862
|
|
|
|
|
|
|
has 'name'; |
|
863
|
|
|
|
|
|
|
has 'count' => ( default => 0 ); |
|
864
|
|
|
|
|
|
|
has 'items' => ( default => sub { [] } ); |
|
865
|
|
|
|
|
|
|
|
|
866
|
|
|
|
|
|
|
# NOT supported: |
|
867
|
|
|
|
|
|
|
has 'name' => ( is => 'ro' ); # No access control |
|
868
|
|
|
|
|
|
|
has 'count' => ( isa => 'Int' ); # No type constraints |
|
869
|
|
|
|
|
|
|
has 'items' => ( lazy => 1 ); # No lazy building |
|
870
|
|
|
|
|
|
|
has 'score' => ( trigger => \&_validate_score ); # No triggers |
|
871
|
|
|
|
|
|
|
|
|
872
|
|
|
|
|
|
|
=head2 Conflict Resolution Limitations |
|
873
|
|
|
|
|
|
|
|
|
874
|
|
|
|
|
|
|
=over 4 |
|
875
|
|
|
|
|
|
|
|
|
876
|
|
|
|
|
|
|
=item * Class methods always silently win over role methods |
|
877
|
|
|
|
|
|
|
|
|
878
|
|
|
|
|
|
|
=item * No built-in way to explicitly override role methods |
|
879
|
|
|
|
|
|
|
|
|
880
|
|
|
|
|
|
|
=item * No method selection or combination features |
|
881
|
|
|
|
|
|
|
|
|
882
|
|
|
|
|
|
|
=item * Aliasing is the primary conflict resolution mechanism |
|
883
|
|
|
|
|
|
|
|
|
884
|
|
|
|
|
|
|
=back |
|
885
|
|
|
|
|
|
|
|
|
886
|
|
|
|
|
|
|
=head2 Compatibility Limitations |
|
887
|
|
|
|
|
|
|
|
|
888
|
|
|
|
|
|
|
=over 4 |
|
889
|
|
|
|
|
|
|
|
|
890
|
|
|
|
|
|
|
=item * Designed to work with simple class systems and L |
|
891
|
|
|
|
|
|
|
|
|
892
|
|
|
|
|
|
|
=item * May have issues with some class builders that don't follow standard Perl OO |
|
893
|
|
|
|
|
|
|
|
|
894
|
|
|
|
|
|
|
=item * No Moose/Mouse compatibility layer |
|
895
|
|
|
|
|
|
|
|
|
896
|
|
|
|
|
|
|
=item * Limited support for role versioning |
|
897
|
|
|
|
|
|
|
|
|
898
|
|
|
|
|
|
|
=back |
|
899
|
|
|
|
|
|
|
|
|
900
|
|
|
|
|
|
|
=head1 DIAGNOSTICS |
|
901
|
|
|
|
|
|
|
|
|
902
|
|
|
|
|
|
|
=head2 Common Errors |
|
903
|
|
|
|
|
|
|
|
|
904
|
|
|
|
|
|
|
=over 4 |
|
905
|
|
|
|
|
|
|
|
|
906
|
|
|
|
|
|
|
=item * C<"Failed to load role 'Role::Name': ..."> |
|
907
|
|
|
|
|
|
|
|
|
908
|
|
|
|
|
|
|
The specified role could not be loaded. Make sure the role package exists and uses C |
|
909
|
|
|
|
|
|
|
|
|
910
|
|
|
|
|
|
|
=item * C<"Conflict: method 'method_name' provided by both 'Role::A' and 'Role::B'..."> |
|
911
|
|
|
|
|
|
|
|
|
912
|
|
|
|
|
|
|
Method conflict detected. Use aliasing or role exclusion to resolve. |
|
913
|
|
|
|
|
|
|
|
|
914
|
|
|
|
|
|
|
=item * C<"Role 'Role::Name' requires method(s) that are missing..."> |
|
915
|
|
|
|
|
|
|
|
|
916
|
|
|
|
|
|
|
The class doesn't implement all required methods specified by the role. |
|
917
|
|
|
|
|
|
|
|
|
918
|
|
|
|
|
|
|
=item * C<"Role 'Role::A' cannot be composed with role(s): Role::B"> |
|
919
|
|
|
|
|
|
|
|
|
920
|
|
|
|
|
|
|
Role exclusion violation. |
|
921
|
|
|
|
|
|
|
|
|
922
|
|
|
|
|
|
|
=item * C<"ROLE WARNING: Role 'Role::Name' has attributes that will be ignored"> |
|
923
|
|
|
|
|
|
|
|
|
924
|
|
|
|
|
|
|
Role defines attributes but the class doesn't support attribute handling. |
|
925
|
|
|
|
|
|
|
|
|
926
|
|
|
|
|
|
|
=back |
|
927
|
|
|
|
|
|
|
|
|
928
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
929
|
|
|
|
|
|
|
|
|
930
|
|
|
|
|
|
|
=over 4 |
|
931
|
|
|
|
|
|
|
|
|
932
|
|
|
|
|
|
|
=item * L - Simple class builder that works well with Role |
|
933
|
|
|
|
|
|
|
|
|
934
|
|
|
|
|
|
|
=item * L - Full-featured role system for Moose |
|
935
|
|
|
|
|
|
|
|
|
936
|
|
|
|
|
|
|
=item * L - Lightweight Moose-compatible roles |
|
937
|
|
|
|
|
|
|
|
|
938
|
|
|
|
|
|
|
=item * L - Minimalist role system |
|
939
|
|
|
|
|
|
|
|
|
940
|
|
|
|
|
|
|
=item * L - Roles for Moo classes |
|
941
|
|
|
|
|
|
|
|
|
942
|
|
|
|
|
|
|
=back |
|
943
|
|
|
|
|
|
|
|
|
944
|
|
|
|
|
|
|
=head1 AUTHOR |
|
945
|
|
|
|
|
|
|
|
|
946
|
|
|
|
|
|
|
Mohammad Sajid Anwar, C<< >> |
|
947
|
|
|
|
|
|
|
|
|
948
|
|
|
|
|
|
|
=head1 REPOSITORY |
|
949
|
|
|
|
|
|
|
|
|
950
|
|
|
|
|
|
|
L |
|
951
|
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
=head1 BUGS |
|
953
|
|
|
|
|
|
|
|
|
954
|
|
|
|
|
|
|
Please report any bugs or feature requests through the web interface at L. |
|
955
|
|
|
|
|
|
|
I will be notified and then you'll automatically be notified of progress on your bug as I make changes. |
|
956
|
|
|
|
|
|
|
|
|
957
|
|
|
|
|
|
|
=head1 SUPPORT |
|
958
|
|
|
|
|
|
|
|
|
959
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command. |
|
960
|
|
|
|
|
|
|
|
|
961
|
|
|
|
|
|
|
perldoc Role |
|
962
|
|
|
|
|
|
|
|
|
963
|
|
|
|
|
|
|
You can also look for information at: |
|
964
|
|
|
|
|
|
|
|
|
965
|
|
|
|
|
|
|
=over 4 |
|
966
|
|
|
|
|
|
|
|
|
967
|
|
|
|
|
|
|
=item * BUG Report |
|
968
|
|
|
|
|
|
|
|
|
969
|
|
|
|
|
|
|
L |
|
970
|
|
|
|
|
|
|
|
|
971
|
|
|
|
|
|
|
=back |
|
972
|
|
|
|
|
|
|
|
|
973
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT |
|
974
|
|
|
|
|
|
|
|
|
975
|
|
|
|
|
|
|
Copyright (C) 2025 Mohammad Sajid Anwar. |
|
976
|
|
|
|
|
|
|
|
|
977
|
|
|
|
|
|
|
This program is free software; you can redistribute it and / or modify it under the terms of the the Artistic License (2.0). You may obtain a copy of the full license at: |
|
978
|
|
|
|
|
|
|
|
|
979
|
|
|
|
|
|
|
L |
|
980
|
|
|
|
|
|
|
|
|
981
|
|
|
|
|
|
|
Any use, modification, and distribution of the Standard or Modified Versions is governed by this Artistic License. By using, modifying or distributing the Package, you accept this license. Do not use, modify, or distribute the Package, if you do not accept this license. |
|
982
|
|
|
|
|
|
|
|
|
983
|
|
|
|
|
|
|
If your Modified Version has been derived from a Modified Version made by someone other than you, you are nevertheless required to ensure that your Modified Version complies with the requirements of this license. |
|
984
|
|
|
|
|
|
|
|
|
985
|
|
|
|
|
|
|
This license does not grant you the right to use any trademark, service mark, tradename, or logo of the Copyright Holder. |
|
986
|
|
|
|
|
|
|
|
|
987
|
|
|
|
|
|
|
This license includes the non-exclusive, worldwide, free-of-charge patent license to make, have made, use, offer to sell, sell, import and otherwise transfer the Package with respect to any patent claims licensable by the Copyright Holder that are necessarily infringed by the Package. If you institute patent litigation (including a cross-claim or counterclaim) against any party alleging that the Package constitutes direct or contributory patent infringement, then this Artistic License to you shall terminate on the date that such litigation is filed. |
|
988
|
|
|
|
|
|
|
|
|
989
|
|
|
|
|
|
|
Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
990
|
|
|
|
|
|
|
|
|
991
|
|
|
|
|
|
|
=cut |
|
992
|
|
|
|
|
|
|
|
|
993
|
|
|
|
|
|
|
1; # End of Role |
|
994
|
|
|
|
|
|
|
|