Skip to content

Instantly share code, notes, and snippets.

@zapo
Last active August 29, 2015 14:00
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 zapo/11094978 to your computer and use it in GitHub Desktop.
Save zapo/11094978 to your computer and use it in GitHub Desktop.
# plagiarism of https://github.com/runemadsen/cancan-backbone/blob/master/cancan-backbone.js
# without backbone dependency
class exports.Rule
constructor: (rule) ->
@actions = rule.actions
@conditions = rule.conditions ? {}
@subjects = rule.subjects
@can = !!rule.can
is_relevant: (action, subject) ->
@matches_action(action) && @matches_subject(subject)
matches_conditions: (action, subject, matcher) ->
if _.isObject(@conditions) && !_.isEmpty(@conditions) && matcher?
matcher.call(@, @conditions)
else
if _.isEmpty(@conditions) then true else @can
matches_action: (action) ->
_.include(@expanded_actions, 'manage') || _.include(@expanded_actions, action)
matches_subject: (subject) ->
_.include(@subjects, 'all') || _.include(@subjects, subject)
class exports.Ability
constructor: (rules = [], aliased_actions = {}) ->
@rules = _.map(rules, (rule) -> new exports.Rule(rule))
@aliased_actions = _.extend({
read: ['index', 'show'],
create: ['new'],
update: ['edit']
}, aliased_actions)
can: (action, subject, matcher) ->
match = _.detect(@relevant_rules(action, subject), (rule) ->
rule.matches_conditions(action, subject, matcher)
, @)
if match then match.can else false
cannot: (action, subject, matcher) -> !@can(action, subject, matcher)
expand_actions: (actions) ->
_.chain(actions).map((action) ->
if @aliased_actions[action]
[action].concat(@expand_actions(@aliased_actions[action]))
else
action
, @).flatten().value()
relevant_rules: (action, subject) ->
reversed_rules = @rules.slice(0).reverse()
_.select(reversed_rules, (rule) ->
rule.expanded_actions = @expand_actions(rule.actions)
rule.is_relevant(action, subject)
, @)
class Foo; end
BAR = :bar
class Ability
def initialize user
if user
can :read, Foo, :way => false
can :read, BAR, :way => true
end
end
def as_json = {}
rules.map do |rule|
{
:can => rule.base_behavior,
:actions => rule.actions.as_json,
:subjects => rule.subjects.map { |s| s.to_s.underscore },
:conditions => rule.conditions.as_json
}
end
end
end
ability = new Ability(<%= raw Ability.new(current_user).to_json %>)
ability.can('read', 'foo') # => true
ability.can('read', 'foo', (conditions) -> !!conditions.way ) # => false
ability.can('read', 'bar', (conditions) -> !!conditions.way ) # => true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment