rulezilla 0.1.5 → 0.4.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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