Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
schlick committed Dec 11, 2022
2 parents 8503530 + 8a8f362 commit b80ae92
Show file tree
Hide file tree
Showing 14 changed files with 264 additions and 135 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Changelog
All notable changes to this gem will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [1.7.0] - 2022-12-12
### Added
- Array element
- Includes and excludes operators to comparison
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
source "https://rubygems.org"
gemspec

gem "codeclimate-test-reporter", group: :test, require: nil
gem "simplecov", group: :test, require: nil
67 changes: 0 additions & 67 deletions Gemfile.lock

This file was deleted.

22 changes: 19 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,26 @@ Our DSL consists of these components:
'alpha'
'What is your name?'

* **integer**, a sequence of 0-9. Examples:
* **integer**, a sequence of [+-]?[0-9]\*. Examples:

0
-654
458457474

* **decimal**, a sequence of [+-]?[0-9]\*.[0-9]\*. Examples:

0.123
-45.78

* **percentage**, a sequence of [+-]?[0-9]\*(.[0-9])?%\*. Examples:

43.56%
-20%

* **array**, a comma separated list of integer, decimal or string elements wrapped by [] (square brackets)

[1,2,3]

* **attribute** reference, a sequence of [A-z\_][A-z0-9\_]\*[?]. When an attribute reference is evaluated,
the context.send(attribute_reference) is called, and the result is used as the attribute's value.
An attribute may optionally be prefixed by a ! (exclamation point). The result is the logial NOT of the
Expand All @@ -66,16 +81,17 @@ Our DSL consists of these components:
!Apple_Tree
!what_i5_YOUR_name?

* **element** is one of a **string**, **integer**, or **attribute**.
* **element** is one of a **string**, **percentage**, **decimal**, **integer**, **array** or **attribute**.

* **comparison** consists of an **element**, followed by one of ==, !=, <, >, <=, >=, (the operator)
* **comparison** consists of an **element**, followed by one of ==, !=, <, >, <=, >=, includes, excludes (the operator)
followed by another **element**. When a comparison is evaluated, the 2 elements are compared using the operater
supplied. The comparison follows Ruby evaluation rules. The result is used as the result of the comparison. Examples:

1 < 2
'test' == beta
alpha != delta
8 >= 1
[1,2,3] includes 3

* **boolean** expression consists of a left side, followed by one of _and_, _or_ (the operator),
followed by a right side.
Expand Down
4 changes: 2 additions & 2 deletions boolean_dsl.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ Gem::Specification.new do |s|

s.required_rubygems_version = '>= 1.3.6'

s.add_development_dependency 'bundler', '~> 1.0'
s.add_development_dependency 'bundler'
s.add_development_dependency 'rspec', '~> 2.14'
s.add_development_dependency 'factory_girl', '~> 4.4'
s.add_development_dependency 'factory_bot', '~> 4.4'
s.add_development_dependency 'cane', '~> 2.6'
s.add_development_dependency 'byebug', '~> 2.7'
s.add_development_dependency 'rake', '~> 10.1'
Expand Down
12 changes: 12 additions & 0 deletions lib/boolean_dsl/evaluator.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require 'bigdecimal'

class BooleanDsl::Evaluator
attr_reader :parser, :expression, :context

Expand Down Expand Up @@ -28,8 +30,14 @@ def evaluate(tree)
end
elsif tree.key?(:negation)
!evaluate(tree[:negation])
elsif tree.key?(:array)
tree[:array].map { |element| evaluate(element) }
elsif tree.key?(:string)
tree[:string]
elsif tree.key?(:percentage)
BigDecimal(tree[:percentage].to_s.chop) / 100
elsif tree.key?(:decimal)
BigDecimal(tree[:decimal])
elsif tree.key?(:integer)
Integer(tree[:integer], 10)
end
Expand All @@ -55,6 +63,10 @@ def evaluate_comparison(left, operator, right)
left > right
when '>='
left >= right
when 'includes'
left.include?(right)
when 'excludes'
!left.include?(right)
end
end

Expand Down
19 changes: 15 additions & 4 deletions lib/boolean_dsl/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,21 @@ class BooleanDsl::Parser < Parslet::Parser
rule(:space?) { space.maybe }

