Class: ActiveSanction::Scorer::Reason
- Inherits:
-
Object
- Object
- ActiveSanction::Scorer::Reason
- Extended by:
- T::Sig
- Defined in:
- lib/active_sanction/scorer/reason.rb
Overview
One line of the answer to "why did this score 87?".
ActiveSanction::Scorer::Reason.new(
factor: :dob, detail: "year 1948 matches", contribution: 6.0
)
The explanation adds up
A Result's explanation is a list of these, and their contributions sum
to exactly the score. That is the whole design: the name blend produces
the first one and every secondary identifier appends a signed delta, so
a reviewer reading the list downwards arrives at the number on the
report rather than at something near it.
It is a property the suite holds rather than a coincidence of the
arithmetic. Contributions are rounded once, here, and the score is the
sum of the rounded values -- not the rounded sum, which is how a total
ends up one tenth away from the figures printed beside it. Where
clamping moves the total off the sum, that correction is itself a reason
(:clamp), because a score that silently stopped at 100 is a score
whose explanation no longer explains it.
Why this is a required output and not a debugging aid
A compliance officer has to defend a screening decision to an examiner, and "the library said 87" is not a defence. Both directions matter: an alert that cannot be accounted for cannot be cleared, and a clearance that cannot be accounted for is the one an examiner asks about. So every adjustment the scorer makes writes one of these, including the ones that lower a score.
Instances are frozen on construction and compare by value.
Instance Attribute Summary collapse
-
#contribution ⇒ Float
readonly
Signed, in the same 0..100 units as the score.
-
#detail ⇒ String
readonly
Written for a person, and it names what was compared rather than which rule fired: "year 1948 matches", not "dob_overlap".
- #factor ⇒ Symbol readonly
Class Method Summary collapse
Instance Method Summary collapse
- #==(other) ⇒ Boolean (also: #eql?)
- #hash ⇒ Integer
- #initialize(factor:, detail:, contribution:) ⇒ void constructor
- #inspect ⇒ String
- #penalty? ⇒ Boolean
- #to_h ⇒ Hash{Symbol => T.untyped}
-
#to_s ⇒ String
The line a report prints: "+6.0 dob: year 1948 matches".
Constructor Details
#initialize(factor:, detail:, contribution:) ⇒ void
86 87 88 89 90 91 |
# File 'lib/active_sanction/scorer/reason.rb', line 86 def initialize(factor:, detail:, contribution:) @factor = T.let(factor!(factor), Symbol) @detail = T.let(detail!(detail), String) @contribution = T.let(Float(contribution).round(PRECISION).to_f, Float) freeze end |
Instance Attribute Details
#contribution ⇒ Float (readonly)
Signed, in the same 0..100 units as the score. Positive raises the score and negative lowers it.
74 75 76 |
# File 'lib/active_sanction/scorer/reason.rb', line 74 def contribution @contribution end |
#detail ⇒ String (readonly)
Written for a person, and it names what was compared rather than which rule fired: "year 1948 matches", not "dob_overlap". A reviewer reading it should not need this library's vocabulary.
69 70 71 |
# File 'lib/active_sanction/scorer/reason.rb', line 69 def detail @detail end |
#factor ⇒ Symbol (readonly)
63 64 65 |
# File 'lib/active_sanction/scorer/reason.rb', line 63 def factor @factor end |
Class Method Details
.from_h(hash) ⇒ T.attached_class
77 78 79 80 81 82 83 |
# File 'lib/active_sanction/scorer/reason.rb', line 77 def self.from_h(hash) attributes = hash.to_h.transform_keys(&:to_sym) unknown = attributes.keys - MEMBERS raise InvalidArgument, "unknown Reason attribute(s): #{unknown.join(", ")}" if unknown.any? T.unsafe(self).new(**attributes) end |
Instance Method Details
#==(other) ⇒ Boolean Also known as: eql?
104 105 106 107 108 |
# File 'lib/active_sanction/scorer/reason.rb', line 104 def ==(other) return false unless other.instance_of?(self.class) to_h == other.to_h end |
#hash ⇒ Integer
112 |
# File 'lib/active_sanction/scorer/reason.rb', line 112 def hash = [self.class, to_h].hash |
#inspect ⇒ String
115 |
# File 'lib/active_sanction/scorer/reason.rb', line 115 def inspect = "#<#{self.class} #{self}>" |
#penalty? ⇒ Boolean
94 |
# File 'lib/active_sanction/scorer/reason.rb', line 94 def penalty? = contribution.negative? |
#to_h ⇒ Hash{Symbol => T.untyped}
97 |
# File 'lib/active_sanction/scorer/reason.rb', line 97 def to_h = { factor: factor, detail: detail, contribution: contribution } |
#to_s ⇒ String
The line a report prints: "+6.0 dob: year 1948 matches".
101 |
# File 'lib/active_sanction/scorer/reason.rb', line 101 def to_s = format("%+.#{PRECISION}f %s: %s", contribution, factor, detail) |