How can I discover all the roles a Perl 6 type does? - oop

With .does I can check if a type has the role I already know. I'd like to get the list of roles. Inheritance has .^mro but I didn't see anything like that for roles in the meta model stuff.
Along with that, given a "type", how can I tell if it was defined as a class or a role?

.^roles
say Rat.^roles; # ((Rational[Int,Int]) (Real) (Numeric))
By default it includes every role, including roles brought in by other roles. To only get the first level use :!transitive
Rat.^roles(:!transitive); # ((Rational[Int,Int]))

There's already a good answer to the first question. About the second one, each meta-object has an archetypes method that in turn carries a range of properties of the types represented by that meta-type. This exists because Perl 6 is open to new meta-types (which might be easier to think about as "types of type"); probably the most widely used example of this today is OO::Monitors. The archetypes are more focused on what one can do with the type. For example:
> role R { }; say "C: {.composable} I: {.inheritable}" given R.HOW.archetypes;
C: 1 I: 0
> class C { }; say "C: {.composable} I: {.inheritable}" given C.HOW.archetypes;
C: 0 I: 1
The set of available properties can be introspected:
> Int.HOW.archetypes.^methods(:local)
(nominal nominalizable inheritable inheritalizable composable
composalizable generic parametric coercive definite augmentable)
For example, "nominal" means "can this serve as a nominal type", and "augmentable" means "is it allowed to augment this kind of type". The things like "inheritalizable" mean "can I inheritalize such a type" - that is, turn it into a type that I can inherit from even if I can't inherit from this type. A role is not inheritable, but it is inheritalizable, and the inheritalize operation on it will produce the pun of the role. This is what is happening under the hood when writing something like class C is SomeRole { }, and means that not only is Perl 6 open to new types of type, but those new types of type can describe how they want to work, if at all, with inheritance and composition.
Being composable with does is probably the main defining property of a role, and thus the composable property is likely the best one to use when asking "is this a role". It is also possible to look at the type of the meta-object, as suggested in another answer, but there are multiple meta-objects involved in representing roles (the short name role group, a currying of that group with parameters, and an individual role, plus an internal concretization form that supports the composition process).
> say (role RRR[::T] { }).HOW.^name
Perl6::Metamodel::ParametricRoleHOW
> say RRR.HOW.^name
Perl6::Metamodel::ParametricRoleGroupHOW
> say RRR[Int].HOW.^name
Perl6::Metamodel::CurriedRoleHOW
Thus it's rather more robust to simply check if the thing is composable.
> say (role RRR[::T] { }).HOW.archetypes.composable
1
> say RRR.HOW.archetypes.composable
1
> say RRR[Int].HOW.archetypes.composable
1

Along with that, given a "type", how can I tell if it was defined as a class or a role?
A class is a type whose meta class is of type Metamodel::ClassHOW:
sub type-classify(Mu \t) {
given t.HOW {
return 'class' when Metamodel::ClassHOW;
return 'role' when Metamodel::ParametricRoleGroupHOW;
}
return 'other';
}
say type-classify(Int); # class
say type-classify(Rational); # role
say type-classify(Bool); # other

Regarding your second question,
given a "type", how can I tell if it was defined as a class or a role?
I haven't found a direct way of doing that. Both classes and roles have Mu in their hierarchy, so that will not distinguish them. However, only classes get to be recognized by (the curiously named) MetaModel::ClassHOW. So we can hack something like this:
role Ur { }
role F does Ur { }
class G does F { }
for Ur, F, G -> $class-or-role {
CATCH {
default {
say "not classy";
}
}
$class-or-role.say;
$class-or-role.^mro.say;
}
Which will print:
(Ur)
not classy
(F)
not classy
(G)
((G) (Any) (Mu))
, since calling ^mro on a role will raise an exception. That can be turned into a function for printing out which one is a role, and which is not.

Related

RBAC with Ory Keto and ownership of objects

