Module: Stellwerk::Functions
- Defined in:
- lib/stellwerk/functions.rb
Class Method Summary collapse
-
.ARRAY(*args) ⇒ Object
ARRAY function - creates an array from arguments Allows: values = ARRAY(1, 2, 3) which can then be used with SUM(values).
- .AVERAGE(*args) ⇒ Object (also: AVG)
- .coerce_numeric_list(list) ⇒ Object
-
.COUNT(*args) ⇒ Object
COUNT dual mode: counts non-nil entries.
- .COUNTIF(collection, predicate_expr, calculator: Dentaku::Calculator.new) ⇒ Object
-
.DEEP_PROJECT(root, path_string, field_name) ⇒ Object
DEEP_PROJECT(root, path_string, field_name) Traverse a dotted path to reach an array, then extract field_name from each element.
- .DISTINCT(collection) ⇒ Object
- .ensure_array(arg) ⇒ Object
- .FILTER(collection, predicate_expr, calculator: Dentaku::Calculator.new) ⇒ Object
- .FIRST(arg) ⇒ Object
- .LAST(arg) ⇒ Object
-
.MAP(collection, expr, calculator: Dentaku::Calculator.new) ⇒ Object
----------------------------- Phase 2 Functions ----------------------------- MAP(collection, expr_string, safe: false) - Applies a Dentaku expression to each element with variable 'item' bound to element FILTER(collection, predicate_expr) - Keeps elements where predicate_expr evaluates truthy DISTINCT(collection) - Removes duplicates via Ruby equality AVERAGE/AVG dual-mode like SUM but returns nil if no numeric values SUMIF(collection, predicate_expr, projection_expr=nil) - If projection_expr provided, sum of projection for items where predicate matches - Else sum of numeric-looking items themselves COUNTIF(collection, predicate_expr) - Count of elements where predicate is truthy TRACE(value, label=nil) - Returns value unchanged; placeholder for future instrumentation hook.
-
.MAX(*args) ⇒ Object
MAX dual mode: returns nil if no numeric values.
-
.MIN(*args) ⇒ Object
MIN dual mode: returns nil if no numeric values.
-
.PROJECT(collection, field_name) ⇒ Object
Internal projection helper: given an array of hashes (or objects responding to [] / .public_send) and a field name, extract that field, allowing nils.
- .REDUCE(collection, initial, expr, calculator: Dentaku::Calculator.new) ⇒ Object
-
.SUM(*args) ⇒ Object
SUM dual mode.
- .SUMIF(collection, predicate_expr, projection_expr = nil, calculator: Dentaku::Calculator.new) ⇒ Object
- .TAKE(arg, n) ⇒ Object
- .TRACE(value, _label = nil) ⇒ Object
Class Method Details
.ARRAY(*args) ⇒ Object
ARRAY function - creates an array from arguments Allows: values = ARRAY(1, 2, 3) which can then be used with SUM(values)
314 315 316 |
# File 'lib/stellwerk/functions.rb', line 314 def ARRAY(*args) args end |
.AVERAGE(*args) ⇒ Object Also known as: AVG
229 230 231 232 233 234 235 236 237 |
# File 'lib/stellwerk/functions.rb', line 229 def AVERAGE(*args) values = if args.length == 1 && args.first.is_a?(Array) coerce_numeric_list(args.first) else coerce_numeric_list(args) end return nil if values.empty? values.sum / values.length.to_f end |
.coerce_numeric_list(list) ⇒ Object
30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/stellwerk/functions.rb', line 30 def coerce_numeric_list(list) list.filter_map do |v| case v when Numeric then v.to_f when String v_strip = v.strip if v_strip.match?(/^[-+]?\d+(?:\.\d+)?$/) v_strip.to_f end else nil end end end |
.COUNT(*args) ⇒ Object
COUNT dual mode: counts non-nil entries. If single array arg, count its non-nil members.
125 126 127 128 129 130 131 |
# File 'lib/stellwerk/functions.rb', line 125 def COUNT(*args) if args.length == 1 && args.first.is_a?(Array) args.first.compact.length else args.compact.length end end |
.COUNTIF(collection, predicate_expr, calculator: Dentaku::Calculator.new) ⇒ Object
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
# File 'lib/stellwerk/functions.rb', line 270 def COUNTIF(collection, predicate_expr, calculator: Dentaku::Calculator.new) arr = ensure_array(collection) return 0 if predicate_expr.nil? || predicate_expr.strip.empty? count = 0 arr.each do |item| begin ctx = { "item" => item } if item.is_a?(Hash) item.each { |k, v| ctx[k.to_s] = v } end count += 1 if calculator.evaluate!(predicate_expr, ctx) rescue StandardError # ignore end end count end |
.DEEP_PROJECT(root, path_string, field_name) ⇒ Object
DEEP_PROJECT(root, path_string, field_name) Traverse a dotted path to reach an array, then extract field_name from each element. Example: DEEP_PROJECT(order, "items", "price") where order is an array of hashes.
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/stellwerk/functions.rb', line 74 def DEEP_PROJECT(root, path_string, field_name) return [] if root.nil? current = root path_segments = path_string.to_s.split(".") path_segments.each do |seg| break if current.nil? if current.is_a?(Hash) current = current[seg.to_sym] || current[seg.to_s] elsif current.respond_to?(seg) begin current = current.public_send(seg) rescue StandardError current = nil end else current = nil end end arr = ensure_array(current) arr.map do |item| if item.is_a?(Hash) item[field_name.to_sym] || item[field_name.to_s] elsif item.respond_to?(:[]) && !item.is_a?(String) begin item[field_name] rescue StandardError nil end elsif item.respond_to?(field_name) begin item.public_send(field_name) rescue StandardError nil end else nil end end end |
.DISTINCT(collection) ⇒ Object
225 226 227 |
# File 'lib/stellwerk/functions.rb', line 225 def DISTINCT(collection) ensure_array(collection).uniq end |
.ensure_array(arg) ⇒ Object
24 25 26 27 28 |
# File 'lib/stellwerk/functions.rb', line 24 def ensure_array(arg) return [] if arg.nil? return arg if arg.is_a?(Array) [arg] end |
.FILTER(collection, predicate_expr, calculator: Dentaku::Calculator.new) ⇒ Object
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/stellwerk/functions.rb', line 209 def FILTER(collection, predicate_expr, calculator: Dentaku::Calculator.new) arr = ensure_array(collection) return arr if predicate_expr.nil? || predicate_expr.strip.empty? arr.select do |item| begin ctx = { "item" => item } if item.is_a?(Hash) item.each { |k, v| ctx[k.to_s] = v } end !!calculator.evaluate!(predicate_expr, ctx) rescue StandardError false end end end |
.FIRST(arg) ⇒ Object
153 154 155 156 |
# File 'lib/stellwerk/functions.rb', line 153 def FIRST(arg) arr = ensure_array(arg) arr.first end |
.LAST(arg) ⇒ Object
158 159 160 161 |
# File 'lib/stellwerk/functions.rb', line 158 def LAST(arg) arr = ensure_array(arg) arr.last end |
.MAP(collection, expr, calculator: Dentaku::Calculator.new) ⇒ Object
Phase 2 Functions
MAP(collection, expr_string, safe: false)
- Applies a Dentaku expression to each element with variable 'item' bound to element FILTER(collection, predicate_expr)
- Keeps elements where predicate_expr evaluates truthy DISTINCT(collection)
- Removes duplicates via Ruby equality AVERAGE/AVG dual-mode like SUM but returns nil if no numeric values SUMIF(collection, predicate_expr, projection_expr=nil)
- If projection_expr provided, sum of projection for items where predicate matches
- Else sum of numeric-looking items themselves COUNTIF(collection, predicate_expr)
- Count of elements where predicate is truthy TRACE(value, label=nil)
- Returns value unchanged; placeholder for future instrumentation hook
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/stellwerk/functions.rb', line 192 def MAP(collection, expr, calculator: Dentaku::Calculator.new) arr = ensure_array(collection) return [] if expr.nil? || expr.strip.empty? arr.map do |item| begin # Provide 'item' binding; if item is a Hash, flatten simple keys ctx = { "item" => item } if item.is_a?(Hash) item.each { |k, v| ctx[k.to_s] = v } end calculator.evaluate!(expr, ctx) rescue StandardError nil end end end |
.MAX(*args) ⇒ Object
MAX dual mode: returns nil if no numeric values
144 145 146 147 148 149 150 151 |
# File 'lib/stellwerk/functions.rb', line 144 def MAX(*args) values = if args.length == 1 && args.first.is_a?(Array) coerce_numeric_list(args.first) else coerce_numeric_list(args) end values.max end |
.MIN(*args) ⇒ Object
MIN dual mode: returns nil if no numeric values
134 135 136 137 138 139 140 141 |
# File 'lib/stellwerk/functions.rb', line 134 def MIN(*args) values = if args.length == 1 && args.first.is_a?(Array) coerce_numeric_list(args.first) else coerce_numeric_list(args) end values.min end |
.PROJECT(collection, field_name) ⇒ Object
Internal projection helper: given an array of hashes (or objects responding to [] / .public_send) and a field name, extract that field, allowing nils. Named PROJECT (instead of __project) because Dentaku will constantize function names.
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/stellwerk/functions.rb', line 48 def PROJECT(collection, field_name) arr = ensure_array(collection) arr.map do |item| if item.is_a?(Hash) item[field_name.to_sym] || item[field_name.to_s] elsif item.respond_to?(:[]) && !item.is_a?(String) begin item[field_name] rescue StandardError nil end elsif item.respond_to?(field_name) begin item.public_send(field_name) rescue StandardError nil end else nil end end end |
.REDUCE(collection, initial, expr, calculator: Dentaku::Calculator.new) ⇒ Object
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 |
# File 'lib/stellwerk/functions.rb', line 288 def REDUCE(collection, initial, expr, calculator: Dentaku::Calculator.new) arr = ensure_array(collection) return initial if arr.empty? accumulator = initial arr.each do |item| begin ctx = { "item" => item, "accumulator" => accumulator, "acc" => accumulator } if item.is_a?(Hash) item.each { |k, v| ctx[k.to_s] = v } end accumulator = calculator.evaluate!(expr, ctx) rescue StandardError nil end end accumulator end |
.SUM(*args) ⇒ Object
SUM dual mode
115 116 117 118 119 120 121 122 |
# File 'lib/stellwerk/functions.rb', line 115 def SUM(*args) values = if args.length == 1 && args.first.is_a?(Array) coerce_numeric_list(args.first) else coerce_numeric_list(args) end values.sum end |
.SUMIF(collection, predicate_expr, projection_expr = nil, calculator: Dentaku::Calculator.new) ⇒ Object
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 |
# File 'lib/stellwerk/functions.rb', line 240 def SUMIF(collection, predicate_expr, projection_expr = nil, calculator: Dentaku::Calculator.new) arr = ensure_array(collection) return 0 if predicate_expr.nil? || predicate_expr.strip.empty? total = 0.0 arr.each do |item| begin ctx = { "item" => item } if item.is_a?(Hash) item.each { |k, v| ctx[k.to_s] = v } end next unless calculator.evaluate!(predicate_expr, ctx) value = if projection_expr && !projection_expr.strip.empty? calculator.evaluate!(projection_expr, ctx) rescue nil else item end case value when Numeric total += value.to_f when String v = value.strip total += v.to_f if v.match?(/^[-+]?\d+(?:\.\d+)?$/) end rescue StandardError next end end total end |
.TAKE(arg, n) ⇒ Object
163 164 165 166 167 168 169 170 171 172 |
# File 'lib/stellwerk/functions.rb', line 163 def TAKE(arg, n) arr = ensure_array(arg) limit = begin Integer(n) rescue StandardError 0 end return [] if limit <= 0 arr.first(limit) end |
.TRACE(value, _label = nil) ⇒ Object
307 308 309 310 |
# File 'lib/stellwerk/functions.rb', line 307 def TRACE(value, _label = nil) # Future: push to instrumentation buffer value end |