rulezilla 0.1.4 → 0.1.5
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/.gitignore +4 -0
- data/Gemfile +0 -4
- data/README.md +48 -24
- data/lib/rulezilla/node.rb +7 -11
- data/lib/rulezilla/tree.rb +3 -1
- data/lib/rulezilla/version.rb +1 -1
- data/rulezilla.gemspec +5 -1
- data/spec/features/{rulezilla_dsl_framwork.feature → rulezilla_dsl_framework.feature} +50 -3
- data/spec/features/step_definitions/rulezilla_dsl_framework_steps.rb +1 -1
- metadata +59 -4
- data/Gemfile.lock +0 -44
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8de468c7423c8fb95e879491361d8587ced03b28
|
4
|
+
data.tar.gz: 9cbbd613deab120d67a6da93e81a4e7fc360ec38
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 52adab9a3346e9cfb20b58a7197c2a724f823f04d647f2dc313f07cb01ed169c59c5a10c708fe2581a6c7914da33e88a3641f04b02ce337f8d2207dad181df51
|
7
|
+
data.tar.gz: 9fdf3bb8bf2b3366fec2e7d140ade30020113f8e3ef80bdd198720139152e6b3c81a3b816a2c292dcc315e01ed0b26a96687beccfbe25cb848569db8d3d47965
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,43 +1,61 @@
|
|
1
|
-
[](https://semaphoreci.com/simplybusiness/rulezilla)
|
2
2
|
[](https://codeclimate.com/repos/53ecc0416956800c1d01f6bf/feed)
|
3
|
-
|
3
|
+
[](http://badge.fury.io/rb/rulezilla)
|
4
4
|
|
5
5
|
rulezilla
|
6
6
|
=========
|
7
7
|
|
8
|
-
This
|
8
|
+
This provides a DSL to implement rules for various tasks. In the current version we are still relying on user to have a certain level of Ruby knowledge
|
9
|
+
in order to be able to use this DSL. The ultimate goal is for people without prior Ruby knowledge to be able to change and even write the Rule.
|
9
10
|
|
10
11
|
|
11
12
|
# Installation
|
12
13
|
|
13
|
-
|
14
|
+
## Using `Gemfile`
|
15
|
+
|
16
|
+
Add the following line to your `Gemfile`:
|
17
|
+
|
18
|
+
gem 'rulezilla'
|
19
|
+
|
20
|
+
Then run:
|
21
|
+
|
22
|
+
bundle install
|
23
|
+
|
24
|
+
## Without `Gemfile`
|
14
25
|
|
15
|
-
|
26
|
+
On your command line run the following:
|
27
|
+
|
28
|
+
gem install 'rulezilla'
|
29
|
+
|
30
|
+
## Usage
|
16
31
|
|
17
32
|
### Rules
|
18
33
|
|
34
|
+
Rules can be defined either using `Gherkin` or pure Ruby. In either case, rules are classes that include the `Rulezilla::DSL`.
|
35
|
+
|
19
36
|
#### Gherkin (Beta)
|
20
37
|
|
21
|
-
rulezilla Gherkin has only very limited support
|
38
|
+
> *Note:* Currently, rulezilla Gherkin has only very limited support.
|
22
39
|
|
23
|
-
|
40
|
+
Rules are defined inside `.feature` files which should be organized under a specific directory. In order to be able to use these rules, you need to first
|
41
|
+
set the path that rulezilla can use in order to load them.
|
24
42
|
|
25
|
-
Rulezilla.gherkin_rules_path = 'absolute path'
|
43
|
+
Rulezilla.gherkin_rules_path = 'absolute path to folder holding your feature files'
|
26
44
|
|
27
|
-
The filename will
|
45
|
+
Rulezilla will load all the feature files and for each one will create a rule class. The filename will be used to build the name of the rule class. For example,
|
46
|
+
the file with name `invalid_number_rule.feature` will generate rule class `Rulezilla::InvalidNumberRule`.
|
28
47
|
|
29
|
-
We currently
|
48
|
+
We currently support a very limited type of steps. Please refer to:
|
30
49
|
|
31
50
|
[True / False](spec/features/gherkin_rules/animal_rule.feature)
|
32
51
|
|
33
52
|
[Duration](spec/features/gherkin_rules/duration_rule.feature)
|
34
53
|
|
35
|
-
|
36
54
|
#### Ruby
|
37
55
|
|
38
|
-
|
56
|
+
You can use plain Ruby to define the rule classes. But you will need to include the `Rulezilla::DSL` module. That will give you access to the DSL used to define rules.
|
39
57
|
|
40
|
-
|
58
|
+
Here is an example:
|
41
59
|
|
42
60
|
class RoboticsRule
|
43
61
|
include Rulezilla::DSL
|
@@ -59,6 +77,8 @@ To use rulezilla, please include `Rulezilla::DSL` in your class:
|
|
59
77
|
|
60
78
|
end
|
61
79
|
|
80
|
+
Please refer to the [feature](spec/features/rulezilla_dsl_framework.feature) for further details of the DSL.
|
81
|
+
|
62
82
|
### Support Module
|
63
83
|
|
64
84
|
The support module will be automatically included if its name is `"#{rule_class_name}Support"`
|
@@ -73,24 +93,28 @@ e.g. if the rule class name is `RoboticsRule`, then the support would be `Roboti
|
|
73
93
|
|
74
94
|
### How to execute the rule
|
75
95
|
|
76
|
-
|
96
|
+
If the entity is:
|
77
97
|
|
78
|
-
{
|
79
|
-
not_injure_human?:
|
80
|
-
do_as_human_told?:
|
81
|
-
in_danger?:
|
98
|
+
entity = {
|
99
|
+
not_injure_human?: true,
|
100
|
+
do_as_human_told?: true,
|
101
|
+
in_danger?: true,
|
82
102
|
not_letting_itself_be_detroyed?: true
|
83
103
|
}
|
84
104
|
|
85
|
-
#### To get the first matching result
|
105
|
+
#### To get the first matching result output
|
86
106
|
|
87
107
|
RoboticsRule.apply(entity) #=> true
|
88
108
|
|
89
|
-
#### To get all matching
|
109
|
+
#### To get all matching result outputs
|
90
110
|
|
91
|
-
RoboticsRule.all(entity) #=> [true]
|
111
|
+
RoboticsRule.all(entity) #=> [true, false]
|
112
|
+
|
113
|
+
Note that `false` is the result outcome coming out from `default(false)` on top level, which is also called `root node`. The `root` node does not have any condition and hence
|
114
|
+
it is considered to be matching. This means, by consequence, that its result (`default(false)`) is included in the list of matching result outputs which `#all(entity)` above
|
115
|
+
returns.
|
92
116
|
|
93
|
-
#### To get the trace of all
|
117
|
+
#### To get the trace of all nodes
|
94
118
|
|
95
119
|
RoboticsRule.trace(entity)
|
96
120
|
#=> all the nodes instance: [root, may_not_injure_human, obey_human, protect_its_own_existence] in sequence order.
|
@@ -100,10 +124,10 @@ if the entity is:
|
|
100
124
|
RoboticsRule.results #=> [true, false]
|
101
125
|
|
102
126
|
|
103
|
-
|
127
|
+
### Syntax
|
104
128
|
|
105
129
|
Please refer to the features for DSL syntax:
|
106
130
|
|
107
|
-
[DSL Feature](spec/features/
|
131
|
+
[DSL Feature](spec/features/rulezilla_dsl_framework.feature),
|
108
132
|
|
109
133
|
[Default Support Methods Feature](spec/features/default_support_methods.feature)
|
data/lib/rulezilla/node.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
module Rulezilla
|
2
2
|
class Node
|
3
|
-
attr_accessor :
|
4
|
-
|
3
|
+
attr_accessor :children,
|
4
|
+
:condition,
|
5
|
+
:parent
|
6
|
+
|
7
|
+
attr_reader :default, :name
|
8
|
+
attr_writer :result
|
5
9
|
|
6
10
|
def initialize
|
7
11
|
@children = []
|
@@ -17,25 +21,17 @@ module Rulezilla
|
|
17
21
|
|
18
22
|
def applies?(record)
|
19
23
|
return true if condition.nil?
|
20
|
-
record.instance_eval(&condition)
|
24
|
+
!!record.instance_eval(&condition)
|
21
25
|
end
|
22
26
|
|
23
27
|
def result(record)
|
24
28
|
@result.is_a?(Proc) ? record.instance_eval(&@result) : @result
|
25
29
|
end
|
26
30
|
|
27
|
-
def condition=(block)
|
28
|
-
@condition = block
|
29
|
-
end
|
30
|
-
|
31
31
|
def name=(value)
|
32
32
|
@name = value.to_s
|
33
33
|
end
|
34
34
|
|
35
|
-
def result=(block)
|
36
|
-
@result = block
|
37
|
-
end
|
38
|
-
|
39
35
|
def add_child(node)
|
40
36
|
node.parent = self
|
41
37
|
children << node
|
data/lib/rulezilla/tree.rb
CHANGED
@@ -12,6 +12,8 @@ module Rulezilla
|
|
12
12
|
@current_node = is_root? ? @root_node : @current_node.parent
|
13
13
|
end
|
14
14
|
|
15
|
+
# Returns all the result outcomes of all the matching nodes.
|
16
|
+
#
|
15
17
|
def find_all(record, node=@root_node)
|
16
18
|
array = []
|
17
19
|
if node.applies?(record)
|
@@ -21,7 +23,7 @@ module Rulezilla
|
|
21
23
|
|
22
24
|
return node.has_result? ? array + [node] : array
|
23
25
|
end
|
24
|
-
|
26
|
+
array
|
25
27
|
end
|
26
28
|
|
27
29
|
def trace(record, node=@root_node)
|
data/lib/rulezilla/version.rb
CHANGED
data/rulezilla.gemspec
CHANGED
@@ -14,5 +14,9 @@ Gem::Specification.new do |gem|
|
|
14
14
|
gem.version = Rulezilla::VERSION
|
15
15
|
gem.license = 'MIT'
|
16
16
|
|
17
|
-
gem.add_runtime_dependency('gherkin')
|
17
|
+
gem.add_runtime_dependency('gherkin', '~> 2.5')
|
18
|
+
gem.add_runtime_dependency('rspec')
|
19
|
+
gem.add_runtime_dependency('turnip')
|
20
|
+
gem.add_development_dependency('pry')
|
21
|
+
gem.add_development_dependency('pry-doc')
|
18
22
|
end
|
@@ -85,7 +85,7 @@ Scenario: If nothing is matched in a group, it will fall to default value of the
|
|
85
85
|
Then the result is "It is alright"
|
86
86
|
|
87
87
|
|
88
|
-
Scenario: If nothing is matched, and no default is
|
88
|
+
Scenario: If nothing is matched, and no default is defined in the group, it will fall to the next default
|
89
89
|
Given the rule is:
|
90
90
|
"""
|
91
91
|
group :group_1 do
|
@@ -126,7 +126,7 @@ Scenario: If nothing is matched, it will continue to evaluate the next group
|
|
126
126
|
Then the result is "Bad"
|
127
127
|
|
128
128
|
|
129
|
-
Scenario Outline: It
|
129
|
+
Scenario Outline: It evaluates the rule against a record
|
130
130
|
Given the rule is:
|
131
131
|
"""
|
132
132
|
define :fruit do
|
@@ -185,6 +185,53 @@ Scenario: To get all matching outcomes from a rule
|
|
185
185
|
"""
|
186
186
|
Then all the matching outcomes are "B, C, D, F"
|
187
187
|
|
188
|
+
Scenario: To get all matching outcomes from a rule with root node default result
|
189
|
+
Given the rule is:
|
190
|
+
"""
|
191
|
+
group :may_not_injure_human do
|
192
|
+
condition { not_injure_human? }
|
193
|
+
|
194
|
+
group :obey_human do
|
195
|
+
condition { do_as_human_told? }
|
196
|
+
result(true)
|
197
|
+
|
198
|
+
define :protect_its_own_existence do
|
199
|
+
condition { in_danger? && not_letting_itself_be_detroyed? }
|
200
|
+
result(true)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
default(false)
|
206
|
+
"""
|
207
|
+
When the record has attribute "not_injure_human?" and returns "true"
|
208
|
+
When the record has attribute "do_as_human_told?" and returns "true"
|
209
|
+
When the record has attribute "in_dangert?" and returns "true"
|
210
|
+
When the record has attribute "not_letting_itself_be_detroyed?" and returns "true"
|
211
|
+
Then all the matching outcomes are "true, false"
|
212
|
+
|
213
|
+
Scenario: To get all matching outcomes from a rule without root node default result
|
214
|
+
Given the rule is:
|
215
|
+
"""
|
216
|
+
group :may_not_injure_human do
|
217
|
+
condition { not_injure_human? }
|
218
|
+
|
219
|
+
group :obey_human do
|
220
|
+
condition { do_as_human_told? }
|
221
|
+
result(true)
|
222
|
+
|
223
|
+
define :protect_its_own_existence do
|
224
|
+
condition { in_danger? && not_letting_itself_be_detroyed? }
|
225
|
+
result(true)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
"""
|
230
|
+
When the record has attribute "not_injure_human?" and returns "true"
|
231
|
+
When the record has attribute "do_as_human_told?" and returns "true"
|
232
|
+
When the record has attribute "in_dangert?" and returns "true"
|
233
|
+
When the record has attribute "not_letting_itself_be_detroyed?" and returns "true"
|
234
|
+
Then all the matching outcomes are "true"
|
188
235
|
|
189
236
|
Scenario: Support Module
|
190
237
|
Given the rule class name is "FruitRule"
|
@@ -227,7 +274,7 @@ Scenario Outline: Validate the presence of attributes
|
|
227
274
|
| apple, orange | does not | |
|
228
275
|
|
229
276
|
|
230
|
-
Scenario: Rule return nil if no rule is defined
|
277
|
+
Scenario: Rule return nil if no rule is defined
|
231
278
|
Given the rule is:
|
232
279
|
"""
|
233
280
|
"""
|
@@ -90,7 +90,7 @@ step 'all the outcomes are :outcomes' do |outcomes|
|
|
90
90
|
end
|
91
91
|
|
92
92
|
step 'all the matching outcomes are :outcomes' do |outcomes|
|
93
|
-
outcomes = outcomes.split(',').map(&:strip)
|
93
|
+
outcomes = outcomes.split(',').map(&:strip).map {|o| o == 'true' ? true : (o == 'false' ? false : o)}
|
94
94
|
expect(@rule_klass.all(@record)).to match_array outcomes
|
95
95
|
end
|
96
96
|
|
metadata
CHANGED
@@ -1,17 +1,31 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rulezilla
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter Wu
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-02-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: gherkin
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.5'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
16
30
|
requirements:
|
17
31
|
- - ">="
|
@@ -24,6 +38,48 @@ dependencies:
|
|
24
38
|
- - ">="
|
25
39
|
- !ruby/object:Gem::Version
|
26
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: turnip
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry-doc
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
27
83
|
description: Rules DSL
|
28
84
|
email:
|
29
85
|
- peter.wu@simplybusiness.com
|
@@ -34,7 +90,6 @@ files:
|
|
34
90
|
- ".gitignore"
|
35
91
|
- ".rspec"
|
36
92
|
- Gemfile
|
37
|
-
- Gemfile.lock
|
38
93
|
- README.md
|
39
94
|
- lib/rulezilla.rb
|
40
95
|
- lib/rulezilla/basic_support.rb
|
@@ -50,7 +105,7 @@ files:
|
|
50
105
|
- spec/features/gherkin_dsl_framework.feature
|
51
106
|
- spec/features/gherkin_rules/animal_rule.feature
|
52
107
|
- spec/features/gherkin_rules/duration_rule.feature
|
53
|
-
- spec/features/
|
108
|
+
- spec/features/rulezilla_dsl_framework.feature
|
54
109
|
- spec/features/step_definitions/rule_steps.rb
|
55
110
|
- spec/features/step_definitions/rulezilla_dsl_framework_steps.rb
|
56
111
|
- spec/spec_helper.rb
|
data/Gemfile.lock
DELETED
@@ -1,44 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
rulezilla (0.1.4)
|
5
|
-
gherkin
|
6
|
-
|
7
|
-
GEM
|
8
|
-
remote: https://rubygems.org/
|
9
|
-
specs:
|
10
|
-
coderay (1.1.0)
|
11
|
-
diff-lcs (1.2.5)
|
12
|
-
gherkin (2.12.2)
|
13
|
-
multi_json (~> 1.3)
|
14
|
-
method_source (0.8.2)
|
15
|
-
multi_json (1.10.1)
|
16
|
-
pry (0.9.12.6)
|
17
|
-
coderay (~> 1.0)
|
18
|
-
method_source (~> 0.8)
|
19
|
-
slop (~> 3.4)
|
20
|
-
rspec (3.0.0)
|
21
|
-
rspec-core (~> 3.0.0)
|
22
|
-
rspec-expectations (~> 3.0.0)
|
23
|
-
rspec-mocks (~> 3.0.0)
|
24
|
-
rspec-core (3.0.3)
|
25
|
-
rspec-support (~> 3.0.0)
|
26
|
-
rspec-expectations (3.0.3)
|
27
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
28
|
-
rspec-support (~> 3.0.0)
|
29
|
-
rspec-mocks (3.0.3)
|
30
|
-
rspec-support (~> 3.0.0)
|
31
|
-
rspec-support (3.0.3)
|
32
|
-
slop (3.5.0)
|
33
|
-
turnip (1.2.2)
|
34
|
-
gherkin (>= 2.5)
|
35
|
-
rspec (>= 2.0, < 4.0)
|
36
|
-
|
37
|
-
PLATFORMS
|
38
|
-
ruby
|
39
|
-
|
40
|
-
DEPENDENCIES
|
41
|
-
pry
|
42
|
-
rspec
|
43
|
-
rulezilla!
|
44
|
-
turnip
|