Class: DeclarativePolicy::Step

Inherits:
Object
  • Object
show all
Defined in:
lib/declarative_policy/step.rb

Overview

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.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(context, rule, action) ⇒ Step

Returns a new instance of Step.


11
12
13
14
15
# File 'lib/declarative_policy/step.rb', line 11

def initialize(context, rule, action)
  @context = context
  @rule = rule
  @action = action
end

Instance Attribute Details

#actionObject (readonly)

Returns the value of attribute action


10
11
12
# File 'lib/declarative_policy/step.rb', line 10

def action
  @action
end

#contextObject (readonly)

Returns the value of attribute context


10
11
12
# File 'lib/declarative_policy/step.rb', line 10

def context
  @context
end

#ruleObject (readonly)

Returns the value of attribute rule


10
11
12
# File 'lib/declarative_policy/step.rb', line 10

def rule
  @rule
end

Instance Method Details

#==(other) ⇒ Object

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)


20
21
22
# File 'lib/declarative_policy/step.rb', line 20

def ==(other)
  @context == other.context && @rule == other.rule && @action == other.action
end

#enable?Boolean

Returns:

  • (Boolean)

43
44
45
# File 'lib/declarative_policy/step.rb', line 43

def enable?
  @action == :enable
end

#flattened(roots) ⇒ Object

This rather complex method allows us to split rules into parts so that they can be sorted independently for better optimization


53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/declarative_policy/step.rb', line 53

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

#pass?Boolean

Returns:

  • (Boolean)

80
81
82
# File 'lib/declarative_policy/step.rb', line 80

def pass?
  @rule.pass?(@context)
end

#prevent?Boolean

Returns:

  • (Boolean)

47
48
49
# File 'lib/declarative_policy/step.rb', line 47

def prevent?
  @action == :prevent
end

#reprObject


84
85
86
# File 'lib/declarative_policy/step.rb', line 84

def repr
  "#{@action} when #{@rule.repr} (#{@context.repr})"
end

#scoreObject

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.


28
29
30
31
32
33
34
35
36
37
# File 'lib/declarative_policy/step.rb', line 28

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

#with_action(action) ⇒ Object


39
40
41
# File 'lib/declarative_policy/step.rb', line 39

def with_action(action)
  Step.new(@context, @rule, action)
end