Skip to content

Commit

Permalink
introduce binary choice rules
Browse files Browse the repository at this point in the history
binary and unary are under choice_rule

These are not children of Data so Data can reference the binary and unary classes
  • Loading branch information
kbrock committed May 23, 2024
1 parent a2045c9 commit f851093
Show file tree
Hide file tree
Showing 9 changed files with 147 additions and 71 deletions.
6 changes: 6 additions & 0 deletions lib/floe.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@
require_relative "floe/workflow/choice_rule/is_present"
require_relative "floe/workflow/choice_rule/is_string"
require_relative "floe/workflow/choice_rule/is_timestamp"
require_relative "floe/workflow/choice_rule/equals"
require_relative "floe/workflow/choice_rule/greater_than"
require_relative "floe/workflow/choice_rule/greater_than_equals"
require_relative "floe/workflow/choice_rule/less_than_equals"
require_relative "floe/workflow/choice_rule/less_than"
require_relative "floe/workflow/choice_rule/matches"
require_relative "floe/workflow/choice_rule/data"
require_relative "floe/workflow/context"
require_relative "floe/workflow/path"
Expand Down
24 changes: 22 additions & 2 deletions lib/floe/workflow/choice_rule.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,23 @@ def build_children(sub_payloads)
end
end

attr_reader :next, :payload, :variable, :children
attr_reader :next, :payload, :variable
# used by Not, And, Or
attr_reader :children
# Used by binary for right hand side
attr_reader :ref, :ref_path

def initialize(payload, children = nil)
def initialize(payload, children = nil, compare_key: nil)
@payload = payload
@children = children

@next = payload["Next"]
@variable = payload["Variable"]
if compare_key&.end_with?("Path")
@ref_path = payload[compare_key]
elsif compare_key
@ref = payload[compare_key]
end
end

def true?(*)
Expand All @@ -37,9 +46,20 @@ def true?(*)

private

# used by unary and binary
def variable_value(context, input)
Path.value(variable, context, input)
end

# used by binary
def valid?(value)
!value.nil?
end

# used by binary
def compare_value(context, input)
ref_path ? Path.value(ref_path, context, input) : ref
end
end
end
end
92 changes: 23 additions & 69 deletions lib/floe/workflow/choice_rule/data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,82 +4,36 @@ module Floe
class Workflow
class ChoiceRule
class Data < Floe::Workflow::ChoiceRule
COMPARE_KEYS = %w[IsNull IsPresent IsNumeric IsString IsBoolean IsTimestamp String Numeric Boolean Timestamp].freeze
DATA_RULES = {
"IsNull" => Floe::Workflow::ChoiceRule::IsNull,
"IsPresent" => Floe::Workflow::ChoiceRule::IsPresent,
"IsNumeric" => Floe::Workflow::ChoiceRule::IsNumeric,
"IsString" => Floe::Workflow::ChoiceRule::IsString,
"IsBoolean" => Floe::Workflow::ChoiceRule::IsBoolean,
"IsTimestamp" => Floe::Workflow::ChoiceRule::IsTimestamp,
}

