dmn 0.0.2 → 0.0.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 49694c6ba7cf12162a99192f8a7080d0b7134e8e5a2a0a5d0f88ad1c14bb59f0
4
- data.tar.gz: 63d487600e8b8019d96d7596407a71a4c0e188c07e9ee901c914242cfdcf3bc4
3
+ metadata.gz: 99f8ad17d713e6ce95941ee100f3c4046a2ffc93155ea6629a6761bb3745e1ca
4
+ data.tar.gz: ad53db39e8b2e96884644d177486b8cf1d65a8d608d5b09c136ee37b0d3daad5
5
5
  SHA512:
6
- metadata.gz: 29eb3d6a96514f1056bb3d37779afaeb4d26a62eb5cafe87cb179425d3791d8acea77934cf8f48d51ad94341726c4e38a43221f8e3913e5df8ed6a77306a5036
7
- data.tar.gz: 3b965748993649d0af143dbcc1eb237f203dedf2e37de054e5622d04010d81293ddf116ae4128c3dfef3f37ecbfe5d5e74ee0f33e951cd59ff43208f7a5f8a57
6
+ metadata.gz: 8e8009211631110c50467c8ff2f8b857095ca497409171590d44135c2bd25ee5ced0d7d99c676f48a0137c3b7083c00e349f0853cca2cbfae0e45baa316b612e
7
+ data.tar.gz: bf0a15e9f42f288505314de24eec35a70fdc621238e6944bd46d69b72e82de83cddab12cc891e6f2542bf5a99c7b5c32aa17a216ba4c00ef9482692abac3289a
data/README.md CHANGED
@@ -1,69 +1,11 @@
1
- # Spot Feel
1
+ # DMN
2
2
 
3
- A light-weight DMN FEEL expression evaluator and business rule engine in Ruby.
3
+ A light-weight DMN (Decision Model and Notation) business rule ruby gem.
4
4
 
