rulezilla 0.1.5 → 0.4.0.pre.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.
@@ -1,40 +1,25 @@
1
- step 'the gherkin is:' do |gherkin|
2
- @rule_klass_name = 'DummyRule'
3
- @rule_klass = Rulezilla::RuleBuilder.new(@rule_klass_name, gherkin).build
4
- end
5
-
6
- step 'the incorrect gherkin is:' do |gherkin|
7
- @gherkin = gherkin
8
- end
9
-
10
- step 'it raises exception :exception' do |exception|
11
- @record ||= {}
12
-
13
- result = result == 'nil' ? nil : result
14
-
15
- expect{ @rule_klass.apply(@record) }.to raise_error{ RuntimeError.new(exception) }
16
- end
1
+ # frozen_string_literal: true
17
2
 
18
3
  step 'the rule class name is :klass_name' do |klass_name|
19
4
  @rule_klass_name = klass_name
20
5
  end
21
6
 
22
7
  step 'the rule is:' do |rules|
23
- @rule_klass = Object.const_set(@rule_klass_name || "DummyRule", Class.new)
24
- @rule_klass.class_eval('include Rulezilla::DSL')
8
+ @rule_klass = Object.const_set(@rule_klass_name || 'DummyRule', Class.new)
9
+ @rule_klass.class_eval('include Rulezilla::DSL', __FILE__, __LINE__)
25
10
  @rule_klass.class_eval(rules.to_s)
26
11
  end
27
12
 
28
13
  step 'our rule is:' do |rules|
29
- @rule_klass = Object.const_set("DummyRule", Class.new)
30
- @rule_klass.class_eval('include Rulezilla::DSL')
14
+ @rule_klass = Object.const_set('DummyRule', Class.new)
15
+ @rule_klass.class_eval('include Rulezilla::DSL', __FILE__, __LINE__)
31
16
  @rule_klass.class_eval(rules.to_s)
32
17
  end
33
18
 
34
19
  step 'there is a rule called :rule_name:' do |rule_name, rules|
35
20
  send 'the rule class name is :klass_name', rule_name
36
- @rule_klass = Object.const_set(@rule_klass_name || "DummyRule", Class.new)
37
- @rule_klass.class_eval('include Rulezilla::DSL')
21
+ @rule_klass = Object.const_set(@rule_klass_name || 'DummyRule', Class.new)
22
+ @rule_klass.class_eval('include Rulezilla::DSL', __FILE__, __LINE__)
38
23
  @rule_klass.class_eval(rules.to_s)
39
24
  end
40
25
 
@@ -47,15 +32,15 @@ end
47
32
  step 'the record has attribute :method and returns :value' do |method, value|
48
33
  @record ||= {}
49
34
  value = case value
50
- when 'true'
51
- true
52
- when 'false'
53
- false
54
- when 'nil'
55
- nil
56
- else
57
- value
58
- end
35
+ when 'true'
36
+ true
37
+ when 'false'
38
+ false
39
+ when 'nil'
40
+ nil
41
+ else
42
+ value
43
+ end
59
44
  @record[method] = value
60
45
  end
61
46
 
@@ -71,15 +56,15 @@ step 'the result is :result' do |result|
71
56
  @record ||= {}
72
57
 
73
58
  result = case result
74
- when 'true'
75
- true
76
- when 'false'
77
- false
78
- when 'nil'
79
- nil
80
- else
81
- result
82
- end
59
+ when 'true'
60
+ true
61
+ when 'false'
62
+ false
63
+ when 'nil'
64
+ nil
65
+ else
66
+ result
67
+ end
83
68
 
84
69
  expect(@rule_klass.apply(@record)).to eq result
85
70
  end
@@ -90,26 +75,23 @@ step 'all the outcomes are :outcomes' do |outcomes|
90
75
  end
91
76
 
92
77
  step 'all the matching outcomes are :outcomes' do |outcomes|
93
- outcomes = outcomes.split(',').map(&:strip).map {|o| o == 'true' ? true : (o == 'false' ? false : o)}
78
+ outcomes = outcomes.split(',').map(&:strip).map do |o|
79
+ if o == 'true'
80
+ true
81
+ else
82
+ (o == 'false' ? false : o)
83
+ end
84
+ end
94
85
  expect(@rule_klass.all(@record)).to match_array outcomes
95
86
  end
96
87
 