# Literals
rule(:integer) { match('[0-9]').repeat(1).as(:integer) >> space? }
rule(:sign) { match('[+-]') }
rule(:digits) { match('[0-9]').repeat(1) }
rule(:decimal_fragment) { digits >> str(".") >> digits }

rule(:integer) { (sign.maybe >> digits).as(:integer) >> space? }
rule(:decimal) { (sign.maybe >> decimal_fragment).as(:decimal) >> space? }
rule(:percentage) { (sign.maybe >> (decimal_fragment | digits) >> str("%")).as(:percentage) >> space? }

rule(:string_content) { (str("'").absent? >> any).repeat }
rule(:string) { str("'") >> string_content.as(:string) >> str("'") >> space? }

rule(:array) { str('[') >> space? >> array_list.repeat(0,1).as(:array) >> str(']') >> space? }
rule(:array_list) { array_element >> (str(',') >> space? >> array_element).repeat }
rule(:array_element) { integer | decimal | string }

rule(:attribute) do
(match('[A-Za-z_]') >> match('[A-Za-z_0-9]').repeat >> str('?').maybe).as(:attribute) >> space?
end
Expand All @@ -17,16 +27,17 @@ class BooleanDsl::Parser < Parslet::Parser
rule(:negation) { str('!') >> attribute.as(:negation) }

# Elements
rule(:element) { negation | integer | string | attribute }
rule(:element) { negation | percentage | decimal | integer | string | array | attribute }

# Booleans are rules that will evaluate to a true or false result
rule(:boolean) { value_comparison | negation | attribute }
rule(:boolean_sub) { parens | boolean }

# Operators (Comparison)
rule(:comparison_operator) do
(str('==') | str('!=') | str('<=') | str('>=') | str('<') | str('>')).as(:comparison_operator) >> space?
(str('==') | str('!=') | str('<=') | str('>=') | str('<') | str('>') | str('includes') | str('excludes')).as(:comparison_operator) >> space?
end
rule(:inclusion_comparison) { (array | attribute).as(:left) >> comparison_operator >> element.as(:right) >> space? }
rule(:value_comparison) { element.as(:left) >> comparison_operator >> element.as(:right) >> space? }

# Operators (Boolean)
Expand All @@ -35,7 +46,7 @@ class BooleanDsl::Parser < Parslet::Parser

rule(:parens) { str('(') >> expression.maybe.as(:expression) >> space? >> str(')') >> space? }

rule(:expression) { boolean_comparison | parens | value_comparison | element }
rule(:expression) { boolean_comparison | parens | inclusion_comparison | value_comparison | element }
root(:expression)
end

2 changes: 1 addition & 1 deletion lib/boolean_dsl/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module BooleanDsl
VERSION = '1.4.0'
VERSION = '1.7.0'
end
2 changes: 1 addition & 1 deletion scripts/buildbox.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
set -e

echo '--- setting ruby version'
rbenv local 2.1.2
rbenv local 2.3.6

echo '--- bundling'
bundle install -j $(nproc) --without production --quiet
Expand Down
10 changes: 6 additions & 4 deletions spec/evaluator/context_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
'alpha' => 'omega',
'gamma' => 7,
'delta' => true,
'yotta' => false
'yotta' => false,
'theta' => [1,2,3],
}
end

Expand All @@ -15,8 +16,9 @@
context 'attribute' do
specify { expect(evaluator.evaluate(attribute: 'alpha')).to eq('omega') }
specify { expect(evaluator.evaluate(attribute: 'gamma')).to eq(7) }
specify { expect(evaluator.evaluate(attribute: 'delta')).to be_true }
specify { expect(evaluator.evaluate(attribute: 'yotta')).to be_false }
specify { expect(evaluator.evaluate(attribute: 'delta')).to be_truthy }
specify { expect(evaluator.evaluate(attribute: 'yotta')).to be_falsey }
specify { expect(evaluator.evaluate(attribute: 'theta')).to match_array([1,2,3]) }
specify do
expect { evaluator.evaluate(attribute: 'beta') }.to(
raise_error(BooleanDsl::EvaluationFailed, 'Context does not have key beta'))
Expand Down Expand Up @@ -55,4 +57,4 @@
)
end
end
end
end
Loading

0 comments on commit b80ae92

Please sign in to comment.