amoskeag-rb 0.4.0-x86_64-linux

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0a949a31b6d11220496d214f06316cefae2b117e421bb31c6825781e92bd5835
4
+ data.tar.gz: '09b52fa870f9ec48e3b1ec718733f2c11e1d2c4690b21a7e0fe5d5c477ebdcb7'
5
+ SHA512:
6
+ metadata.gz: a82e87a46dcba64b84eb27b81101c2da844a27ed5fb10ed6a8633f78b70d630c7e983b0974fe228e3f3d45e697459ab9b99ec887fb0d98b4cbef7e59f4bef137
7
+ data.tar.gz: '0978a7b9183716b210d2356b13d2bb64227131a7bef0fcd86934b31ae789b46bcce7263e99fb69491b344daf8e898499f5e8335b04a10aef3487a37cf1805c19'
data/CHANGELOG.md ADDED
@@ -0,0 +1,100 @@
1
+ # Changelog
2
+
3
+ All notable changes to the amoskeag-rb gem will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.4.0] - 2026-10-30
9
+
10
+ 0.3.0 was tagged but never published to RubyGems because its release build
11
+ failed. 0.4.0 ships everything listed under 0.3.0 plus the fixes below.
12
+
13
+ ### Changed
14
+ - `rb_sys` runtime dependency raised to `~> 0.9.117` (source-gem installs only).
15
+
16
+ ### Fixed
17
+ - Precompiled Linux and Windows gems failed to build: the cross-compile images
18
+ were selected from the old `rb_sys` constraint and their cargo could not read
19
+ `Cargo.lock` version 4.
20
+ - `x86_64-darwin` gem build never ran because its CI runner had been retired.
21
+ - Flaky thread-safety tests that depended on thread completion order.
22
+
23
+ ## [0.3.0] - 2026-09-25
24
+
25
+ ### Added
26
+ - Exact `Integer` and `BigDecimal` support. Ruby Integers and BigDecimals passed
27
+ in as data stay exact, and Amoskeag integer and decimal results come back as
28
+ `Integer` and `BigDecimal` instead of widening to `Float`.
29
+
30
+ ### Fixed
31
+ - Source gem failed to compile (`non-exhaustive patterns: AmoskeagValue::Decimal
32
+ and AmoskeagValue::Integer not covered`). The amoskeag git dependency was not
33
+ pinned and the shipped member-level `Cargo.lock` was ignored by the workspace,
34
+ so installs built against upstream HEAD. The dependency is now pinned to a
35
+ fixed `rev` and the workspace `Cargo.lock` ships in the gem.
36
+ - Precompiled platform gems announced in 0.2.0 were never published because the
37
+ build workflow and cross-compile config were not committed. They are now.
38
+ - `extconf.rb` only passes `-static-libgcc` when the linker driver supports it.
39
+
40
+ ## [0.2.0] - 2026-06-19
41
+
42
+ ### Added
43
+ - Precompiled, statically-linked native gems for every supported platform, so
44
+ `gem install amoskeag-rb` downloads a ready-to-run binary — no Rust toolchain,
45
+ no `cargo`, no compile step. Targets: `x86_64-linux`, `aarch64-linux`,
46
+ `x86_64-linux-musl`, `aarch64-linux-musl`, `x64-mingw-ucrt`, `arm64-darwin`,
47
+ and `x86_64-darwin`, each built against Ruby 3.1–3.4.
48
+ - `misc/native.yml` as the single source of truth for build platforms + ABIs.
49
+ - `rakelib/cross.rake` with `rake gem:<platform>` / `gem:cross` tasks (rb-sys-dock)
50
+ for local cross-builds.
51
+ - GitHub Actions workflow (`.github/workflows/gem.yml`) that tests, builds every
52
+ platform gem plus a source fallback, and publishes to RubyGems on tag via
53
+ Trusted Publishing (OIDC).
54
+
55
+ ### Changed
56
+ - Build switched from `Rake::ExtensionTask` to `RbSys::ExtensionTask`; the Rust
57
+ crate is now a Cargo workspace member (root `Cargo.toml`) so cross-compilation
58
+ and `cargo metadata` resolve cleanly.
59
+ - The native binary loads from a per-ABI path (`lib/amoskeag-rb/<ruby>/`), with a
60
+ fallback for source-gem installs.
61
+ - The source gem links libstdc++/libgcc statically on Linux for portability.
62
+
63
+ ### Fixed
64
+ - The extension loader no longer relies on a development-only relative path that
65
+ failed for installed gems.
66
+
67
+ ## [0.1.0] - 2025-01-20
68
+
69
+ ### Added
70
+ - Initial release of amoskeag-rb gem
71
+ - Native Ruby bindings to Amoskeag DSL
72
+ - FFI layer using statically compiled Rust library
73
+ - Core API methods:
74
+ - `Amoskeag.compile(source, symbols)` - Compile programs with symbol validation
75
+ - `Amoskeag.evaluate(program, data)` - Evaluate compiled programs
76
+ - `Amoskeag.eval_expression(source, data, symbols)` - Compile and evaluate in one step
77
+ - Full support for all Amoskeag data types:
78
+ - Numbers, Strings, Booleans, Nil
79
+ - Arrays and Dictionaries
80
+ - Symbols with compile-time validation
81
+ - Ruby-friendly API with automatic type conversion
82
+ - Thread-safe compiled programs
83
+ - Comprehensive error handling with CompileError and EvalError
84
+ - Complete documentation and examples
85
+ - JSON-based marshalling between Ruby and native code
86
+
87
+ ### Features
88
+ - Business Rules Engine capabilities
89
+ - Template Engine functionality
90
+ - Spreadsheet Formula support
91
+ - 60+ standard library functions
92
+ - Excel-compatible financial functions
93
+ - Safe navigation (nil-safe property access)
94
+ - Pure functional evaluation (no side effects)
95
+ - Security-first design (immune to code injection)
96
+
97
+ [0.4.0]: https://github.com/durable-oss/amoskeag-rb/releases/tag/v0.4.0
98
+ [0.3.0]: https://github.com/durable-oss/amoskeag-rb/releases/tag/v0.3.0
99
+ [0.2.0]: https://github.com/durable-oss/amoskeag-rb/releases/tag/v0.2.0
100
+ [0.1.0]: https://github.com/durable-oss/amoskeag-rb/releases/tag/v0.1.0
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Durable Programming, LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,336 @@
1
+ # Amoskeag Ruby Gem
2
+
3
+ Native Ruby bindings for [Amoskeag](https://github.com/durable-oss/amoskeag) - a secure, purely functional DSL for business rules, templates, and spreadsheet formulas.
4
+
5
+ ## Features
6
+
7
+ - **Security-First Design**: Immune to code injection attacks (SSTI, RCE)
8
+ - **Purely Functional**: No side effects, no mutation, no I/O
9
+ - **Static Validation**: Symbols and functions validated at compile-time
10
+ - **High Performance**: Native Rust implementation with zero-copy FFI
11
+ - **Rich Standard Library**: 60+ built-in functions for strings, numbers, collections, and finance
12
+ - **Multiple Use Cases**:
13
+ - Business Rules Engine (insurance, loans, eligibility)
14
+ - Template Engine (secure alternative to ERB)
15
+ - Spreadsheet Formulas (Excel-compatible financial functions)
16
+
17
+ ## Installation
18
+
19
+ ### Prerequisites
20
+
21
+ - Ruby 2.7 or higher
22
+ - Rust and Cargo (for building the native extension)
23
+
24
+ ### Install from source
25
+
26
+ ```bash
27
+ gem build amoskeag-rb.gemspec
28
+ gem install amoskeag-rb-0.1.0.gem
29
+ ```
30
+
31
+ Or add to your Gemfile:
32
+
33
+ ```ruby
34
+ gem 'amoskeag-rb', path: 'path/to/amoskeag-rb'
35
+ ```
36
+
37
+ ## Quick Start
38
+
39
+ ```ruby
40
+ require 'amoskeag-rb'
41
+
42
+ # Basic arithmetic
43
+ Amoskeag.eval_expression("2 + 2", {})
44
+ # => 4.0
45
+
46
+ # Using variables
47
+ Amoskeag.eval_expression("user.age * 2", { "user" => { "age" => 25 } })
48
+ # => 50.0
49
+
50
+ # String operations with pipe
51
+ Amoskeag.eval_expression('"hello world" | upcase', {})
52
+ # => "HELLO WORLD"
53
+
54
+ # Business rules with symbols
55
+ code = "if user.age >= 18 :adult else :minor end"
56
+ Amoskeag.eval_expression(code, { "user" => { "age" => 25 } }, [:adult, :minor])
57
+ # => :adult
58
+ ```
59
+
60
+ ## Core Concepts
61
+
62
+ ### Compile Once, Evaluate Many
63
+
64
+ For best performance, compile programs once and evaluate them multiple times:
65
+
66
+ ```ruby
67
+ # Compile the program (with symbol validation)
68
+ program = Amoskeag.compile(
69
+ "if score >= 90 :A else :B end",
70
+ [:A, :B, :C, :D, :F]
71
+ )
72
+
73
+ # Evaluate with different data
74
+ Amoskeag.evaluate(program, { "score" => 95 }) # => :A
75
+ Amoskeag.evaluate(program, { "score" => 85 }) # => :B
76
+ ```
77
+
78
+ ### Data Types
79
+
80
+ Amoskeag supports seven fundamental types:
81
+
82
+ ```ruby
83
+ # Numbers (64-bit float)
84
+ Amoskeag.eval_expression("42", {}) # => 42.0
85
+
86
+ # Strings
87
+ Amoskeag.eval_expression('"hello"', {}) # => "hello"
88
+
89
+ # Booleans
90
+ Amoskeag.eval_expression("true and false", {}) # => false
91
+
92
+ # Nil
93
+ Amoskeag.eval_expression("nil", {}) # => nil
94
+
95
+ # Arrays
96
+ Amoskeag.eval_expression("[1, 2, 3]", {}) # => [1.0, 2.0, 3.0]
97
+
98
+ # Dictionaries (hashes)
99
+ Amoskeag.eval_expression('{"name": "Alice", "age": 30}', {})
100
+ # => {"name" => "Alice", "age" => 30.0}
101
+
102
+ # Symbols (validated at compile-time)
103
+ Amoskeag.eval_expression(":approved", {}, [:approved, :denied])
104
+ # => :approved
105
+ ```
106
+
107
+ ### Safe Navigation
108
+
109
+ Missing keys return `nil` instead of raising errors:
110
+
111
+ ```ruby
112
+ Amoskeag.eval_expression("user.address.street", { "user" => {} })
113
+ # => nil
114
+ ```
115
+
116
+ ## Examples
117
+
118
+ ### Business Rules Engine
119
+
120
+ ```ruby
121
+ # Define eligibility rules
122
+ rules = <<~AMOSKEAG
123
+ let applicant = {
124
+ "age": user.age,
125
+ "state": user.state,
126
+ "credit_score": user.credit_score
127
+ }
128
+ in let restricted_states = ["FL", "LA", "TX"]
129
+ in
130
+ if contains(restricted_states, applicant.state)
131
+ :deny
132
+ else if applicant.age < 18
133
+ :deny
134
+ else if applicant.credit_score < 650
135
+ :conditional
136
+ else
137
+ :approve
138
+ end
139
+ AMOSKEAG
140
+
141
+ # Compile once
142
+ program = Amoskeag.compile(rules, [:approve, :deny, :conditional])
143
+
144
+ # Evaluate for different users
145
+ user1 = { "age" => 25, "state" => "CA", "credit_score" => 720 }
146
+ Amoskeag.evaluate(program, { "user" => user1 }) # => :approve
147
+
148
+ user2 = { "age" => 30, "state" => "FL", "credit_score" => 750 }
149
+ Amoskeag.evaluate(program, { "user" => user2 }) # => :deny
150
+ ```
151
+
152
+ ### Template Engine
153
+
154
+ ```ruby
155
+ template = <<~AMOSKEAG
156
+ "Hello, " + user.name + "! " +
157
+ if user.premium
158
+ "Welcome to Premium. "
159
+ else
160
+ "Upgrade to Premium today! "
161
+ end +
162
+ "You have " + user.messages | string + " new messages."
163
+ AMOSKEAG
164
+
165
+ program = Amoskeag.compile(template, [])
166
+
167
+ data = {
168
+ "user" => {
169
+ "name" => "Alice",
170
+ "premium" => true,
171
+ "messages" => 5
172
+ }
173
+ }
174
+
175
+ Amoskeag.evaluate(program, data)
176
+ # => "Hello, Alice! Welcome to Premium. You have 5 new messages."
177
+ ```
178
+
179
+ ### Financial Calculations
180
+
181
+ ```ruby
182
+ # Monthly payment for a $250,000 loan at 4.5% APR for 30 years
183
+ # pmt(rate, nper, pv, type_) - type_ is 0 for end-of-period payments
184
+ formula = "pmt(rate / 12, years * 12, -principal, 0) | round(2)"
185
+ program = Amoskeag.compile(formula, [])
186
+
187
+ data = {
188
+ "rate" => 0.045,
189
+ "years" => 30,
190
+ "principal" => 250000
191
+ }
192
+
193
+ Amoskeag.evaluate(program, data)
194
+ # => 1266.71
195
+ ```
196
+
197
+ ### Array and Collection Operations
198
+
199
+ ```ruby
200
+ # Calculate average of filtered values
201
+ code = <<~AMOSKEAG
202
+ let nums = [95, 82, 67, 88, 91, 73]
203
+ in let passing = nums | filter(lambda(x) { x >= 70 })
204
+ in passing | avg | round(2)
205
+ AMOSKEAG
206
+
207
+ Amoskeag.eval_expression(code, {})
208
+ # => 85.8
209
+ ```
210
+
211
+ ## Standard Library
212
+
213
+ Amoskeag includes 60+ built-in functions organized into categories:
214
+
215
+ ### String Functions
216
+ - `upcase`, `downcase`, `capitalize`
217
+ - `strip`, `lstrip`, `rstrip`
218
+ - `split`, `join`, `replace`
219
+ - `truncate`, `prepend`, `append`
220
+
221
+ ### Numeric Functions
222
+ - `abs`, `ceil`, `floor`, `round`
223
+ - `max`, `min`, `clamp`
224
+ - `plus`, `minus`, `times`, `divided_by`
225
+
226
+ ### Collection Functions
227
+ - `size`, `first`, `last`, `at`
228
+ - `contains`, `sort`, `reverse`
229
+ - `sum`, `avg`, `max`, `min`
230
+ - `keys`, `values`
231
+
232
+ ### Logic Functions
233
+ - `if_then_else`, `choose`
234
+ - `is_number`, `is_string`, `is_boolean`, `is_nil`, `is_array`, `is_dictionary`
235
+ - `coalesce`, `default`
236
+
237
+ ### Financial Functions (Excel-compatible)
238
+ - **Time Value**: `pmt`, `pv`, `fv`, `nper`, `rate`
239
+ - **Investment**: `npv`, `irr`, `mirr`
240
+ - **Depreciation**: `sln`, `ddb`, `db`
241
+ - **Payment Components**: `ipmt`, `ppmt`, `cumipmt`, `cumprinc`
242
+
243
+ ## Error Handling
244
+
245
+ ```ruby
246
+ begin
247
+ # Undefined symbol will raise CompileError
248
+ Amoskeag.compile(":invalid_symbol", [:valid, :symbols])
249
+ rescue Amoskeag::CompileError => e
250
+ puts "Compilation failed: #{e.message}"
251
+ end
252
+
253
+ begin
254
+ # Missing variable will raise EvalError
255
+ program = Amoskeag.compile("missing_var", [])
256
+ Amoskeag.evaluate(program, {})
257
+ rescue Amoskeag::EvalError => e
258
+ puts "Evaluation failed: #{e.message}"
259
+ end
260
+ ```
261
+
262
+ ## Performance Tips
263
+
264
+ 1. **Compile once, evaluate many**: Compilation has overhead, so reuse compiled programs
265
+ 2. **Use native types**: Ruby Symbols map directly to Amoskeag Symbols with zero overhead
266
+ 3. **Keep data shallow**: Deep nesting requires more JSON serialization
267
+ 4. **Avoid string concatenation in loops**: Use `join` function instead
268
+
269
+ ## Thread Safety
270
+
271
+ Compiled programs are immutable and thread-safe. You can safely evaluate the same program from multiple threads concurrently:
272
+
273
+ ```ruby
274
+ program = Amoskeag.compile("x * 2", [])
275
+
276
+ threads = 10.times.map do |i|
277
+ Thread.new do
278
+ Amoskeag.evaluate(program, { "x" => i })
279
+ end
280
+ end
281
+
282
+ results = threads.map(&:value)
283
+ # => [0.0, 2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 18.0]
284
+ ```
285
+
286
+ ## Building from Source
287
+
288
+ ```bash
289
+ # Clone the repository
290
+ git clone https://github.com/durable-oss/amoskeag-rb.git
291
+ cd amoskeag-rb
292
+
293
+ # Build and install the gem
294
+ gem build amoskeag-rb.gemspec
295
+ gem install amoskeag-rb-0.1.0.gem
296
+ ```
297
+
298
+ ### Development
299
+
300
+ ```bash
301
+ # Build the native extension
302
+ cd amoskeag
303
+ ruby extconf.rb
304
+ make
305
+
306
+ # Run tests
307
+ rake test
308
+ ```
309
+
310
+ ## Architecture
311
+
312
+ The gem consists of four layers:
313
+
314
+ 1. **Rust Core**: Pure Rust implementation of the Amoskeag compiler and interpreter (from [durable-oss/amoskeag](https://github.com/durable-oss/amoskeag))
315
+ 2. **FFI Layer** (`amoskeag/src/lib.rs`): C-compatible FFI bindings using static library
316
+ 3. **Ruby Extension** (`amoskeag/amoskeag_native.c`): Native Ruby C extension
317
+ 4. **Ruby Wrapper** (`lib/amoskeag-rb.rb`): Idiomatic Ruby API with proper marshalling
318
+
319
+ Data flows through JSON serialization at the Ruby/C boundary for simplicity and type safety.
320
+
321
+ ## License
322
+
323
+ MIT
324
+
325
+ ## Contributing
326
+
327
+ Contributions are welcome! Please open issues or pull requests on the [GitHub repository](https://github.com/durable-oss/amoskeag-rb).
328
+
329
+ ## Support
330
+
331
+ - GitHub Issues: https://github.com/durable-oss/amoskeag-rb/issues
332
+ - Documentation: https://github.com/durable-oss/amoskeag-rb
333
+
334
+ ## Credits
335
+
336
+ Amoskeag is built by [Durable Programming](https://github.com/durable-oss) with a focus on security, correctness, and developer experience.
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Amoskeag
4
+ VERSION = "0.4.0"
5
+ AMOSKEAG_VERSION = "0.1.2"
6
+ end
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require_relative 'amoskeag-rb/version'
5
+
6
+ # Load the native extension.
7
+ #
8
+ # Precompiled platform gems install the binary under a per-ABI directory
9
+ # (lib/amoskeag-rb/<ruby_major.minor>/amoskeag_native.so) so a single gem can
10
+ # carry binaries for several Ruby versions. We try that first, then fall back to
11
+ # the flat layouts used by a freshly `rake compile`d source checkout.
12
+ begin
13
+ ruby_abi = RUBY_VERSION[/\d+\.\d+/]
14
+ require_relative "amoskeag-rb/#{ruby_abi}/amoskeag_native"
15
+ rescue LoadError
16
+ begin
17
+ require_relative 'amoskeag-rb/amoskeag_native'
18
+ rescue LoadError
19
+ require 'amoskeag_native'
20
+ end
21
+ end
22
+
23
+ # Amoskeag - A secure, functional DSL for business rules
24
+ #
25
+ # The native extension defines:
26
+ # - Amoskeag::Error < StandardError
27
+ # - Amoskeag::CompileError < Amoskeag::Error
28
+ # - Amoskeag::EvalError < Amoskeag::Error
29
+ # - Amoskeag::Program (compiled program class)
30
+ # - Amoskeag.compile(source, symbols = []) -> Program
31
+ # - Amoskeag.evaluate(program, data) -> Object
32
+ # - Amoskeag.eval_expression(source, data, symbols = []) -> Object
33
+
34
+ module Amoskeag
35
+ class << self
36
+ # Alias native methods for potential future wrapper use
37
+ alias compile_native compile
38
+ alias evaluate_native evaluate
39
+ alias eval_expression_native eval_expression
40
+
41
+ # Unwrap symbols from their hash representation at the top level only
42
+ # This allows expression results like `:adult` to be returned as symbols
43
+ # while keeping symbols nested in data structures in wrapped format
44
+ def unwrap_symbols(value)
45
+ case value
46
+ when Hash
47
+ # Check if this is a wrapped symbol at the top level
48
+ if value.keys == ["__symbol__"]
49
+ value["__symbol__"].to_sym
50
+ else
51
+ # Don't unwrap nested symbols - they're part of data structures
52
+ value
53
+ end
54
+ else
55
+ value
56
+ end
57
+ end
58
+
59
+ # Recursively wrap symbols for native processing
60
+ def wrap_symbols(value)
61
+ case value
62
+ when Symbol
63
+ { "__symbol__" => value.to_s }
64
+ when Hash
65
+ value.transform_values { |v| wrap_symbols(v) }
66
+ when Array
67
+ value.map { |v| wrap_symbols(v) }
68
+ else
69
+ value
70
+ end
71
+ end
72
+
73
+ # Numeric classes the native extension knows how to convert.
74
+ #
75
+ # BigDecimal maps to Amoskeag's exact base-10 Decimal, but bigdecimal is not
76
+ # loaded by default and this gem does not force it on host programs. The
77
+ # class is only included once something else has required it.
78
+ def scalar_types
79
+ types = [String, Integer, Float, TrueClass, FalseClass, NilClass, Symbol]
80
+ types << BigDecimal if defined?(BigDecimal)
81
+ types
82
+ end
83
+
84
+ # Validate that data is JSON-serializable
85
+ def validate_data(data, path = "data")
86
+ case data
87
+ when *scalar_types
88
+ # These are valid types (note: not all Numeric — only the ones the
89
+ # native extension can convert)
90
+ data
91
+ when Hash
92
+ data.each do |key, value|
93
+ unless key.is_a?(String) || key.is_a?(Symbol)
94
+ raise ArgumentError, "#{path} key must be String or Symbol, got #{key.class}"
95
+ end
96
+ validate_data(value, "#{path}[#{key.inspect}]")
97
+ end
98
+ data
99
+ when Array
100
+ data.each_with_index do |item, idx|
101
+ validate_data(item, "#{path}[#{idx}]")
102
+ end
103
+ data
104
+ else
105
+ # Invalid type - include path information
106
+ raise ArgumentError, "#{path} contains invalid type #{data.class}"
107
+ end
108
+ end
109
+ end
110
+
111
+ # Override the compile method (no wrapping needed, just validation)
112
+ def self.compile(source, symbols = [])
113
+ compile_native(source, symbols)
114
+ end
115
+
116
+ # Override the evaluate method to wrap/unwrap symbols and validate data
117
+ def self.evaluate(program, data)
118
+ validate_data(data)
119
+ result = evaluate_native(program, data)
120
+ unwrap_symbols(result)
121
+ end
122
+
123
+ # Override the eval_expression method to wrap/unwrap symbols and validate data
124
+ def self.eval_expression(source, data, symbols = [])
125
+ validate_data(data)
126
+ result = eval_expression_native(source, data, symbols)
127
+ unwrap_symbols(result)
128
+ end
129
+
130
+ # Override Program.new to raise NoMethodError
131
+ class Program
132
+ def self.new(*)
133
+ raise NoMethodError, "undefined method `new' for Amoskeag::Program"
134
+ end
135
+ end
136
+ end
metadata ADDED
@@ -0,0 +1,150 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: amoskeag-rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
+ platform: x86_64-linux
6
+ authors:
7
+ - Durable Programming
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-10-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: json
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '13.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '13.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake-compiler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.2'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '5.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: yard
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.9'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.9'
83
+ description: |
84
+ Amoskeag is a purely functional, statically-validated Domain-Specific Language (DSL)
85
+ designed for high-security, sandboxed evaluation. It's perfect for:
86
+
87
+ - Business Rules Engines (insurance underwriting, loan approval)
88
+ - Template Engines (secure alternative to ERB with more power than Liquid)
89
+ - Spreadsheet Formula Engines (Excel-like calculations)
90
+
91
+ This gem provides native Ruby bindings to the Amoskeag library, compiled from Rust
92
+ for maximum performance and security.
93
+ email:
94
+ - djberube@durableprogramming.com
95
+ executables: []
96
+ extensions: []
97
+ extra_rdoc_files: []
98
+ files:
99
+ - CHANGELOG.md
100
+ - LICENSE
101
+ - README.md
102
+ - lib/amoskeag-rb.rb
103
+ - lib/amoskeag-rb/3.1/amoskeag_native.so
104
+ - lib/amoskeag-rb/3.2/amoskeag_native.so
105
+ - lib/amoskeag-rb/3.3/amoskeag_native.so
106
+ - lib/amoskeag-rb/3.4/amoskeag_native.so
107
+ - lib/amoskeag-rb/version.rb
108
+ homepage: https://github.com/durable-oss/amoskeag-rb
109
+ licenses:
110
+ - MIT
111
+ metadata:
112
+ homepage_uri: https://github.com/durable-oss/amoskeag-rb
113
+ source_code_uri: https://github.com/durable-oss/amoskeag-rb
114
+ changelog_uri: https://github.com/durable-oss/amoskeag-rb/blob/main/CHANGELOG.md
115
+ post_install_message: |2+
116
+
117
+ Thank you for installing amoskeag-rb!
118
+
119
+ Amoskeag is a secure, purely functional DSL for business rules, templates,
120
+ and spreadsheet formulas. It's designed to be immune to code injection
121
+ attacks while providing powerful expression evaluation capabilities.
122
+
123
+ Get started:
124
+ require 'amoskeag-rb'
125
+ result = Amoskeag.eval_expression("2 + 2", {}) # => 4.0
126
+
127
+ Documentation: https://github.com/durable-oss/amoskeag-rb
128
+
129
+ rdoc_options: []
130
+ require_paths:
131
+ - lib
132
+ required_ruby_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '3.1'
137
+ - - "<"
138
+ - !ruby/object:Gem::Version
139
+ version: 3.5.dev
140
+ required_rubygems_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ requirements: []
146
+ rubygems_version: 3.5.23
147
+ signing_key:
148
+ specification_version: 4
149
+ summary: Ruby bindings for Amoskeag - a secure, functional DSL for business rules
150
+ test_files: []