File Coverage

blib/lib/Role.pm
Criterion Covered Total %
statement 282 304 92.7
branch 88 120 73.3
condition 36 62 58.0
subroutine 41 43 95.3
pod 7 8 87.5
total 454 537 84.5


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