I'm trying to achieve the following points with Ory Keto:
UserA has ownership of ProductA -> Ownership gives one CRUD rights.
UserB has the role Admin -> Admin gives one CRUD rights on everything.
UserA has a role KYCVerified or is part of a group named KYCVerified -> This gives the user additional permissions.
Point 1 describes the concept of ownership, which is described as one of the shortcomings of RBAC (source) with the current state of Ory Keto:
There is no concept of ownership: Dan is the author of article "Hello
World" and is thus allowed to update it.
Point 2 describes a role that basically passes the ownership check, since this role can do everything. This should be possible in the current state, but not in combination with point 1.
Point 3 describes basically the same thing as point 2, but this is more specific to my use case.
I've read this article on the Ory website and this article. However, I'm still unable to wrap my head around this concept. I've made the following example of how I see the concept of ownership with Ory Keto:
# Tenant TenantA needs to be a owner of product ProductA in order to view it
products:ProductA#view#(tenants:TenantA#owner)
# Tenant A is a owner of ProductA
tenants:ProductA#owner#TenantA
But this will result in a lot of rules and I'm not even sure if this is the way to go.
As of this moment you are right. You have to create a bunch of tuples manually. The full set of tuples should be something like:
products:ProductA#owner#UserA
products:ProductA#crud#(products:ProductA#owner)
roles:admin#member#UserB
products:ProductA#curd#(roles:admin#member)
products:ProductA#additional_permissions#(roles:KYCVerified#member)
roles:KYCVerified#member#UserA
With https://github.com/ory/keto/pull/877 you will be able to define global rewrites. It would looks similar to:
import { Context, Namespace } from #ory/keto-config
/**
* "User" is a namespace with no additional rewrite rules defined.
*/
class User implements Namespace {}
/**
* "Role"s only have members.
*/
class Role implements Namespace {
related: {
members: User[]
}
}
/**
* "Product" is a namespace representing a product. It has some rewrites.
*/
class Product implements Namespace {
// Relations are defined and type-annotated.
related: {
/**
* "owners" are the users that are the owners of the product.
*/
owners: User[]
/**
* "admins" are the roles that are administrators of this product (potentially only one).
*/
admins: Role[]
/**
* "special_roles" are the roles a user has to be member of to gain "additional_permissions"
*/
special_roles: Role[]
}
permits = {
// this is probably three/four rewrites (create, read, update, delete) with similar rules
crud: (ctx: Context): boolean =>
this.related.owners.includes(ctx.subject) ||
this.related.admins.some((admin) => admin.related.members.includes(ctx.subject)),
// for the additional_permissions one has to have curd and be member of a special role
additional_permissions: (ctx: Context): boolean =>
this.permits.crud(ctx) &&
this.related.special_roles.some((role) => role.related.members.includes(ctx.subject))
}
}
With that you have to create these tuples:
products:ProductA#owners#UserA
roles:admin#members#UserB
roles:KYCVerified#members#UserA
products:ProductA#admins#(roles:admin)
products:ProductA#additional_permissions#(roles:KYCVerified)
Please note that it is not possible (and not planned right now) to define a single admin group that would have access to everything. You always have to have some kind of relation between the object and subject to query/rewrite it. That is the reason for having the admins and special_roles relations.

Question about If/else/switch in Clean Architecture

I was reading Uncle Bob's blog, especially his article on if-else-switch.
I wanted to go deeper to know if I understood what he means.
Let's imagine that I have a web app with Users, I want to display User's profil depending his sex.
Some would simply do :
if(user.sex == 'female') return renderFemaleProfil(user)
if(user.sex == 'male') return renderMaleProfil(user)
return renderUnknownSexProfilError()
But to respect Clean Architecture Robert C Martin recommend to do DIP :
If I clearly understood :
I need an interface User. To create a fabric that return a polymorphic object Implementing User based on User's sex (class Male and class Female) as User
Done that, how do I display my profil ?
I put a renderProfil() function in both classes that return a html string ? And just do user.renderProfil() ?
And what if I just have a single part that is different in the Male and Female profils ?
Do I create renderPart1(), renderPart2() and renderPart3Male() for Male class and renderPart3Female() for Female class ? And call them in a single function renderProfil() ?
And is it worth it ?
Thank to enlighten me on those points :)
I guess your User is an clean architechture entity. Keep these entites clean from details like html rendering. Rendering a user to html is a detail. Thus you should either create a UserRenderer and pass it a User. E.g.
interface UserRenderer {
String render(User user);
}
You can then implement an HTMLUserRenderer or an JSONUserRenderer or whatever renderer you like.
Either the UserRenderer can do the if/then/else
String render(User user){
if(user.sex == 'female') return renderFemaleProfil(user)
if(user.sex == 'male') return renderMaleProfil(user)
return renderUnknownSexProfilError()
}
or you can implement a UserRendererFactory or UserRendererRegistry that will return an appropriate renderer.
These renderers belong to the outer circle of the clean architecture. So the dependency rule from the outer circle to the inner ones is honored. But you bypass the use case layer.
Maybe it would be better to introduce use cases and response models and let the renderer use the response model.
If you now go a little bit further you will recognize that a presenter is a more general renderer. Maybe the presenter uses renderers.

What is an opaque type in Elm and why is it valuable?

