Skip to content

Instantly share code, notes, and snippets.

@khanlou
Created February 14, 2021 03:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save khanlou/6f360992100c97c77ae56db0c668c317 to your computer and use it in GitHub Desktop.
Save khanlou/6f360992100c97c77ae56db0c668c317 to your computer and use it in GitHub Desktop.
A version of Ruby's cancan for Swift.
import Foundation
struct CanCan<Identity, Action: Equatable> {
var registry: [(String, (Identity, Any) -> [Action])] = []
mutating func register<T>(_ type: T.Type, _ block: @escaping (Identity, T) -> [Action]) {
self.registry.append((String(describing: type), { (identity: Identity, object: Any) -> [Action] in
guard let casted = object as? T else {
return []
}
return block(identity, casted)
}))
}
func can(_ user: Identity, _ action: Action, _ subject: Any) -> Bool {
let typeName = String(describing: type(of: subject))
return registry
.filter(({ $0.0 == typeName }))
.contains(where: { name, block in
let availableActions = block(user, subject)
return availableActions.contains(action)
})
}
}
struct User {
var isAdmin: Bool
}
struct Post { }
enum Action: Equatable, CaseIterable {
case view, edit, delete
}
var canCan = CanCan<User, Action>()
canCan.register(Post.self, { user, post in
if user.isAdmin {
return Action.allCases
} else {
return [.view]
}
})
canCan.can(User(isAdmin: false), .view, Post()) == true
canCan.can(User(isAdmin: false), .edit, Post()) == false
canCan.can(User(isAdmin: true), .edit, Post()) == true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment