class DeclarativePolicy::Step
This object represents one step in the runtime decision of whether an ability is allowed. It contains a Rule
and a context (instance of DeclarativePolicy::Base
), which contains the user, the subject, and the cache. It also contains an “action”, which is the symbol :prevent or :enable.
Attributes
Public Class Methods
# File lib/declarative_policy/step.rb, line 12 def initialize(context, rule, action) @context = context @rule = rule @action = action end
Public Instance Methods
In the flattening process, duplicate steps may be generated in the same rule. This allows us to eliminate those (see Runner#steps_by_score
and note its use of a Set)
# File lib/declarative_policy/step.rb, line 21 def ==(other) @context == other.context && @rule == other.rule && @action == other.action end
# File lib/declarative_policy/step.rb, line 44 def enable? @action == :enable end
This rather complex method allows us to split rules into parts so that they can be sorted independently for better optimization
# File lib/declarative_policy/step.rb, line 54 def flattened(roots) case @rule when Rule::Or # A single `Or` step is the same as each of its elements as separate steps @rule.rules.flat_map { |r| Step.new(@context, r, @action).flattened(roots) } when Rule::Ability # This looks like a weird micro-optimization but it buys us quite a lot # in some cases. If we depend on an Ability (i.e. a `can?(...)` rule), # and that ability *only* has :enable actions (modulo some actions that # we already have taken care of), then its rules can be safely inlined. steps = @context.runner(@rule.ability).steps.reject { |s| roots.include?(s) } if steps.all?(&:enable?) # in the case that we are a :prevent step, each inlined step becomes # an independent :prevent, even though it was an :enable in its initial # context. steps.map! { |s| s.with_action(:prevent) } if prevent? steps.flat_map { |s| s.flattened(roots) } else [self] end else [self] end end
# File lib/declarative_policy/step.rb, line 81 def pass? @rule.pass?(@context) end
# File lib/declarative_policy/step.rb, line 48 def prevent? @action == :prevent end
# File lib/declarative_policy/step.rb, line 85 def repr "#{@action} when #{@rule.repr} (#{@context.repr})" end
In the runner, steps are sorted dynamically by score, so that we are sure to compute them in close to the optimal order.
See also Rule#score, ManifestCondition#score
, and Runner#steps_by_score
.
# File lib/declarative_policy/step.rb, line 29 def score # we slightly prefer the preventative actions # since they are more likely to short-circuit case @action when :prevent @rule.score(@context) * (7.0 / 8) when :enable @rule.score(@context) end end
# File lib/declarative_policy/step.rb, line 40 def with_action(action) Step.new(@context, @rule, action) end