Class: Ruby::Rego::Value
- Inherits:
-
Object
- Object
- Ruby::Rego::Value
- Defined in:
- lib/ruby/rego/value.rb
Overview
Base class for Rego values. :reek:InstanceVariableAssumption (lazily memoizes @canonical)
Direct Known Subclasses
ArrayValue, BooleanValue, NullValue, NumberValue, ObjectValue, SetValue, StringValue, UndefinedValue
Constant Summary collapse
- TYPE_NAME =
"value"
Instance Attribute Summary collapse
-
#value ⇒ Object
readonly
The wrapped Ruby value.
Class Method Summary collapse
-
.canonicalize(value) ⇒ Object
-
.canonicalize_each(collection) ⇒ Array
-
.canonicalize_float(value) ⇒ Numeric
A Float canonicalizes to the same exact form a Number would: its shortest decimal as an exact Rational (an integer-valued Float collapsing to its Integer), so a Float produced by a not-yet- migrated builtin compares and hashes equal to a Number of the same value (1.0 == 1, 0.1 == 0.1).
-
.from_ruby(value) ⇒ Value
Coerce Ruby values into Rego values.
Instance Method Summary collapse
-
#==(other) ⇒ Boolean
(also: #eql?)
Compare values by class and canonical Ruby value.
-
#canonical ⇒ Object
The value’s canonical Ruby form: numerically-equal numbers are unified (an integer-valued Float collapses to its Integer) recursively through arrays, sets, and objects, so identity/equality/hashing match OPA’s number semantics.
-
#fetch_reference(_key) ⇒ Value
Resolve a reference on the value.
-
#hash ⇒ Integer
Hash for use in Ruby collections, consistent with ==/eql? (so a Set/Hash dedups 1 and 1.0).
-
#initialize(value = nil) ⇒ Value
constructor
Create a value wrapper.
-
#object_key ⇒ Object
Return a normalized object key representation.
-
#to_ruby ⇒ Object
Convert the value back to Ruby.
-
#truthy? ⇒ Boolean
Determine truthiness for Rego evaluation.
-
#type_name ⇒ String
Return the Rego type name.
-
#undefined? ⇒ Boolean
Check if the value is undefined.
Constructor Details
#initialize(value = nil) ⇒ Value
Create a value wrapper.
20 21 22 |
# File 'lib/ruby/rego/value.rb', line 20 def initialize(value = nil) @value = value end |
Instance Attribute Details
#value ⇒ Object (readonly)
The wrapped Ruby value.
27 28 29 |
# File 'lib/ruby/rego/value.rb', line 27 def value @value end |
Class Method Details
.canonicalize(value) ⇒ Object
108 109 110 111 112 113 114 115 116 117 |
# File 'lib/ruby/rego/value.rb', line 108 def self.canonicalize(value) case value when Number then value.exact when ::Float then canonicalize_float(value) when ::Array then canonicalize_each(value) when ::Set then ::Set.new(canonicalize_each(value)) when ::Hash then value.to_h { |key, val| [canonicalize(key), canonicalize(val)] } else value end end |
.canonicalize_each(collection) ⇒ Array
120 121 122 |
# File 'lib/ruby/rego/value.rb', line 120 def self.canonicalize_each(collection) collection.map { |element| canonicalize(element) } end |
.canonicalize_float(value) ⇒ Numeric
A Float canonicalizes to the same exact form a Number would: its shortest decimal as an exact Rational (an integer-valued Float collapsing to its Integer), so a Float produced by a not-yet- migrated builtin compares and hashes equal to a Number of the same value (1.0 == 1, 0.1 == 0.1). A non-finite Float has no exact value and is left as-is.
129 130 131 132 133 134 |
# File 'lib/ruby/rego/value.rb', line 129 def self.canonicalize_float(value) return value unless value.finite? rational = BigDecimal(value.to_s).to_r rational.denominator == 1 ? rational.numerator : rational end |
.from_ruby(value) ⇒ Value
Coerce Ruby values into Rego values.
140 141 142 143 144 145 146 147 148 |
# File 'lib/ruby/rego/value.rb', line 140 def self.from_ruby(value) return value if value.is_a?(Value) return UndefinedValue.new if value.equal?(UndefinedValue::UNDEFINED) built_value = build_value(value) return built_value if built_value raise ArgumentError, "Unsupported value type: #{value.class}" end |
Instance Method Details
#==(other) ⇒ Boolean Also known as: eql?
Compare values by class and canonical Ruby value. The canonical form makes numerically-equal numbers (e.g. 1 and 1.0) compare equal everywhere — including as array/set/object members — matching OPA, where 1 == 1.0.
80 81 82 |
# File 'lib/ruby/rego/value.rb', line 80 def ==(other) other.is_a?(self.class) && other.canonical == canonical end |
#canonical ⇒ Object
The value’s canonical Ruby form: numerically-equal numbers are unified (an
integer-valued Float collapses to its Integer) recursively through arrays, sets, and
objects, so identity/equality/hashing match OPA’s number semantics. to_ruby still
returns the original (first-seen) representation.
100 101 102 103 104 |
# File 'lib/ruby/rego/value.rb', line 100 def canonical return @canonical if defined?(@canonical) @canonical = self.class.canonicalize(to_ruby) end |
#fetch_reference(_key) ⇒ Value
Resolve a reference on the value.
54 55 56 57 58 |
# File 'lib/ruby/rego/value.rb', line 54 def fetch_reference(_key) return self if undefined? UndefinedValue.new end |
#hash ⇒ Integer
Hash for use in Ruby collections, consistent with ==/eql? (so a Set/Hash dedups 1 and 1.0). Without this, eql?-equal numbers could hash differently and never collapse.
90 91 92 |
# File 'lib/ruby/rego/value.rb', line 90 def hash [self.class.name, canonical].hash end |
#object_key ⇒ Object
Return a normalized object key representation.
70 71 72 |
# File 'lib/ruby/rego/value.rb', line 70 def object_key to_ruby end |
#to_ruby ⇒ Object
Convert the value back to Ruby.
39 40 41 |
# File 'lib/ruby/rego/value.rb', line 39 def to_ruby value end |
#truthy? ⇒ Boolean
Determine truthiness for Rego evaluation.
32 33 34 |
# File 'lib/ruby/rego/value.rb', line 32 def truthy? true end |
#type_name ⇒ String
Return the Rego type name.
46 47 48 |
# File 'lib/ruby/rego/value.rb', line 46 def type_name self.class::TYPE_NAME end |
#undefined? ⇒ Boolean
Check if the value is undefined.
63 64 65 |
# File 'lib/ruby/rego/value.rb', line 63 def undefined? is_a?(UndefinedValue) end |