97
- step ':does_or_does_not raise the exception :exception' do |does_or_does_not, exception|
88
+ step ':does_or_does_not raise the exception :exception' do |does_or_does_not, exception_message|
98
89
  if does_or_does_not == 'does'
99
- expect{ @rule_klass.apply @record }.to raise_error do |exception|
100
- expect(exception.message).to match /#{exception}/
90
+ expect { @rule_klass.apply @record }.to raise_error do |exception|
91
+ expect(exception.message).to match(/#{exception_message}/)
101
92
  end
102
93
  else
103
- expect{ @rule_klass.apply @record }.not_to raise_error
104
- end
105
- end
106
-
107
- step 'it raises exception :exception' do |exception|
108
- expect {
109
- @rule_klass_name = 'DummyRule'
110
- @rule_klass = Rulezilla::RuleBuilder.new(@rule_klass_name, @gherkin).build
111
- }.to raise_error do |exception|
112
- expect(exception.message).to match /#{exception}/
94
+ expect { @rule_klass.apply @record }.not_to raise_error
113
95
  end
114
96
  end
115
97
 
data/spec/spec_helper.rb CHANGED
@@ -1,21 +1,41 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rulezilla'
2
4
  require 'pry'
3
5
 
4
- Dir.glob("spec/features/step_definitions/**/*steps.rb") { |f| load f, true }
5
-
6
- Rulezilla.gherkin_rules_path = File.join(Dir.pwd, 'spec/features/gherkin_rules')
6
+ Dir.glob('spec/features/step_definitions/**/*steps.rb') { |f| load f, true }
7
7
 
8
8
  RSpec.configure do |config|
9
9
  config.after(:each) do
10
- Object.send(:remove_const, ('DummyRule').to_sym) rescue NameError
11
- Object.send(:remove_const, 'DummyRuleRecord'.to_sym) rescue NameError
12
- Object.send(:remove_const, 'DummyRuleSupport'.to_sym) rescue NameError
13
- Object.send(:remove_const, (@rule_klass_name).to_sym) rescue NameError
14
- Object.send(:remove_const, "#{@rule_klass_name}Record".to_sym) rescue NameError
15
- Object.send(:remove_const, "#{@support_name}Support".to_sym) rescue NameError
16
-
17
- Rulezilla.send(:remove_const, (@rule_klass_name || 'DummyRule').to_sym) rescue NameError
18
- Rulezilla.send(:remove_const, "#{(@rule_klass_name || 'DummyRule')}Record".to_sym) rescue NameError
19
- Rulezilla.send(:remove_const, "#{(@support_name || 'DummyRule')}Support".to_sym) rescue NameError
10
+ begin
11
+ Object.send(:remove_const, 'DummyRule'.to_sym)
12
+ rescue StandardError
13
+ NameError
14
+ end
15
+ begin
16
+ Object.send(:remove_const, 'DummyRuleRecord'.to_sym)
17
+ rescue StandardError
18
+ NameError
19
+ end
20
+ begin
21
+ Object.send(:remove_const, 'DummyRuleSupport'.to_sym)
22
+ rescue StandardError
23
+ NameError
24
+ end
25
+ begin
26
+ Object.send(:remove_const, @rule_klass_name.to_sym)
27
+ rescue StandardError
28
+ NameError
29
+ end
30
+ begin
31
+ Object.send(:remove_const, "#{@rule_klass_name}Record".to_sym)
32
+ rescue StandardError
33
+ NameError
34
+ end
35
+ begin
36
+ Object.send(:remove_const, "#{@support_name}Support".to_sym)
37
+ rescue StandardError
38
+ NameError
39
+ end
20
40
  end
21
41
  end
metadata CHANGED
@@ -1,37 +1,37 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rulezilla
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.4.0.pre.1
5
5
  platform: ruby
6
6
  authors:
7
- - Peter Wu
7
+ - Simply Business
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-17 00:00:00.000000000 Z
11
+ date: 2022-07-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: gherkin
14
+ name: rspec
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '2.5'
19
+ version: '0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '2.5'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rspec
28
+ name: pry
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
- type: :runtime
34
+ type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
@@ -39,13 +39,13 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: turnip
42
+ name: pry-doc
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
- type: :runtime
48
+ type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
@@ -53,7 +53,7 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: pry
56
+ name: rubocop
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
@@ -67,7 +67,7 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: pry-doc
70
+ name: turnip
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
@@ -82,31 +82,35 @@ dependencies:
82
82
  version: '0'
83
83
  description: Rules DSL
84
84
  email:
85
- - peter.wu@simplybusiness.com
85
+ - tech@simplybusiness.co.uk
86
86
  executables: []
87
87
  extensions: []
88
88
  extra_rdoc_files: []
89
89
  files:
90
+ - ".github/workflows/ci.yaml"
91
+ - ".github/workflows/codeql-analysis.yaml"
92
+ - ".github/workflows/dobby-actions.yml"
93
+ - ".github/workflows/gem-publish.yaml"
94
+ - ".github/workflows/version-forget-me-not.yaml"
90
95
  - ".gitignore"
91
96
  - ".rspec"
97
+ - ".rubocop.yml"
98
+ - ".rubocop_todo.yml"
99
+ - ".ruby-version"
100
+ - CODEOWNERS
92
101
  - Gemfile
102
+ - LICENSE.txt
93
103
  - README.md
104
+ - catalog-info.yaml
94
105
  - lib/rulezilla.rb
95
106
  - lib/rulezilla/basic_support.rb
96
107
  - lib/rulezilla/dsl.rb
97
108
  - lib/rulezilla/node.rb
98
- - lib/rulezilla/rule_builder.rb
99
- - lib/rulezilla/rule_builder/gherkin_to_condition_rule.rb
100
- - lib/rulezilla/rule_builder/gherkin_to_result_rule.rb
101
109
  - lib/rulezilla/tree.rb
102
110
  - lib/rulezilla/version.rb
103
111
  - rulezilla.gemspec
104
112
  - spec/features/default_support_methods.feature
105
- - spec/features/gherkin_dsl_framework.feature
106
- - spec/features/gherkin_rules/animal_rule.feature
107
- - spec/features/gherkin_rules/duration_rule.feature
108
113
  - spec/features/rulezilla_dsl_framework.feature
109
- - spec/features/step_definitions/rule_steps.rb
110
114
  - spec/features/step_definitions/rulezilla_dsl_framework_steps.rb
111
115
  - spec/spec_helper.rb
112
116
  homepage: https://github.com/simplybusiness/rulezilla
@@ -124,14 +128,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
124
128
  version: '0'
125
129
  required_rubygems_version: !ruby/object:Gem::Requirement
126
130
  requirements:
127
- - - ">="
131
+ - - ">"
128
132
  - !ruby/object:Gem::Version
129
- version: '0'
133
+ version: 1.3.1
130
134
  requirements: []
131
- rubyforge_project:
132
- rubygems_version: 2.0.14
135
+ rubygems_version: 3.3.7
133
136
  signing_key:
134
137
  specification_version: 4
135
138
  summary: Rules DSL
136
139
  test_files: []
137
- has_rdoc:
@@ -1,63 +0,0 @@
1
- module Rulezilla
2
- class RuleBuilder
3
- class DefaultCondition; end
4
-
5
- class GherkinToConditionRule
6
- include Rulezilla::DSL
7
-
8
- define :'this is a "field"' do
9
- condition { name =~ /^this is\s?a?n? \"(.*)\"$/i }
10
-
11
- result do
12
- field = name.scan(/^this is\s?a?n? \"(.*)\"$/i).flatten.first
13
- "#{field}?"
14
- end
15
- end
16
-
17
- define :'the "field" is "value"' do
18
- condition { name =~ /^the \"(.*)\" is \"(.*)\"$/i }
19
-
20
- result do
21
- field, value = name.scan(/^the \"(.*)\" is \"(.*)\"$/i).flatten
22
- field = field.gsub(/\s/, '_').downcase
23
-
24
- "#{field} == #{ConditionValueEvaluateRule.apply(value: value)}"
25
- end
26
- end
27
-
28
- define :'the "field" is in: {table}' do
29
- condition { name =~ /^the \"(.*)\" is in:$/i }
30
-
31
- result do
32
- field = name.scan(/^the \"(.*)\" is in:$/i).flatten.first
33
- field = field.gsub(/\s/, '_').downcase
34
-
35
- values = rows.map{ |row| "#{ConditionValueEvaluateRule.apply(value: row['cells'].first)}" }.join(', ')
36
-
37
- "[#{values}].include?(#{field})"
38
- end
39
- end
40
-
41
- define :'none of the above' do
42
- condition { name == 'none of the above' }
43
- result(DefaultCondition)
44
- end
45
-
46
- default { raise "Condition steps is not recognised: #{name}" }
47
- end
48
-
49
-
50
-
51
- class ConditionValueEvaluateRule
52
- include Rulezilla::DSL
53
-
54
- define :blank do
55
- condition { value == 'blank'}
56
- result("''")
57
- end
58
-
59
- default { "\"#{value}\"" }
60
- end
61
- end
62
-
63
- end
@@ -1,49 +0,0 @@
1
- module Rulezilla
2
- class RuleBuilder
3
- class GherkinToResultRule
4
- include Rulezilla::DSL
5
-
6
- group :keyword_is do
7
- condition { name =~ /^the #{step_keyword} is/i }
8
-
9
- define :value_is do
10
- condition { name =~ /^the #{step_keyword} is \"(.*)\"$/i }
11
- result { "\"#{name.scan(/^the #{step_keyword} is \"(.*)\"$/i).flatten.first}\"" }
12
- end
13
-
14
- define :duration_is do
15
- condition { name =~ /^the #{step_keyword} is \"(\d+)\" (days?|hours?|minutes?|seconds?)$/i }
16
- result do
17
- quantity, unit = name.scan(/^the #{step_keyword} is \"(\d+)\" (days?|hours?|minutes?|seconds?)$/i).flatten
18
-
19
- multiplier = case unit
20
- when /day/
21
- 86400
22
- when /hour/
23
- 3600
24
- when /minute/
25
- 60
26
- when /second/
27
- 1
28
- end
29
-
30
- quantity.to_i * multiplier
31
- end
32
- end
33
- end
34
-
35
- define :start_with_this_is_a do
36
- condition { name =~ /^this is an? #{step_keyword}$/i }
37
- result("true")
38
- end
39
-
40
- define :start_with_this_is_not_a do
41
- condition { name =~ /^this is not an? #{step_keyword}$/i }
42
- result("false")
43
- end
44
-
45
- default { raise "Unrecognisable step: #{name}" }
46
-
47
- end
48
- end
49
- end
@@ -1,101 +0,0 @@
1
- require 'gherkin/parser/parser'
2
- require 'gherkin/formatter/json_formatter'
3
- require 'stringio'
4
- require 'json'
5
- require 'rulezilla/rule_builder/gherkin_to_result_rule'
6
- require 'rulezilla/rule_builder/gherkin_to_condition_rule'
7
-
8
- module Rulezilla
9
- class RuleBuilder
10
-
11
- def self.from_file(name, file)
12
- new(name, IO.read(file))
13
- end
14
-
15
- attr_reader :name, :content
16
-
17
- def initialize(name, content)
18
- @name = name
19
- @content = content
20
- end
21
-
22
- def build
23
- klass_definition = rules.map do |rule|
24
- rule = RuleDefinition.new(rule, step_keyword)
25
-
26
- condition_definition = rule.conditions.empty? ? "" : "condition { #{rule.conditions} }"
27
-
28
- """
29
- define \"#{rule.name}\" do
30
- #{condition_definition}
31
- result(#{rule.result})
32
- end
33
- """
34
- end.join("\n")
35
-
36
- klass = Rulezilla.const_set(name, Class.new)
37
-
38
- klass.class_eval('include Rulezilla::DSL')
39
- klass.class_eval(klass_definition)
40
-
41
- klass
42
- end
43
-
44
-
45
- private
46
-
47
- def step_keyword
48
- gherkin_json.first['name'].gsub(/\s?rule/i, '')
49
- end
50
-
51
- def rules
52
- gherkin_json.first['elements']
53
- end
54
-
55
- def gherkin_json
56
- @gherkin_json ||= begin
57
- io = StringIO.new
58
- formatter = Gherkin::Formatter::JSONFormatter.new(io)
59
- parser = Gherkin::Parser::Parser.new(formatter)
60
-
61
- parser.parse(content, content, 0)
62
- formatter.done
63
-
64
- JSON.parse(io.string)
65
- end
66
- end
67
-
68
-
69
- class RuleDefinition
70
- def initialize(gherkin_json, step_keyword)
71
- @gherkin_json = gherkin_json
72
- @step_keyword = step_keyword
73
- end
74
-
75
- def name
76
- @gherkin_json['name']
77
- end
78
-
79
- def conditions
80
- condition_steps = steps.reject{|step| step['keyword'].strip.downcase == 'then'}
81
- conditions = condition_steps.map do |step|
82
- ::Rulezilla::RuleBuilder::GherkinToConditionRule.apply(record(step))
83
- end.reject{|condition| condition == Rulezilla::RuleBuilder::DefaultCondition}.join(' && ')
84
- end
85
-
86
- def result
87
- ::Rulezilla::RuleBuilder::GherkinToResultRule.apply record(steps.detect{|step| step['keyword'].strip.downcase == 'then'})
88
- end
89
-
90
- private
91
-
92
- def steps
93
- @steps ||= @gherkin_json['steps']
94
- end
95
-
96
- def record(step)
97
- step.merge(step_keyword: @step_keyword)
98
- end
99
- end
100
- end
101
- end
@@ -1,90 +0,0 @@
1
- Feature: Rulezilla Gherkin DSL
2
-
3
- Scenario: Condition: something is something
4
- Given the gherkin is:
5
- """
6
- Feature: Awesomeness Rule
7
-
8
- Scenario: Robocop
9
- When the "target" is "Robocop"
10
- Then the awesomeness is "very awesome"
11
- """
12
- When the record has attribute "target" and returns "Robocop"
13
- Then the result is "very awesome"
14
-
15
-
16
- Scenario: Multiple Condition: something is something
17
- Given the gherkin is:
18
- """
19
- Feature: Winner Rule
20
-
21
- Scenario: Robocop vs Ironman
22
- When the "target" is "Robocop"
23
- And the "opponent" is "Ironman"
24
- Then the winner is "Ironman"
25
- """
26
- When the record has attribute "target" and returns "Robocop"
27
- And the record has attribute "opponent" and returns "Ironman"
28
- Then the result is "Ironman"
29
-
30
-
31
- Scenario: Default
32
- Given the gherkin is:
33
- """
34
- Feature: Winner Rule
35
-
36
- Scenario: Default
37
- When none of the above
38
- Then the winner is "Ironman"
39
- """
40
- Then the result is "Ironman"
41
-
42
-
43
- Scenario: 'The :keyword is :value', Keyword mismatch
44
- Given the incorrect gherkin is:
45
- """
46
- Feature: Winner Rule
47
-
48
- Scenario: Default
49
- When none of the above
50
- Then the loser is "Hello kitty"
51
- """
52
- Then it raises exception "Unrecognisable step: the loser is 'Hello kitty'"
53
-
54
-
55
- Scenario: True value
56
- Given the gherkin is:
57
- """
58
- Feature: Dummy Rule
59
-
60
- Scenario: Dummy
61
- When the "name" is "007"
62
- Then this is a dummy
63
- """
64
- When the record has attribute "name" and returns "007"
65
- Then the result is "true"
66
-
67
-
68
- Scenario: False value
69
- Given the gherkin is:
70
- """
71
- Feature: Dummy Rule
72
-
73
- Scenario: Not Dummy
74
- When the "name" is "Tom"
75
- Then this is not a dummy
76
- """
77
- When the record has attribute "name" and returns "Tom"
78
- Then the result is "false"
79
-
80
-
81
- Scenario: 'This is( not) a :keyword', Keyword mismatch
82
- Given the incorrect gherkin is:
83
- """
84
- Feature: Winner Rule
85
-
86
- Scenario: Not Dummy
87
- When the "name" is "Tom"
88
- Then this is not a cat
89
- """
90
- Then it raises exception "Unrecognisable step: this is not a 'cat'"
@@ -1,16 +0,0 @@
1
- @rule_steps
2
- Feature: Animal Rule
3
-
4
- Scenario: entity is a cat
5
- When this is a "cat"
6
- Then this is an animal
7
-
8
- Scenario: telephone number is dog or bird
9
- When the "entity" is in:
10
- | dog |
11
- | bird |
12
- Then this is an animal
13
-
14
- Scenario: default
15
- When none of the above
16
- Then this is not an animal
@@ -1,14 +0,0 @@
1
- @rule_steps
2
- Feature: Duration Rule
3
-
4
- Scenario: Maths class
5
- When the "name of the class" is "Maths"
6
- Then the duration is "1" minute
7
-
8
- Scenario: Science class
9
- When the "name of the class" is "Science"
10
- Then the duration is "10" hours
11
-
12
- Scenario: PE
13
- When the "name of the class" is "PE"
14
- Then the duration is "2" days