diff --git a/lib/floe.rb b/lib/floe.rb index 611376ef..39c7ae6d 100644 --- a/lib/floe.rb +++ b/lib/floe.rb @@ -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" diff --git a/lib/floe/workflow/choice_rule.rb b/lib/floe/workflow/choice_rule.rb index c7241979..7801f600 100644 --- a/lib/floe/workflow/choice_rule.rb +++ b/lib/floe/workflow/choice_rule.rb @@ -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?(*) @@ -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 diff --git a/lib/floe/workflow/choice_rule/data.rb b/lib/floe/workflow/choice_rule/data.rb index ecff0b14..9bb6a569 100644 --- a/lib/floe/workflow/choice_rule/data.rb +++ b/lib/floe/workflow/choice_rule/data.rb @@ -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::Equals, + "LessThan" => Floe::Workflow::ChoiceRule::LessThan, + "GreaterThan" => Floe::Workflow::ChoiceRule::GreaterThan, + "LessThanEquals" => Floe::Workflow::ChoiceRule::LessThanEquals, + "GreaterThanEquals" => Floe::Workflow::ChoiceRule::GreaterThanEquals, + "Matches" => Floe::Workflow::ChoiceRule::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 diff --git a/lib/floe/workflow/choice_rule/equals.rb b/lib/floe/workflow/choice_rule/equals.rb new file mode 100644 index 00000000..789f9288 --- /dev/null +++ b/lib/floe/workflow/choice_rule/equals.rb @@ -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 diff --git a/lib/floe/workflow/choice_rule/greater_than.rb b/lib/floe/workflow/choice_rule/greater_than.rb new file mode 100644 index 00000000..afd280ac --- /dev/null +++ b/lib/floe/workflow/choice_rule/greater_than.rb @@ -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 diff --git a/lib/floe/workflow/choice_rule/greater_than_equals.rb b/lib/floe/workflow/choice_rule/greater_than_equals.rb new file mode 100644 index 00000000..55dcaff0 --- /dev/null +++ b/lib/floe/workflow/choice_rule/greater_than_equals.rb @@ -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 diff --git a/lib/floe/workflow/choice_rule/less_than.rb b/lib/floe/workflow/choice_rule/less_than.rb new file mode 100644 index 00000000..fd93c701 --- /dev/null +++ b/lib/floe/workflow/choice_rule/less_than.rb @@ -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 diff --git a/lib/floe/workflow/choice_rule/less_than_equals.rb b/lib/floe/workflow/choice_rule/less_than_equals.rb new file mode 100644 index 00000000..b2f93541 --- /dev/null +++ b/lib/floe/workflow/choice_rule/less_than_equals.rb @@ -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 diff --git a/lib/floe/workflow/choice_rule/matches.rb b/lib/floe/workflow/choice_rule/matches.rb new file mode 100644 index 00000000..3b5c8e1b --- /dev/null +++ b/lib/floe/workflow/choice_rule/matches.rb @@ -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