5
- This gem implements a subset of FEEL (Friendly Enough Expression Language) as defined in the [DMN 1.3 specification](https://www.omg.org/spec/DMN/1.3/PDF) with some additional extensions.
6
-
7
- FEEL expressions are parsed into an abstract syntax tree (AST) and then evaluated in a context. The context is a hash of variables and functions to be resolved inside the expression.
8
-
9
- Expressions are safe, side-effect free, and deterministic. They are ideal for capturing business logic for storage in a database or embedded in DMN, BPMN, or Form documents for execution in a workflow engine like [Spot Flow](https://github.com/connectedbits/spot-flow).
10
-
11
- This project was inspired by these excellent libraries:
12
-
13
- - [feelin](https://github.com/nikku/feelin)
14
- - [dmn-eval-js](https://github.com/mineko-io/dmn-eval-js)
5
+ This gem depends on the [FEEL](https://github.com/connectedbits/bpmn/tree/main/feel) gem to evaluate DMN decision tables and expressions.
15
6
 
16
7
  ## Usage
17
8
 
18
- To evaluate an expression:
19
-
20
- ```ruby
21
- DMN.evaluate('"👋 Hello " + name', variables: { name: "World" })
22
- # => "👋 Hello World"
23
- ```
24
-
25
- A slightly more complex example:
26
-
27
- ```ruby
28
- variables = {
29
- person: {
30
- name: "Eric",
31
- age: 59,
32
- }
33
- }
34
- DMN.evaluate('if person.age >= 18 then "adult" else "minor"', variables:)
35
- # => "adult"
36
- ```
37
-
38
- Calling a built-in function:
39
-
40
- ```ruby
41
- DMN.evaluate('sum([1, 2, 3])')
42
- # => 6
43
- ```
44
-
45
- Calling a user-defined function:
46
-
47
- ```ruby
48
- DMN.config.functions = {
49
- "reverse": ->(s) { s.reverse }
50
- }
51
- DMN.evaluate('reverse("Hello World!")', functions:)
52
- # => "!dlroW olleH"
53
- ```
54
-
55
- To evaluate a unary tests:
56
-
57
- ```ruby
58
- DMN.test(3, '<= 10, > 50'))
59
- # => true
60
- ```
61
-
62
- ```ruby
63
- DMN.test("Eric", '"Bob", "Holly", "Eric"')
64
- # => true
65
- ```
66
-
67
9
  ![Decision Table](docs/media/decision_table.png)
68
10
 
69
11
  To evaluate a DMN decision table:
@@ -80,71 +22,8 @@ result = DMN.decide('fine_decision', definitions_xml: fixture_source("fine.dmn")
80
22
  # => { "amount" => 1000, "points" => 7 })
81
23
  ```
82
24
 
83
- To get a list of variables or functions used in an expression:
84
-
85
- ```ruby
86
- LiteralExpression.new(text: 'person.first_name + " " + person.last_name').variable_names
87
- # => ["person.age, person.last_name"]
88
- ```
89
-
90
- ```ruby
91
- LiteralExpression.new(text: 'sum([1, 2, 3])').function_names
92
- # => ["sum"]
93
- ```
94
-
95
- ```ruby
96
- UnaryTests.new(text: '> speed - speed_limit').variable_names
97
- # => ["speed, speed_limit"]
98
- ```
99
-
100
25
  ## Supported Features
101
26
 
102
- ### Data Types
103
-
104
- - [x] Boolean (true, false)
105
- - [x] Number (integer, decimal)
106
- - [x] String (single and double quoted)
107
- - [x] Date, Time, Duration (ISO 8601)
108
- - [x] List (array)
109
- - [x] Context (hash)
110
-
111
- ### Expressions
112
-
113
- - [x] Literal
114
- - [x] Path
115
- - [x] Arithmetic
116
- - [x] Comparison
117
- - [x] Function Invocation
118
- - [x] Positional Parameters
119
- - [x] If Expression
120
- - [ ] For Expression
121
- - [ ] Quantified Expression
122
- - [ ] Filter Expression
123
- - [ ] Disjunction
124
- - [ ] Conjuction
125
- - [ ] Instance Of
126
- - [ ] Function Definition
127
-
128
- ### Unary Tests
129
-
130
- - [x] Comparison
131
- - [x] Interval/Range (inclusive and exclusive)
132
- - [x] Disjunction
133
- - [x] Negation
134
- - [ ] Expression
135
-
136
- ### Built-in Functions
137
-
138
- - [x] Conversion: `string`, `number`
139
- - [x] Boolean: `not`, `is defined`, `get or else`
140
- - [x] String: `substring`, `substring before`, `substring after`, `string length`, `upper case`, `lower case`, `contains`, `starts with`, `ends with`, `matches`, `replace`, `split`, `strip`, `extract`
141
- - [x] Numeric: `decimal`, `floor`, `ceiling`, `round`, `abs`, `modulo`, `sqrt`, `log`, `exp`, `odd`, `even`, `random number`
142
- - [x] List: `list contains`, `count`, `min`, `max`, `sum`, `product`, `mean`, `median`, `stddev`, `mode`, `all`, `any`, `sublist`, `append`, `concatenate`, `insert before`, `remove`, `reverse`, `index of`, `union`, `distinct values`, `duplicate values`, `flatten`, `sort`, `string join`
143
- - [x] Context: `get entries`, `get value`, `get keys`
144
- - [x] Temporal: `now`, `today`, `day of week`, `day of year`, `month of year`, `week of year`
145
-
146
- ### DMN
147
-
148
27
  - [x] Parse DMN XML documents
149
28
  - [x] Evaluate DMN Decision Tables
150
29
  - [x] Evaluate dependent DMN Decision Tables
@@ -172,10 +51,6 @@ $ bin/setup
172
51
  $ bin/guard
173
52
  ```
174
53
 
175
- ## Development
176
-
177
- [Treetop Doumentation](https://cjheath.github.io/treetop/syntactic_recognition.html) is a good place to start learning about Treetop.
178
-
179
54
  ## License
180
55
 
181
56
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/lib/dmn/decision.rb CHANGED
@@ -7,7 +7,7 @@ module DMN
7
7
  def self.from_json(json)
8
8
  information_requirements = Array.wrap(json[:information_requirement]).map { |ir| InformationRequirement.from_json(ir) } if json[:information_requirement]
9
9
  decision_table = DecisionTable.from_json(json[:decision_table]) if json[:decision_table]
10
- literal_expression = LiteralExpression.from_json(json[:literal_expression]) if json[:literal_expression]
10
+ literal_expression = FEEL::LiteralExpression.from_json(json[:literal_expression]) if json[:literal_expression]
11
11
  variable = Variable.from_json(json[:variable]) if json[:variable]
12
12
  Decision.new(id: json[:id], name: json[:name], decision_table:, variable:, literal_expression:, information_requirements:)
13
13
  end
data/lib/dmn/input.rb CHANGED
@@ -5,7 +5,8 @@ module DMN
5
5
  attr_reader :id, :label, :input_expression
6
6
 
7
7
  def self.from_json(json)
8
- input_expression = LiteralExpression.from_json(json[:input_expression]) if json[:input_expression]
8
+ input_expression = FEEL::
9
+ LiteralExpression.from_json(json[:input_expression]) if json[:input_expression]
9
10
  Input.new(id: json[:id], label: json[:label], input_expression:)
10
11
  end
11
12
 
data/lib/dmn/rule.rb CHANGED
@@ -5,8 +5,8 @@ module DMN
5
5
  attr_accessor :id, :input_entries, :output_entries, :description
6
6
 
7
7
  def self.from_json(json)
8
- input_entries = Array.wrap(json[:input_entry]).map { |input_entry| UnaryTests.from_json(input_entry) }
9
- output_entries = Array.wrap(json[:output_entry]).map { |output_entry| LiteralExpression.from_json(output_entry) }
8
+ input_entries = Array.wrap(json[:input_entry]).map { |input_entry| FEEL::UnaryTests.from_json(input_entry) }
9
+ output_entries = Array.wrap(json[:output_entry]).map { |output_entry| FEEL::LiteralExpression.from_json(output_entry) }
10
10
  Rule.new(id: json[:id], input_entries:, output_entries:, description: json[:description])
11
11
  end
12
12
 
@@ -48,7 +48,7 @@ module DMN
48
48
  private
49
49
 
50
50
  def nested_hash_value(hash, key_string, value)
51
- keys = key_string.split('.')
51
+ keys = key_string.split(".")
52
52
  current = hash
53
53
  keys[0...-1].each do |key|
54
54
  current[key] ||= {}
data/lib/dmn/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DMN
4
- VERSION = '0.0.2'
4
+ VERSION = "0.0.4"
5
5
  end
data/lib/dmn.rb CHANGED
@@ -2,25 +2,17 @@
2
2
 
3
3
  require_relative "dmn/version"
4
4
 
5
- require "awesome_print"
6
-
7
5
  require "active_support"
8
- require "active_support/duration"
9
6
  require "active_support/time"
10
7
  require "active_support/core_ext/hash"
11
- require "active_support/core_ext/object/json"
12
- require "active_support/configurable"
13
8
 
14
- require "treetop"
9
+ require "feel"
10
+
15
11
  require "xmlhasher"
16
12
 
17
13
  require "dmn/configuration"
18
- require "dmn/nodes"
19
- require "dmn/parser"
20
14
 
21
15
  require "dmn/variable"
22
- require "dmn/literal_expression"
23
- require "dmn/unary_tests"
24
16
  require "dmn/input"
25
17
  require "dmn/output"
26
18
  require "dmn/rule"
@@ -35,13 +27,13 @@ module DMN
35
27
  class EvaluationError < StandardError; end
36
28
 
37
29
  def self.evaluate(expression_text, variables: {})
38
- literal_expression = DMN::LiteralExpression.new(text: expression_text)
30
+ literal_expression = FEEL::LiteralExpression.new(text: expression_text)
39
31
  raise SyntaxError, "Expression is not valid" unless literal_expression.valid?
40
32
  literal_expression.evaluate(variables)
41
33
  end
42
34
 
43
35
  def self.test(input, unary_tests_text, variables: {})
44
- unary_tests = DMN::UnaryTests.new(text: unary_tests_text)
36
+ unary_tests = FEEL::UnaryTests.new(text: unary_tests_text)
45
37
  raise SyntaxError, "Unary tests are not valid" unless unary_tests.valid?
46
38
  unary_tests.test(input, variables)
47
39
  end
metadata CHANGED
@@ -1,15 +1,28 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dmn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Connected Bits
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-12-31 00:00:00.000000000 Z
10
+ date: 2025-04-02 00:00:00.000000000 Z
12
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: feel
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: 0.0.4
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: 0.0.4
13
26
  - !ruby/object:Gem::Dependency
14
27
  name: activemodel
15
28
  requirement: !ruby/object:Gem::Requirement
@@ -39,33 +52,33 @@ dependencies:
39
52
  - !ruby/object:Gem::Version
40
53
  version: 7.0.2.3
41
54
  - !ruby/object:Gem::Dependency
42
- name: awesome_print
55
+ name: ostruct
43
56
  requirement: !ruby/object:Gem::Requirement
44
57
  requirements:
45
- - - "~>"
58
+ - - ">="
46
59
  - !ruby/object:Gem::Version
47
- version: '1.9'
60
+ version: '0'
48
61
  type: :runtime
49
62
  prerelease: false
50
63
  version_requirements: !ruby/object:Gem::Requirement
51
64
  requirements:
52
- - - "~>"
65
+ - - ">="
53
66
  - !ruby/object:Gem::Version
54
- version: '1.9'
67
+ version: '0'
55
68
  - !ruby/object:Gem::Dependency
56
69
  name: treetop
57
70
  requirement: !ruby/object:Gem::Requirement
58
71
  requirements:
59
- - - ">="
72
+ - - '='
60
73
  - !ruby/object:Gem::Version
61
- version: '0'
74
+ version: 1.6.12
62
75
  type: :runtime
63
76
  prerelease: false
64
77
  version_requirements: !ruby/object:Gem::Requirement
65
78
  requirements:
66
- - - ">="
79
+ - - '='
67
80
  - !ruby/object:Gem::Version
68
- version: '0'
81
+ version: 1.6.12
69
82
  - !ruby/object:Gem::Dependency
70
83
  name: xmlhasher
71
84
  requirement: !ruby/object:Gem::Requirement
@@ -276,15 +289,10 @@ files:
276
289
  - lib/dmn/decision.rb
277
290
  - lib/dmn/decision_table.rb
278
291
  - lib/dmn/definitions.rb
279
- - lib/dmn/dmn.treetop
280
292
  - lib/dmn/information_requirement.rb
281
293
  - lib/dmn/input.rb
282
- - lib/dmn/literal_expression.rb
283
- - lib/dmn/nodes.rb
284
294
  - lib/dmn/output.rb
285
- - lib/dmn/parser.rb
286
295
  - lib/dmn/rule.rb
287
- - lib/dmn/unary_tests.rb
288
296
  - lib/dmn/variable.rb
289
297
  - lib/dmn/version.rb
290
298
  homepage: https://www.connectedbits.com
@@ -293,7 +301,6 @@ licenses:
293
301
  metadata:
294
302
  homepage_uri: https://www.connectedbits.com
295
303
  source_code_uri: https://github.com/connectedbits/bpmn/feel
296
- post_install_message:
297
304
  rdoc_options: []
298
305
  require_paths:
299
306
  - lib
@@ -308,8 +315,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
308
315
  - !ruby/object:Gem::Version
309
316
  version: '0'
310
317
  requirements: []
311
- rubygems_version: 3.4.19
312
- signing_key:
318
+ rubygems_version: 3.6.5
313
319
  specification_version: 4
314
320
  summary: A light-weight DMN FEEL expression evaluator and business rule engine in
315
321
  Ruby.