attr_reader :compare_key
attr_accessor :ref, :ref_path
"IsNull" => Floe::Workflow::ChoiceRule::IsNull,
"IsPresent" => Floe::Workflow::ChoiceRule::IsPresent,
"IsNumeric" => Floe::Workflow::ChoiceRule::IsNumeric,
"IsString" => Floe::Workflow::ChoiceRule::IsString,
"IsBoolean" => Floe::Workflow::ChoiceRule::IsBoolean,
"IsTimestamp" => Floe::Workflow::ChoiceRule::IsTimestamp,
"Equals" => Floe::Workflow::ChoiceRule::Data::Equals,
"LessThan" => Floe::Workflow::ChoiceRule::Data::LessThan,
"GreaterThan" => Floe::Workflow::ChoiceRule::Data::GreaterThan,
"LessThanEquals" => Floe::Workflow::ChoiceRule::Data::LessThanEquals,
"GreaterThanEquals" => Floe::Workflow::ChoiceRule::Data::GreaterThanEquals,
"Matches" => Floe::Workflow::ChoiceRule::Data::Matches
}.freeze
COMPARE_RULE = /^(String|Numeric|Boolean|Timestamp)?(#{DATA_RULES.keys.join("|")})(Path)?$/.freeze

def self.build(payload)
compare_key = payload.keys.detect { |key| key.match?(/^(#{DATA_RULES.keys.join("|")})/) }
if compare_key
DATA_RULES[compare_key].new(payload)
else
Floe::Workflow::ChoiceRule::Data.new(payload)
end
end
compare_key, klass = klass_params(payload)
raise Floe::InvalidWorkflowError, "Data-test Expression Choice Rule must have a compare key" unless compare_key
raise Floe::InvalidWorkflowError, "Data-test Expression Choice Rule must have a valid compare key" unless klass

def initialize(*)
super
@compare_key = payload.keys.detect { |key| key.match?(/^(#{COMPARE_KEYS.join("|")})/) }
raise Floe::InvalidWorkflowError, "Data-test Expression Choice Rule must have a compare key" if @compare_key.nil?

if @compare_key&.end_with?("Path")
@ref_path = payload[compare_key]
else
@ref = payload[compare_key]
end
klass.new(payload, :compare_key => compare_key)
end

def true?(context, input)
lhs = variable_value(context, input)
rhs = compare_value(context, input)
return false unless valid?(lhs)

case compare_key
when "StringEquals", "StringEqualsPath",
"NumericEquals", "NumericEqualsPath",
"BooleanEquals", "BooleanEqualsPath",
"TimestampEquals", "TimestampEqualsPath"
lhs == rhs
when "StringLessThan", "StringLessThanPath",
"NumericLessThan", "NumericLessThanPath",
"TimestampLessThan", "TimestampLessThanPath"
lhs < rhs
when "StringGreaterThan", "StringGreaterThanPath",
"NumericGreaterThan", "NumericGreaterThanPath",
"TimestampGreaterThan", "TimestampGreaterThanPath"
lhs > rhs
when "StringLessThanEquals", "StringLessThanEqualsPath",
"NumericLessThanEquals", "NumericLessThanEqualsPath",
"TimestampLessThanEquals", "TimestampLessThanEqualsPath"
lhs <= rhs
when "StringGreaterThanEquals", "StringGreaterThanEqualsPath",
"NumericGreaterThanEquals", "NumericGreaterThanEqualsPath",
"TimestampGreaterThanEquals", "TimestampGreaterThanEqualsPath"
lhs >= rhs
when "StringMatches"
lhs.match?(Regexp.escape(rhs).gsub('\*', '.*?'))
else
raise Floe::InvalidWorkflowError, "Invalid choice [#{compare_key}]"
def self.klass_params(payload)
payload.each_key do |key|
values = COMPARE_RULE.match(key)
return [key, DATA_RULES[values[2]], !!values[3]] if values
end
end

private

def valid?(value)
!value.nil?
end

def compare_value(context, input)
@ref_path ? Path.value(@ref_path, context, input) : @ref
nil
end
end
end
Expand Down
15 changes: 15 additions & 0 deletions lib/floe/workflow/choice_rule/equals.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

module Floe
class Workflow
class ChoiceRule
class Equals < Floe::Workflow::ChoiceRule
def true?(context, input)
lhs = variable_value(context, input)
rhs = compare_value(context, input)
valid?(lhs) && lhs == rhs
end
end
end
end
end
15 changes: 15 additions & 0 deletions lib/floe/workflow/choice_rule/greater_than.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

module Floe
class Workflow
class ChoiceRule
class GreaterThan < Floe::Workflow::ChoiceRule
def true?(context, input)
lhs = variable_value(context, input)
rhs = compare_value(context, input)
valid?(lhs) && lhs > rhs
end
end
end
end
end
15 changes: 15 additions & 0 deletions lib/floe/workflow/choice_rule/greater_than_equals.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

module Floe
class Workflow
class ChoiceRule
class GreaterThanEquals < Floe::Workflow::ChoiceRule
def true?(context, input)
lhs = variable_value(context, input)
rhs = compare_value(context, input)
valid?(lhs) && lhs >= rhs
end
end
end
end
end
15 changes: 15 additions & 0 deletions lib/floe/workflow/choice_rule/less_than.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

module Floe
class Workflow
class ChoiceRule
class LessThan < Floe::Workflow::ChoiceRule
def true?(context, input)
lhs = variable_value(context, input)
rhs = compare_value(context, input)
valid?(lhs) && lhs < rhs
end
end
end
end
end
15 changes: 15 additions & 0 deletions lib/floe/workflow/choice_rule/less_than_equals.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

module Floe
class Workflow
class ChoiceRule
class LessThanEquals < Floe::Workflow::ChoiceRule
def true?(context, input)
lhs = variable_value(context, input)
rhs = compare_value(context, input)
valid?(lhs) && lhs <= rhs
end
end
end
end
end
21 changes: 21 additions & 0 deletions lib/floe/workflow/choice_rule/matches.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

module Floe
class Workflow
class ChoiceRule
class Matches < Floe::Workflow::ChoiceRule
def initialize(payload, *, **)
super
# NOTE: only StringMatches exists (so no Path option)
# Since this is static, we're converting it up front
@ref = Regexp.escape(@ref).gsub('\*', '.*?')
end

def true?(context, input)
lhs = variable_value(context, input)
valid?(lhs) && lhs.match?(ref)
end
end
end
end
end

0 comments on commit f851093

Please sign in to comment.