{"id":15801974,"url":"https://github.com/zhandao/i_am_i_can","last_synced_at":"2025-09-03T03:37:46.442Z","repository":{"id":56877162,"uuid":"149858679","full_name":"zhandao/i_am_i_can","owner":"zhandao","description":"(RBAC like) Concise and Natural DSL for `Subject - Role(Role Group) - Permission - Resource` Management.","archived":false,"fork":false,"pushed_at":"2023-04-07T19:05:55.000Z","size":186,"stargazers_count":4,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-08-13T06:52:51.024Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zhandao.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-09-22T08:22:20.000Z","updated_at":"2023-04-07T18:55:36.000Z","dependencies_parsed_at":"2024-10-26T07:59:48.398Z","dependency_job_id":"85940436-0fdf-4311-b4dc-d7769175148c","html_url":"https://github.com/zhandao/i_am_i_can","commit_stats":{"total_commits":77,"total_committers":1,"mean_commits":77.0,"dds":0.0,"last_synced_commit":"84ee241ce388dc5b815f8772306cb601baffac7b"},"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/zhandao/i_am_i_can","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zhandao%2Fi_am_i_can","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zhandao%2Fi_am_i_can/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zhandao%2Fi_am_i_can/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zhandao%2Fi_am_i_can/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zhandao","download_url":"https://codeload.github.com/zhandao/i_am_i_can/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zhandao%2Fi_am_i_can/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273386417,"owners_count":25096244,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-03T02:00:09.631Z","response_time":76,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-05T01:41:34.038Z","updated_at":"2025-09-03T03:37:46.421Z","avatar_url":"https://github.com/zhandao.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# IAmICan\n\n[![Gem Version](https://badge.fury.io/rb/i_am_i_can.svg)](https://badge.fury.io/rb/i_am_i_can)\n[![Build Status](https://travis-ci.org/zhandao/i_am_i_can.svg?branch=master)](https://travis-ci.org/zhandao/i_am_i_can)\n[![Maintainability](https://api.codeclimate.com/v1/badges/27b664da01b6cc7180e3/maintainability)](https://codeclimate.com/github/zhandao/i_am_i_can/maintainability)\n[![Test Coverage](https://api.codeclimate.com/v1/badges/27b664da01b6cc7180e3/test_coverage)](https://codeclimate.com/github/zhandao/i_am_i_can/test_coverage)\n\nConcise and Natural DSL for `Subject - Role(Role Group) - Permission - Resource` Management (RBAC like).\n\n```ruby\n# our Subject is People, and subject is he:\nhe = People.take\n# let: Roles means PeopleRole, Groups means PeopleRoleGroup\n\n# Role\nPeople.have_role :admin # role definition\nhe.becomes_a :admin     # role assignment\nhe.is? :admin           # role querying =\u003e true\nhe.is? :someone_else    # role querying =\u003e false\n\n# Role Group\nPeople.have_and_group_roles :dev, :master, :committer, by_name: :team\nhe.becomes_a :master    # role assignment\nhe.in_role_group? :team # role group querying =\u003e true\n\n# Role - Permission\nPeople.have_role :coder            # role definition\nRoles.have_permission :fly         # permission definition\nRoles.which(name: :coder).can :fly # permission assignment (by predicate)\nhe.becomes_a :coder                # role assignment\nhe.can? :fly                       # permission querying\n\n# Role Group - Permission\nGroups.have_permission :manage, obj: User        # permission definition\nGroups.which(name: :team).can :manage, obj: User # permission assignment (by predicate and object)\nhe.is? :master                                   # yes\nhe.can? :manage, User                            # permission querying\n\n# more concise and faster way\nhe.becomes_a :magician, which_can: [:perform], obj: :magic\nhe.is? :magician # =\u003e true\nRoles.which(name: :magician).can? :perform, :magic # =\u003e true\nhe.can? :perform, :magic # =\u003e true\n\n# Cancel Assignment\nhe.falls_from :admin\nRoles.which(name: :coder).cannot :fly\n\n# Get allowed resources:\nResource.that_allow(user, to: :manage) # =\u003e ActiveRecord_Relation[]\n```\n\n## Table of Content\n\n1. [Concepts and Overview](#concepts-and-overview)\n    - [In one word](#in-one-word)\n    - [Definition and uniqueness of nouns](#definition-and-uniqueness-of-nouns)\n    - [About role group](#about-role-group)\n    - [Three steps to use this gem](#three-steps-to-use-this-gem)\n    - [Two Concepts of this gem](#two-concepts-of-this-gem)\n    - [How it work?](#how-it-work)\n\n2. [Installation and Setup](#installation-and-setup)\n\n3. [Usage](#usage)\n    - [Config Options](#config-options)\n    - [Methods and helpers](#methods-and-helpers)\n        - [A. Role Definition](#a-role-definition)\n        - [B. Grouping Roles](#b-grouping-roles)\n        - [C. Role Assignment](#c-role-assignment)\n        - [D. Role / Group Querying](#d-role--group-querying)\n        - [E. Permission Definition](#e-permission-definition)\n        - [F. Permission Assignment](#f-permission-assignment)\n        - [G. Permission Querying](#g-permission-querying)\n        - [H. Shortcut Combinations - which_can](#h-shortcut-combinations---which_can)\n        - [I. Resource Querying](#i-resource-querying)\n        - [J. Useful Helpers](#j-useful-helpers)\n\n## Concepts and Overview\n\n### In one word:\n```\n- role has permissions\n- subject has the roles\n\u003e subject has the permissions through the roles.\n```\n\n### Definition and uniqueness of nouns\n\n0. Subject\n    - Someone who can be assigned roles, and who has permissions through the assigned roles.\n    - See wiki [RBAC](https://en.wikipedia.org/wiki/Role-based_access_control)\n1. Role\n    - A job function that groups a series of permissions according to a certain dimension.\n    - Also see wiki [RBAC](https://en.wikipedia.org/wiki/Role-based_access_control)\n    - Uniquely identified by `name`\n2. Role Group\n    - A group of roles that may have the same permissions.\n    - Uniquely identified by `name`\n3. Permission\n    - An action, or an approval of a mode of access to a resource\n    - Also see wiki [RBAC](https://en.wikipedia.org/wiki/Role-based_access_control)\n    - Uniquely identified by `predicate( + object)` (name),\n      or we can say, `action( + resource)`\n4. Object (Resource)\n    - Polymorphic association with permissions\n\n### About role group?\n```\n- role group has permissions\n- roles are in the group\n- subject has one or more of the roles\n\u003e subject has the permissions through the role which is in the group\n```\n\n### Three steps to use this gem\n\n1. Querying\n    - Find if the given role is assigned to the subject\n    - Find if the given permission is assigned to the subject's roles / group\n    - instance methods, like: `user.can? :fly`\n2. Assignment\n    - assign role to subject, or assign permission to role / group\n    - instance methods, like: `user.has_role :admin`\n3. Definition\n    - the role or permission you want to assign **MUST** be defined before\n    - option :auto_definition (before assignment) you may need in some cases\n    - class methods, like: `UserRoleGroup.have_permission :fly`\n\n**Definition =\u003e Assignment =\u003e Querying**\n\n### Two Concepts of this gem\n\n1. Stored (save in database) TODO\n2. Temporary (save in instance variable) TODO\n\n### How it work?\n\nVery simple. Really simple. Sooooo Simple.\n\n1. To define something, you actually `create` records.\n    [see here](https://github.com/zhandao/i_am_i_can/blob/master/lib/i_am_i_can/helpers/dynamic.rb#L92)\n2. To assign something, you actually call one of the activerecord association methods.\n    [see here](https://github.com/zhandao/i_am_i_can/blob/master/lib/i_am_i_can/helpers/dynamic.rb#L67)\n2. To query  something, you actually call the querying interfaces of activerecord.\n    [see here](https://github.com/zhandao/i_am_i_can/tree/master/lib/i_am_i_can/subject)\n\n[Feature List: needs you](https://github.com/zhandao/i_am_i_can/issues/2)\n\n## Installation and Setup\n\n1. Add this line to your application's Gemfile and then `bundle`:\n\n    ```ruby\n    gem 'i_am_i_can'\n    ```\n\n2. Generate migrations and models by your subject name:\n    \n    ```bash\n    rails g i_am_i_can:setup \u003csubject_name\u003e\n    ```\n\n    For example, if your subject name is `user`, it will generate\n    model `UserRole`, `UserRoleGroup` and `UserPermission`\n\n3. Add the code returned by the generator to your subject model, like:\n\n    ```ruby\n    class User\n      has_and_belongs_to_many :stored_roles, -\u003e { where('expire_at IS NULL OR expire_at \u003e ?', Time.current) },\n                              join_table: 'users_and_user_roles', foreign_key: 'user_id', \n                              class_name: 'UserRole', association_foreign_key: 'user_role_id'\n      has_many_temporary_roles\n      acts_as_subject\n    end\n    ```\n\n    [here](#config-options) is some options you can pass to the declaration.\n\n4. Run `rails db:migrate`\n\nThat's all!\n\n## Usage\n\n### Config Options\n\n1. auto_definition: Auto definition before assignment if it's set to `true`. defaults to `false`.\n\n2. strict_mode: Raise error when doing wrong definition or assignment if it's\nset to `true`. defaults to `false`.\n\n3. without_group: Unable `role group` feature if it's set to `true`. defaults to `false`.\n\n4. **relation names**: you can change the names in model declarations, defaults to `stored_roles`, `permissions`, `stored_users` and so on.\n\n### Methods and helpers\n\n#### A. [Role Definition](https://github.com/zhandao/i_am_i_can/blob/master/lib/i_am_i_can/role/definition.rb)\n\n1. caller: Subject Model, like `User`\n2. method: `have_role`. aliases:\n    1. `have_roles`\n    2. `has_role` \u0026 `has_roles`\n\nExplanation:\n```ruby\n# === method signature ===\nhave_role *names, which_can: [ ], obj: nil\n\n# === examples ===\nUser.have_roles :admin, :master # =\u003e 'Role Definition Done' or error message\n# is the same as: `UserRole.create([{ name: :admin }, ...])`\n\n# then:\nUserRole.count # =\u003e 2\n```\n\n#### B. [Grouping Roles](https://github.com/zhandao/i_am_i_can/blob/master/lib/i_am_i_can/role/definition.rb)\n\n**Tip:** Roles that you're going to group should be defined\n\n1. caller: Subject Model, like `User`\n2. method: `group_roles`. aliases:\n    1. `group_role`\n    2. `groups_role` \u0026 `groups_roles`\n3. shortcut combination method: `have_and_group_roles` (alias `has_and_groups_roles`)  \n    it will do: roles definition \u0026\u0026 roles grouping\n4. helpers:\n    1. relation with role (member), defaults to `members`.\n\nExplanation:\n```ruby\n# === method signature ===\ngroup_roles *members, by_name:, which_can: [ ], obj: nil\n\n# === examples ===\n# 1. normal usage\nUser.have_roles :vip1, :vip2, :vip3\nUser.group_roles :vip1, :vip2, :vip3, by_name: :vip\n\n# 2. shortcut combination\nUser.have_and_group_roles :vip1, :vip2, :vip3, by_name: :vip\n\nUserRoleGroup.count # =\u003e 1\nUserRoleGroup.which(name: :vip).members.names # =\u003e %i[vip1 vip2 vip3]\n```\n\n#### C. [Role Assignment](https://github.com/zhandao/i_am_i_can/blob/master/lib/i_am_i_can/role/definition.rb)\n\n1. caller: subject instance, like `User.first`\n2. assignment by calling:\n    1. `becomes_a`, or it's aliases:\n        1. `is` / `is_a_role` / `is_roles`\n        2. `has_role` / `has_roles`\n        3. `role_is` / `role_are`\n    2. `is_a_temporary`: just like the name, the assignment occurs only\n       in **instance variable** not in database (will be in the cache).\n3. cancel assignment by calling:\n    1. `falls_from`, or it's aliases:\n        1. `removes_role`\n        2. `leaves`\n        3. `is_not_a` / `has_not_role` / `has_not_roles`\n        4. `will_not_be`\n    2. `is_not_a_temporary`\n4. replacement assignment by calling: `is_only_a`, alias `currently_is`.\n    (makes the role collection contain only the supplied roles, by adding and deleting as appropriate)\n5. callbacks - before / after / around:\n    1. `role_assign`: assignment\n    2. `cancel_role_assign`: cancel assignment\n    3. `role_update`: \n6. helpers:\n    1. relation with stored role, defaults to `stored_roles`.\n    2. `temporary_roles` and `valid_temporary_roles`\n    3. `roles`\n    4. `assoc_with_\u003croles\u003e`, like: `assoc_with_stored_roles`\n\nExplanation:\n```ruby\nhe = User.take\n# Dont't forget to define roles before assignment\nUser.have_roles :admin, :coder\n\n# === Stored Assignment ===\n# method signature\nbecomes_a *roles, which_can: [ ], obj: nil,\n                  _d: config.auto_definition,\n                  auto_definition: _d || which_can.present?,\n                  expires_in: nil, expires_at: (expires_in.after if expires_in)\n# 1. example of giving Symbol to `roles` params\nhe.becomes_a :admin # =\u003e 'Role Assignment Done' or error message\nhe.stored_roles     # =\u003e [\u003c#UserRole id: 1\u003e]\n# 2. example of giving role instances to `roles` params\nhe.becomes_a UserRole.all # =\u003e 'Role Assignment Done' or error message\nhe.stored_roles     # =\u003e [\u003c#UserRole id: 1\u003e, \u003c#UserRole id: 2\u003e]\n# 3. `expires` (subject assocates roles with a `expire_at` scope)\nhe.is_a :visitor, expires_in: 1.hour # or `expires_at: 1.hour.after`\nhe.is? :visitor     # =\u003e true\n#   an hour later ...\nhe.is? :visitor     # =\u003e false\n\n# assoc_with_\u003croles\u003e: for getting the relation records between subject and it's roles\nhe.assoc_with_stored_roles # =\u003e UsersAndUserRoles::ActiveRecord_Associations_CollectionProxy\n\n\n# === Temporary Assignment ===\n# signature as `becomes_a`\n# examples\nhe.is_a_temporary :coder # =\u003e 'Role Assignment Done' or error message\nhe.temporary_roles       # =\u003e [\u003c#UserRole id: 2\u003e]\n\nhe.roles # =\u003e [:admin, :coder]\n\n# === Cancel Assignment ===\n# method signature\nfalls_from *roles\nis_not_a_temporary *roles\n# examples\nhe.falls_from :admin         # =\u003e 'Role Assignment Done' or error message\nhe.is_not_a_temporary :coder # =\u003e 'Role Assignment Done' or error message\nhe.roles # =\u003e []\n\n# === Replacement Assignment ===\n# method signature\nis_only_a *roles\n# examples\nhe.is_only_a :role1, :role2\n```\n\n#### D. [Role / Group Querying](https://github.com/zhandao/i_am_i_can/blob/master/lib/i_am_i_can/subject/role_querying.rb)\n\n1. caller: subject instance, like `User.first`\n2. role querying methods:\n    1. `is?` / `is_role?` / `has_role?`\n    2. `isnt?`\n    3. `is!` / `is_role!` / `has_role!`\n    4. `is_one_of?` / `is_one_of_roles?`\n    4. `is_one_of!` / `is_one_of_roles!`\n    5. `is_every?` / `is_every_role_in?`\n    6. `is_every!` / `is_every_role_in!`\n3. group querying methods:\n    1. `is_in_role_group?` / `in_role_group?`\n    2. `is_in_one_of?` / `in_one_of?`\n    \nall the `?` methods will return `true` or `false`  \nall the `!` bang methods will return `true` or raise `IAmICan::VerificationFailed`\n    \nExamples:\n```ruby\nhe = User.take\n\nhe.is?   :admin\nhe.isnt? :admin\nhe.is!   :admin\n\nhe.is_every?  :admin, :master # return false if he is not a `admin` or `master`\nhe.is_one_of! :admin, :master # return true if he is a `master` or `admin`\n\nhe.is_in_role_group? :vip # return true if he has at least one role of the group `vip`\n```\n\n#### E. [Permission Definition](https://github.com/zhandao/i_am_i_can/blob/master/lib/i_am_i_can/permission/definition.rb)\n\n1. caller: Role / Role Group Model, like `UserRole` / `UserRoleGroup`\n2. method: `have_permission`. aliases:\n    1. `have_permissions`\n    2. `has_permission` \u0026 `has_permissions`\n\nExplanation:\n```ruby\n# === method signature ===\nhave_permission *actions, obj: nil\n# It is not recommended to pass an array of objects\n\n# === examples ===\nUserRole.have_permission :fly # =\u003e 'Permission Definition Done' or error message\nUserPermission.count          # =\u003e 1\n\nUserRoleGroup.have_permissions :read, :write, obj: book # =\u003e 'Permission Definition Done' or error message\nUserPermission.count # =\u003e 1 + 2\n```\n\n#### F. [Permission Assignment](https://github.com/zhandao/i_am_i_can/blob/master/lib/i_am_i_can/permission/assignment.rb)\n\n1. caller: role / role group instance, like `UserRole.which(name: :admin)`\n2. assignment by calling `can`. alias `has_permission`\n3. cancel assignment by calling `cannot`. alias `is_not_allowed_to`\n4. replacement assignment by calling: `can_only`,.\n    (makes the permission collection contain only the supplied permissions, by adding and deleting as appropriate)\n5. callbacks - before / after / around:\n    1. `permission_assign`: assignment\n    2. `cancel_permission_assign`: cancel assignment\n    3. `permission_update`: replacement assignment\n4. helpers:\n    1. relation with stored permission, defaults to `permissions`.\n\n\nExplanation:\n```ruby\nrole = UserRole.which(name: :admin)\n# Dont't forget to define permission before assginment\nUserRole.have_permission :fly\n\n# === Assignment ===\n# method signature\ncan *actions, resource: nil, obj: resource, # you can use `resource` or `obj` \n    _d: config.auto_definition, auto_definition: _d\n# examples\nrole.can :fly # =\u003e 'Permission Assignment Done' or error message\nrole.permissions # =\u003e [\u003c#UserPermission id: ..\u003e]\n# you can also passing permission instances to `actions` params, like:\nrole.can UserPermission.all\n\n# === Cancel Assignment ===\n# method signature\ncannot *actions, resource: nil, obj: resource\n# examples\nrole.cannot :fly\n\n# === Replacement Assignment ===\n# method signature\ncan_only *actions, resource: nil, obj: resource\n# examples\nrole.can_only :run\n```\n\n#### G. [Permission Querying](https://github.com/zhandao/i_am_i_can/blob/master/lib/i_am_i_can/subject/role_querying.rb)\n\n1. caller: \n    1. subject instance, like `User.find(1)`\n    2. role / role group instance, like `Role.which(name: :master)`\n        (only have `can?` method)\n2. methods:\n    1. `can?`\n    2. `cannot?`\n    3. `can!`\n    4. `can_each?` \u0026 `can_each!`\n    4. `can_one_of!` \u0026 `can_one_of!`\n    5. `temporarily_can?`\n    6. `stored_can?`\n    7. `group_can?`\n    \nall the `?` methods will return `true` or `false`  \nall the `!` bang methods will return `true` or raise `IAmICan::InsufficientPermission`\n    \nExamples:\n```ruby\nhe = User.take\n\n# `perform` is action, and `magic` is object (resource)\nhe.can?    :perform, :magic\n# the same as:\nhe.can?    :perform, obj: :magic\n\nhe.cannot? :perform, :magic\nhe.can!    :perform, :magic\n\nhe.can_each?   %i[ fly jump ] # return false if he can not `fly` or `jump`\nhe.can_one_of! %i[ fly jump ] # return true if he can `fly` or `jump`\n```\n\n#### H. Shortcut Combinations - which_can\n\nFaster way to assign, define roles and their permissions.  \nYou can use it when defining role even assigning role.\n\n```ruby\n# === use when defining role ===\n# it does:\n#   1. define the role to Subject Model\n#   2. define \u0026 assign the permission to the role\nUser.have_role :coder, which_can: [:perform], obj: :magic\nUserRole.which(name: :coder).can? :perform, :magic # =\u003e true\n\n# === use when assigning role ===\n# it does:\n#   1. define the role to Subject Model\n#   2. assign the role to subject instance\n#   2. define \u0026 assign the permission to the role\nuser = User.take\nuser.becomes_a :master, which_can: [:read], obj: :book\nuser.is? :master # =\u003e true\nuser.can? :read, :book # =\u003e true\n```\n\n#### I. Resource Querying\n\n1. caller: Resource Collection or Instance\n2. scopes:\n    1. `that_allow`\n\nExplanation:\n```ruby\n# === method signature ===\nscope :that_allow, -\u003e (subject, to:) { }\n\n# === examples ===\nBook.that_allow(User.all, to: :read)\nBook.that_allow(User.last, to: :write)\n```\n\n#### J. Useful Helpers\n\n1. for Subject (e.g. User)\n\n    ```ruby\n    # declaration in User\n    has_and_belongs_to_many :identities # stored_roles\n    \n    # 1. [scope] with_\u003cstored_roles\u003e\n    #   is the same as `includes(:stored_roles)` for avoiding N+1 querying\n    User.with_identities.where(identities: { name: 'teacher' })\n    ```\n\n2. for Role / RoleGroup (e.g. UserRole)\n\n    ```ruby\n    # declaration in UserRole\n    has_and_belongs_to_many :related_users\n    has_and_belongs_to_many :related_role_groups\n    has_and_belongs_to_many :permissions\n    \n    # 1. [class method] which(name:, **conditions)\n    #    the same as `find_by!`\n    UserRole.which(name: :admin)\n \n    # 2. [class method] names\n    UserRole.all.names # =\u003e symbol array\n \n    # 3. [class method] \u003crelated_*\u003e\n    #    returns a ActiveRecord_Relation\n    #    for example, to get the users of the role `admin` and `dev`:\n    UserRole.where(name: ['admin', 'dev']).related_users\n    #    to get the groups of the role `admin` and `dev`:\n    UserRole.where(name: ['admin', 'dev']).related_role_groups\n \n    # 4. [scope] with_\u003cpermissions\u003e\n    #   is the same as `includes(:permissions)` for avoiding N+1 querying\n    UserRole.with_permissions.where(permissions: { id: 1 })\n    ```\n\n3. for `Permission` (e.g. UserPermission)\n\n    ```ruby\n    # declaration in UserPermission\n    has_and_belongs_to_many :related_roles\n    has_and_belongs_to_many :related_role_groups\n \n    # 1. [class method] which(action:, obj: nil, **conditions)\n    #    the same as `find_by!`\n    UserPermission.which(action: :read, obj: Book.first)\n    UserPermission.which(action: :read, obj_type: 'Book', obj_id: 1)\n\n    # 2. [class method] names\n    UserPermission.all.names # =\u003e symbol array\n \n    # 3. [class method] \u003crelated_*\u003e\n    #    returns a ActiveRecord_Relation as above\n    UserPermission.where(..).related_roles\n    UserPermission.where(..).related_role_groups\n \n    # 4. [instance method] name\n    UserPermission.first.name # =\u003e :read_Book_1\n \n    # 5. [instance method] obj\n    UserPermission.first.obj # =\u003e nil / Book / book / :book\n    ```\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.\n\nTo install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/i_am_i_can. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n\n## Code of Conduct\n\nEveryone interacting in the IAmICan project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/i_am_i_can/blob/master/CODE_OF_CONDUCT.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzhandao%2Fi_am_i_can","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzhandao%2Fi_am_i_can","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzhandao%2Fi_am_i_can/lists"}