brule 0.3.0 → 0.3.1
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 +4 -4
- data/README.md +7 -7
- data/lib/brule.rb +4 -94
- data/lib/brule/context.rb +22 -0
- data/lib/brule/engine.rb +36 -0
- data/lib/brule/rule.rb +40 -0
- data/lib/brule/utils.rb +5 -0
- data/lib/brule/utils/either.rb +35 -0
- metadata +6 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 67b697b560f27e984a2ec669e672dee5b1cab777071c88d1b0ea7f1d358e4067
|
4
|
+
data.tar.gz: 3d9c7e21ca6fecc1a8b3d1b9120e15894dfc7c7e5ecd055d24fb118fb9ee11e2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b7b7e87db8e466cda35e0330774818f03a75ce5d925f4dcdc394e0bdb1751a5037141bf4490cd602434d3ee56caee16526b40b3fb3fb9d8ddf4e19224a029032
|
7
|
+
data.tar.gz: 21d8166b5a3aac5cc63ef8cd1414bd2defc05eab1548d85697081d1c3d678ffc42cf9ac960399a61308db6fb99fa60e7e60610a4490ea25d1b33aa8dad13b649
|
data/README.md
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
This project aims at providing a set of tools to build
|
2
|
-
business
|
1
|
+
This project aims at providing a set of tools to build complex and evolving
|
2
|
+
business rules. It helps when:
|
3
3
|
|
4
|
-
* You decided to split the
|
5
|
-
* You need to keep
|
6
|
-
* You want to persist the rules
|
4
|
+
* You decided to split the computation into smaller parts working together.
|
5
|
+
* You need to keep old versions of the rules working.
|
6
|
+
* You want to persist the rules leading to a result.
|
7
7
|
* You want to make your tests independent from your production data.
|
8
|
-
* You want to introspect
|
8
|
+
* You want to introspect the computation of a result.
|
9
9
|
|
10
10
|
## How does it work
|
11
11
|
|
@@ -50,7 +50,7 @@ module Pricing
|
|
50
50
|
end
|
51
51
|
|
52
52
|
class OrderTotal < Brule::Rule
|
53
|
-
|
53
|
+
context_reader :unit_price, :item_count
|
54
54
|
context_writer :price
|
55
55
|
|
56
56
|
def apply
|
data/lib/brule.rb
CHANGED
@@ -1,98 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'forwardable'
|
4
|
-
|
5
3
|
module Brule
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
def context_writer(*symbols)
|
14
|
-
symbols.each do |symbol|
|
15
|
-
define_method("#{symbol}=") { |value| context[symbol] = value }
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def context_accessor(*symbols)
|
20
|
-
context_reader(*symbols)
|
21
|
-
context_writer(*symbols)
|
22
|
-
end
|
23
|
-
|
24
|
-
def config_reader(*symbols)
|
25
|
-
symbols.each do |symbol|
|
26
|
-
define_method(symbol) { config.fetch(symbol) }
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
Rule = Struct.new(:config) do
|
32
|
-
extend RuleHelpers
|
33
|
-
|
34
|
-
attr_accessor :context
|
35
|
-
|
36
|
-
def trigger?
|
37
|
-
true
|
38
|
-
end
|
39
|
-
|
40
|
-
def apply
|
41
|
-
raise NotImplementedError
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
class Context
|
46
|
-
extend Forwardable
|
47
|
-
|
48
|
-
def_delegators :@content, :[], :fetch, :fetch_values, :key?, :[]=, :merge!
|
49
|
-
|
50
|
-
def self.wrap(context)
|
51
|
-
context.is_a?(self) ? context : new(context)
|
52
|
-
end
|
53
|
-
|
54
|
-
def initialize(hash = {})
|
55
|
-
@content = hash
|
56
|
-
end
|
57
|
-
|
58
|
-
def initialize_copy(orig)
|
59
|
-
super
|
60
|
-
@content = orig.instance_variable_get(:@content).dup
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
class Engine
|
65
|
-
attr_reader :context
|
66
|
-
|
67
|
-
def initialize(rules:)
|
68
|
-
@rules = rules
|
69
|
-
@history = {}
|
70
|
-
end
|
71
|
-
|
72
|
-
def call(context = {})
|
73
|
-
@history = {}
|
74
|
-
@context = Context.wrap(context)
|
75
|
-
snapshot!(tag: :initial)
|
76
|
-
@rules.each { |rule| apply(rule) }
|
77
|
-
result
|
78
|
-
end
|
79
|
-
|
80
|
-
def history(key:)
|
81
|
-
@history.map { |tag, content| [tag, content.fetch(key, nil)] }
|
82
|
-
end
|
83
|
-
|
84
|
-
private
|
85
|
-
|
86
|
-
def snapshot!(tag:)
|
87
|
-
@history[tag] = @context.dup
|
88
|
-
end
|
89
|
-
|
90
|
-
def apply(rule)
|
91
|
-
rule.context = @context
|
92
|
-
return unless rule.trigger?
|
93
|
-
|
94
|
-
rule.apply
|
95
|
-
snapshot!(tag: rule)
|
96
|
-
end
|
97
|
-
end
|
4
|
+
autoload :Context, 'brule/context'
|
5
|
+
autoload :Engine, 'brule/engine'
|
6
|
+
autoload :Rule, 'brule/rule'
|
7
|
+
autoload :Utils, 'brule/utils'
|
98
8
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Brule
|
4
|
+
class Context
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
def_delegators :@content, :[], :fetch, :fetch_values, :key?, :[]=, :merge!
|
8
|
+
|
9
|
+
def initialize(hash = {})
|
10
|
+
@content = hash
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize_copy(orig)
|
14
|
+
super
|
15
|
+
@content = orig.instance_variable_get(:@content).dup
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.wrap(context)
|
19
|
+
context.is_a?(self) ? context : new(context)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/brule/engine.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module Brule
|
2
|
+
class Engine
|
3
|
+
attr_reader :context
|
4
|
+
|
5
|
+
def initialize(rules:)
|
6
|
+
@rules = rules
|
7
|
+
@history = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(context = {})
|
11
|
+
@history = {}
|
12
|
+
@context = Context.wrap(context)
|
13
|
+
snapshot!(tag: :initial)
|
14
|
+
@rules.each { |rule| apply(rule) }
|
15
|
+
result
|
16
|
+
end
|
17
|
+
|
18
|
+
def history(key:)
|
19
|
+
@history.map { |tag, content| [tag, content.fetch(key, nil)] }
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def snapshot!(tag:)
|
25
|
+
@history[tag] = @context.dup
|
26
|
+
end
|
27
|
+
|
28
|
+
def apply(rule)
|
29
|
+
rule.context = @context
|
30
|
+
return unless rule.trigger?
|
31
|
+
|
32
|
+
rule.apply
|
33
|
+
snapshot!(tag: rule.to_tag)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/brule/rule.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module Brule
|
2
|
+
Rule = Struct.new(:config) do
|
3
|
+
attr_accessor :context
|
4
|
+
|
5
|
+
def trigger?
|
6
|
+
true
|
7
|
+
end
|
8
|
+
|
9
|
+
def apply
|
10
|
+
raise NotImplementedError
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_tag
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.context_reader(*symbols)
|
18
|
+
symbols.each do |symbol|
|
19
|
+
define_method(symbol) { context.fetch(symbol) }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.context_writer(*symbols)
|
24
|
+
symbols.each do |symbol|
|
25
|
+
define_method("#{symbol}=") { |value| context[symbol] = value }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.context_accessor(*symbols)
|
30
|
+
context_reader(*symbols)
|
31
|
+
context_writer(*symbols)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.config_reader(*symbols)
|
35
|
+
symbols.each do |symbol|
|
36
|
+
define_method(symbol) { config.fetch(symbol) }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/brule/utils.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Brule
|
4
|
+
module Utils
|
5
|
+
class Either < Brule::Rule
|
6
|
+
TooManyMatches = Class.new(StandardError)
|
7
|
+
NoMatchFound = Class.new(StandardError)
|
8
|
+
|
9
|
+
extend Forwardable
|
10
|
+
|
11
|
+
def_delegators :only_match, :apply, :trigger?, :to_tag
|
12
|
+
|
13
|
+
config_reader :rules
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def only_match
|
18
|
+
matches = rules.select do |rule|
|
19
|
+
rule.context = context
|
20
|
+
rule.trigger?
|
21
|
+
end
|
22
|
+
|
23
|
+
if matches.size >= 2
|
24
|
+
raise TooManyMatches, "Rules #{matches.join(', ')} are all a match"
|
25
|
+
end
|
26
|
+
|
27
|
+
if matches.empty?
|
28
|
+
raise NoMatchFound, "No rules from #{rules.join(', ')} is a match"
|
29
|
+
end
|
30
|
+
|
31
|
+
matches.first
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: brule
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nicolas Zermati
|
@@ -46,6 +46,11 @@ extra_rdoc_files: []
|
|
46
46
|
files:
|
47
47
|
- README.md
|
48
48
|
- lib/brule.rb
|
49
|
+
- lib/brule/context.rb
|
50
|
+
- lib/brule/engine.rb
|
51
|
+
- lib/brule/rule.rb
|
52
|
+
- lib/brule/utils.rb
|
53
|
+
- lib/brule/utils/either.rb
|
49
54
|
homepage: https://github.com/nicoolas25/brule-rb
|
50
55
|
licenses:
|
51
56
|
- MIT
|