Search code examples
ruby-on-railsruby-on-rails-6rolify

Rolify: Add Expire Datetime


I'm pretty new to Rails, and I would like to know if there is a way to had an "expired_at" field using the gems "Rolify", so I could add a Role to an User for a while, or permanent Role if field is NULL.

I thought of adding the field into the migration :

class RolifyCreateRoles < ActiveRecord::Migration[6.1]
  def change
    create_table(:roles) do |t|
      t.string :name
      t.references :resource, :polymorphic => true

      t.timestamps
    end

    create_table(:users_roles, :id => false) do |t|
      t.references :user
      t.references :role

      t.datetime :expired_at     # Adding field to the "join_table"
    end
    
    add_index(:roles, [ :name, :resource_type, :resource_id ])
    add_index(:users_roles, [ :user_id, :role_id ])
  end
end

But I don't have any idea about how to "override" the methods like "add_role" or "has_role?" to take this "expired_at" field into consideration, so I could do something like :

room = Chat::Room.find(1)

sarah = User.find(1)
david = User.find(2)

# Today
sarah.add_role :muted, room, 7.days.from_now
david.add_role :muted, room

sarah.has_role? :muted, room     # should return true
david.has_role? :muted, room     # should return true

# A week later
sarah.has_role? :muted, room    # should return false
david.has_role? :muted, room    # should return true

Thanks.


Solution

  • i have an idea that you don't need to add expired_at field, you can extend rolify gem to append the expired time after the role name, then we can parse the expired time and check whether that role is expired or not, take a look following code:

    # lib/rolify/role.rb
    module Rolify
      module Role
        def add_expire_role(role_name, expire_time, resource = nil)
          expire_role_name = "#{role_name}  expire_at #{expire_time}"
          add_role(expire_role_name, resource)
        end
    
        def check_role(role_name, resource = nil)
          expire_roles = self.class.adapter.where(self.roles).where("roles.name LIKE ?","%#{role_name}%")
          # filter still available roles
          avail_roles = expire_roles.select do |role|
            _, expire_role_time = role.name.split("expire_at")
            expired = expire_role_time.present? && expire_role_time.to_time < Time.now
            # remove expired role
            self.class.adapter.remove(self, role.name, resource) if expired
            !expired
          end
          # return true if there's still any role
          avail_roles.any?
        end
      end
    end
    
    # config/initializers/rolify.rb
    Rolify.configure do |config|
     # ...
    end
    Dir[File.join(Rails.root, "lib", "rolify", "**/*.rb")].each {|l| require l }
    

    demo

    user = User.first
    user.add_expire_role :test, 1.minute.from_now
    user.check_role :test # true
    # 1 minute later
    user.check_role :test # false
    
    user.add_role :test
    user.check_role :test # always true
    # try override
    user.add_expire_role :test, 1.minute.from_now
    # 1 minute later
    user.check_role :test # still true