## ------------------------------------------------------------------------- ## ## Roles ## ------------------------------------------------------------------------- ## ^Role := ^Class.new({}); ^Role.set_name('Role'); ^Role.set_version('0.0.1'); ^Role.set_authority('url:pugscode.org'); ^Role.set_superclasses([ ^Module ]); ^Role.add_attribute('@!roles', []); ^Role.add_attribute('%!methods', {}); ^Role.add_attribute('%!attributes', {}); # NOTE: # These methods are pretty much duplicates of the # ones in ^Class, so we can just copy them here. ^Role.add_method('add_method', ^Class.get_method('add_method')); ^Role.add_method('has_method', ^Class.get_method('has_method')); ^Role.add_method('get_method', ^Class.get_method('get_method')); ^Role.add_method('get_method_list', ^Class.get_method('get_method_list')); ^Role.add_method('add_attribute', ^Class.get_method('add_attribute')); ^Role.add_method('has_attribute', ^Class.get_method('has_attribute')); ^Role.add_method('get_attribute', ^Class.get_method('get_attribute')); ^Role.add_method('get_attribute_list', ^Class.get_method('get_attribute_list')); ^Role.add_method('get_attributes', ^Class.get_method('get_attributes')); ^Role.add_method('roles', -> { self`get_attr('@!roles') }); ^Role.add_method('set_roles', -> @roles { self`set_attr('@!roles', @roles); }); ^Role.add_method('does', -> $role { self.name`eq($role)`if_else( -> { true }, -> { @roles := self.collect_all_roles(); @roles`is_empty`if_else( -> { false }, -> { -> @r { &redo := &?SUB; @r`is_empty`if_else( -> { false }, -> { @r`fetch(0).name`eq($role)`if_else( -> { true }, -> { &redo`(@r`splice(1)) } ) } ) }`(@roles); } ); } ); }); # NOTE: # We need to collect conflicts as we collect all the roles, # this will allow a role to resolve a conflict in it's subroles. # This means we probably should create some kind of "required" # list so that we can make sure our consuming class fufills all # these requirements. In other words,.. Roles are still pretty # broken :) ^Role.add_method('collect_all_roles', -> { -> $c, $role { &recurse := &?SUB; -> $r { -> { $c`set_attr_hash('%seen', $r.name(), 1); $c`set_attr('@roles', $c`get_attr('@roles')`push($r)); -> { -> $subrole { -> { $c`set_attr_hash('%seen', $subrole.name(), 1); $c`set_attr('@roles', $c`get_attr('@roles')`concat($subrole)); }`do_unless($c`get_attr('%seen')`exists($subrole.name())) }`do_for(&recurse`($c, $r)); }`do_unless($r.roles`is_empty()); }`do_unless($c`get_attr('%seen')`exists($r.name())) }`do_for($role.roles()); $c`get_attr('@roles'); }`(^`create('p6opaque', { '@roles' => [], '%seen' => {} }), self); }); ^Role.add_method('resolve', -> { $conflicts := ^`create('p6opaque', { 'methods' => {}, 'attrs' => {} }); -> $role { -> $method_name { $conflicts`get_attr('methods')`exists($method_name)`if_else( -> { self.remove_method($method_name) }, -> { $conflicts`set_attr_hash('methods', $method_name, 1); self.has_method($method_name)`if_else( -> { nil }, -> { self.add_method($method_name, $role.get_method($method_name)) } ); } ); }`do_for($role.get_method_list); -> $attribute_name { $conflicts`get_attr('attrs')`exists($attribute_name)`if_else( -> { self.remove_attribute($attribute_name) }, -> { $conflicts`set_attr_hash('attrs', $attribute_name, 1); self.has_attribute($attribute_name)`if_else( -> { nil }, -> { self.add_attribute($attribute_name, $role.get_attribute($attribute_name)) } ); } ); }`do_for($role.get_attribute_list); }`do_for(self.collect_all_roles()); -> { self.add_method('does', -> $role { self.does($role) }); }`do_unless(self.isa('Role')); });