gitlab-org--gitlab-foss/lib/declarative_policy/dsl.rb

104 lines
2.4 KiB
Ruby

module DeclarativePolicy
# The DSL evaluation context inside rule { ... } blocks.
# Responsible for creating and combining Rule objects.
#
# See Base.rule
class RuleDsl
def initialize(context_class)
@context_class = context_class
end
def can?(ability)
Rule::Ability.new(ability)
end
def all?(*rules)
Rule::And.make(rules)
end
def any?(*rules)
Rule::Or.make(rules)
end
def none?(*rules)
~Rule::Or.new(rules)
end
def cond(condition)
Rule::Condition.new(condition)
end
def delegate(delegate_name, condition)
Rule::DelegatedCondition.new(delegate_name, condition)
end
def method_missing(m, *a, &b)
return super unless a.size == 0 && !block_given?
if @context_class.delegations.key?(m)
DelegateDsl.new(self, m)
else
cond(m.to_sym)
end
end
end
# Used when the name of a delegate is mentioned in
# the rule DSL.
class DelegateDsl
def initialize(rule_dsl, delegate_name)
@rule_dsl = rule_dsl
@delegate_name = delegate_name
end
def method_missing(m, *a, &b)
return super unless a.size == 0 && !block_given?
@rule_dsl.delegate(@delegate_name, m)
end
end
# The return value of a rule { ... } declaration.
# Can call back to register rules with the containing
# Policy class (context_class here). See Base.rule
#
# Note that the #policy method just performs an #instance_eval,
# which is useful for multiple #enable or #prevent callse.
#
# Also provides a #method_missing proxy to the context
# class's class methods, so that helper methods can be
# defined and used in a #policy { ... } block.
class PolicyDsl
def initialize(context_class, rule)
@context_class = context_class
@rule = rule
end
def policy(&b)
instance_eval(&b)
end
def enable(*abilities)
@context_class.enable_when(abilities, @rule)
end
def prevent(*abilities)
@context_class.prevent_when(abilities, @rule)
end
def prevent_all
@context_class.prevent_all_when(@rule)
end
def method_missing(m, *a, &b)
return super unless @context_class.respond_to?(m)
@context_class.__send__(m, *a, &b) # rubocop:disable GitlabSecurity/PublicSend
end
def respond_to_missing?(m)
@context_class.respond_to?(m) || super
end
end
end