Module: Amoskeag

Defined in:
lib/amoskeag-rb.rb,
lib/amoskeag-rb/version.rb

Overview

Amoskeag - A secure, functional DSL for business rules

The native extension defines:

  • Amoskeag::Error < StandardError
  • Amoskeag::CompileError < Amoskeag::Error
  • Amoskeag::EvalError < Amoskeag::Error
  • Amoskeag::Program (compiled program class)
  • Amoskeag.compile(source, symbols = []) -> Program
  • Amoskeag.evaluate(program, data) -> Object
  • Amoskeag.eval_expression(source, data, symbols = []) -> Object

Defined Under Namespace

Classes: Program

Constant Summary collapse

VERSION =
"0.2.0"
AMOSKEAG_VERSION =
"0.1.2"

Class Method Summary collapse

Class Method Details

.compile(source, symbols = []) ⇒ Object

Override the compile method (no wrapping needed, just validation)



100
101
102
# File 'lib/amoskeag-rb.rb', line 100

def self.compile(source, symbols = [])
  compile_native(source, symbols)
end

.compile_nativeObject

Alias native methods for potential future wrapper use



37
# File 'lib/amoskeag-rb.rb', line 37

alias compile_native compile

.eval_expression(source, data, symbols = []) ⇒ Object

Override the eval_expression method to wrap/unwrap symbols and validate data



112
113
114
115
116
# File 'lib/amoskeag-rb.rb', line 112

def self.eval_expression(source, data, symbols = [])
  validate_data(data)
  result = eval_expression_native(source, data, symbols)
  unwrap_symbols(result)
end

.eval_expression_nativeObject



39
# File 'lib/amoskeag-rb.rb', line 39

alias eval_expression_native eval_expression

.evaluate(program, data) ⇒ Object

Override the evaluate method to wrap/unwrap symbols and validate data



105
106
107
108
109
# File 'lib/amoskeag-rb.rb', line 105

def self.evaluate(program, data)
  validate_data(data)
  result = evaluate_native(program, data)
  unwrap_symbols(result)
end

.evaluate_nativeObject



38
# File 'lib/amoskeag-rb.rb', line 38

alias evaluate_native evaluate

.unwrap_symbols(value) ⇒ Object

Unwrap symbols from their hash representation at the top level only This allows expression results like :adult to be returned as symbols while keeping symbols nested in data structures in wrapped format



44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/amoskeag-rb.rb', line 44

def unwrap_symbols(value)
  case value
  when Hash
    # Check if this is a wrapped symbol at the top level
    if value.keys == ["__symbol__"]
      value["__symbol__"].to_sym
    else
      # Don't unwrap nested symbols - they're part of data structures
      value
    end
  else
    value
  end
end

.validate_data(data, path = "data") ⇒ Object

Validate that data is JSON-serializable



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/amoskeag-rb.rb', line 74

def validate_data(data, path = "data")
  case data
  when String, Integer, Float, TrueClass, FalseClass, NilClass, Symbol
    # These are valid types (note: not all Numeric, only Integer and Float)
    data
  when Hash
    data.each do |key, value|
      unless key.is_a?(String) || key.is_a?(Symbol)
        raise ArgumentError, "#{path} key must be String or Symbol, got #{key.class}"
      end
      validate_data(value, "#{path}[#{key.inspect}]")
    end
    data
  when Array
    data.each_with_index do |item, idx|
      validate_data(item, "#{path}[#{idx}]")
    end
    data
  else
    # Invalid type - include path information
    raise ArgumentError, "#{path} contains invalid type #{data.class}"
  end
end

.wrap_symbols(value) ⇒ Object

Recursively wrap symbols for native processing



60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/amoskeag-rb.rb', line 60

def wrap_symbols(value)
  case value
  when Symbol
    { "__symbol__" => value.to_s }
  when Hash
    value.transform_values { |v| wrap_symbols(v) }
  when Array
    value.map { |v| wrap_symbols(v) }
  else
    value
  end
end