I've used types before but don't know what an opaque type is. I've seen it mentioned as well. Is it better to expose an opaque type than a type alias?
Let’s answer this question by first looking at type aliases:
A type alias is fully transparent. This means that any other module importing it will have full access to its inner workings. Let’s say we’ve got a User module exposing a User type:
module User exposing User
type alias User =
{ userName : String
, age : Int
}
Anyone importing User can manipulate the data, e.g. newUser = { oldUser | age = 25 }. Or do someUser = User "Bill" 27. These manipulations are fine when you have control over the context that they exist in.
However, if User is part of a library then every change to the User type is a breaking change to people that use the library. For example, if an email field is added to User, then the constructor example (someUser = User "Bill" 27) will give a compiler error.
Even inside of a project codebase, a type alias can provide too much information to other modules which leads to code that is difficult to maintain and evolve. Perhaps a User changes drastically at some point and has a completely new set of properties. This would require changes wherever the code manipulates Users.
Opaque types are valuable because they avoid these issues. Here’s an opaque version of User:
module User exposing User
type User =
User
{ userName : String
, age : Int
}
With this version, other modules cannot access or manipulate the data directly. Often, this means you will make and expose some getter and functions:
initUser : String -> Int -> User
userName : User -> String
age : User -> String
setAge : Int -> User -> User
This is more work, but it has advantages:
Other modules only care about the User functions and don’t need to know what data is in the type
The type can be updated without breaking code outside the containing module
Much of this explanation comes from #wintvelt: elmlang.slack.com

When designing classes, how do you deal with sub-types?

I'm learning about object oriented design as I go through my first real, albeit personal, project.
I'm at the stage where I'm going to build a user object. I have 4 user subtypes:
root-admin - webmaster, site owner
group-admin - leader of a group that has a membership and page (e.g. band manager)
group-member - user that can view, post, and comment as group member on the page and see private content (e.g. band member)
unassociated user - member of the entire site that can view and comment publicly (e.g. fan)
All group admins will be group members and all group members will be unassociated users as well. So basically it's a simple user permissions hierarchy.
With that said, how do I go about setting up the architecture of my class with respect to these levels? Is there a nesting technique? I'm just struggling to hash out the scaffolding here. I have my use cases and narratives written out already.
So you're implementing role-based access control. Each user has one of four roles. So a user is an object and the users's role is one of the attributes in a user object.
enum roles {
root_admin,
group_admin,
group_member,
unassociated_user
}
class user {
string id;
roles role;
}
Next, you need to enforce the business rules that allow users of certain roles to do certain activities.
Start with a simple solution (this is always a good policy):
enum activities {
view,
post,
add_comment
}
And implement a function or class whose job is to say if an activity is allowed for a given role.
class role_based_access_control {
private:
permissions perm;
public:
bool is_permitted(activities a, roles r) {
return perm[r].contain(a);
}
}
Then, in places in your code where you implement different activities, include a call to this function:
void add_comment_to_page(user u, comment c, page p) {
if (!role_base_access_control.is_permitted(add_comment,u.role))
throw "access_forbidden";
...
}
The important thing is to keep the role based access control rules centralized so that they are visible, easy to monitor, and to audit.

how to recognize object's responsibility?

I'm new in OOP and I just started learning it. Its too complicated to determine the functionality of classes. Let's take an example:
We have an Address-book and an user want to add a new contact to it.
In this scenario we have 2 classes:
User: that determine the user that logged in.
Contact: A contact object that consists of Name, Address, Phone Number, etc
And the questions:
Who have to save a new contact?User class or Contact Class
If we try to check the user's permission before doing anything where is the best place for it?
Is it OK that these classes have a access to database?(Is it better to create 3rd class for doing query stuffs?)
Thanks for any good idea ;)
Usable distribution of "responsibility" is an OOP design and architecture decision with no single simple correct answer. For discussion refer to Stack Overflow question What is the single most influential book every programmer should read?
You'll learn the pros/cons by coding (using someone's design or creating your own design which does not work well).
However there are some useful/frequent distributions of responsibility already known as http://en.wikipedia.org/wiki/Software_design_pattern
In my opinion the only fixed fact is that each class/function/structure should have its responsibility clearly defined/documented - since the very first lines of code - and "do one thing and do it well"
Contacts are user specific. Thus every user object (class instance) should contain its own contacts object which is a container of contact (other user) objects, comprising in turn of name, address, phone etc.
class User {
String name;
String phone;
String address;
Contacts contacts;
....
}
class Contacts {
List<User> items;
}
The Contacts class should have the implementation of saving a new contact, which needs to be called from a User method, something like the following.
User u;
Contacts c = u.getContacts();
c.addContact(name, address, phone);
User's permissions should be checked in the User class.
The methods of these classes should interface with the database. For this each class method can open a new connection to a database and execute SQL queries. Example method of User cass:
User getContact(String name) {
Connection conn = getConnection();
....
PreparedStatement ps = con.prepareStatement("select * from Contacts where name = ?");
...
return userRcd;
}
1) Save new contact must the separate class, which working directly with database
2) Best place to check user permission - in user class of course
3) See the item 1:)
I recommend you get strong knowledge about SOLID principles, it's basics for good design.