Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/dry/logic/operations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
require 'dry/logic/operations/key'
require 'dry/logic/operations/attr'
require 'dry/logic/operations/each'
require 'dry/logic/operations/part'
require 'dry/logic/operations/set'
require 'dry/logic/operations/check'

Expand Down
17 changes: 17 additions & 0 deletions lib/dry/logic/operations/multiple.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
require 'dry/logic/operations/abstract'

module Dry
module Logic
module Operations
class Multiple < Abstract
def ast(input = Undefined)
[type, rules.map { |rule| rule.ast(input) }]
end

def to_s
"#{type}(#{rules.map(&:to_s).join(', ')})"
end
end
end
end
end
29 changes: 29 additions & 0 deletions lib/dry/logic/operations/part.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
require 'dry/logic/operations/multiple'
require 'dry/logic/result'

module Dry
module Logic
module Operations
class Part < Multiple
def type
:part
end

def call(input)
results = rules.map { |rule| rule.(input) }
success = results.any?(&:success?)

return Result::SUCCESS if success

Result.new(success, id) do
[type, results.map { |failure| failure.to_ast }]
end
end

def [](input)
rules.map { |rule| rule[input] }.any?
end
end
end
end
end
12 changes: 2 additions & 10 deletions lib/dry/logic/operations/set.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
require 'dry/logic/operations/abstract'
require 'dry/logic/operations/multiple'
require 'dry/logic/result'

module Dry
module Logic
module Operations
class Set < Abstract
class Set < Multiple
def type
:set
end
Expand All @@ -21,14 +21,6 @@ def call(input)
def [](input)
rules.map { |rule| rule[input] }.all?
end

def ast(input = Undefined)
[type, rules.map { |rule| rule.ast(input) }]
end

def to_s
"#{type}(#{rules.map(&:to_s).join(', ')})"
end
end
end
end
Expand Down
8 changes: 8 additions & 0 deletions lib/dry/logic/result.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@ def visit_not(node)
def visit_hint(node)
visit(node)
end

def visit_set(nodes)
"set(#{nodes.map { |node| visit(node) }.join(', ')})"
end

def visit_part(nodes)
"part(#{nodes.map { |node| visit(node) }.join(', ')})"
end
end
end
end
4 changes: 4 additions & 0 deletions lib/dry/logic/rule_compiler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ def visit_attr(node)
Operations::Attr.new(visit(predicate), name: name)
end

def visit_part(node)
Operations::Part.new(*call(node))
end

def visit_set(node)
Operations::Set.new(*call(node))
end
Expand Down
2 changes: 2 additions & 0 deletions spec/shared/predicates.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
let(:case?) { Dry::Logic::Predicates[:case?] }

let(:equal?) { Dry::Logic::Predicates[:equal?] }

let(:odd?) { Dry::Logic::Predicates[:odd?] }
end

RSpec.shared_examples 'a passing predicate' do
Expand Down
42 changes: 42 additions & 0 deletions spec/unit/operations/part_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
RSpec.describe Operations::Part do
subject(:operation) { Operations::Part.new(is_odd, gt_18) }

include_context 'predicates'

let(:is_odd) { Rule::Predicate.new(odd?) }
let(:gt_18) { Rule::Predicate.new(gt?, args: [18]) }

describe '#call' do
it 'applies at least one of its rules to the input' do
expect(operation.(20)).to be_success
expect(operation.(17)).to be_success
expect(operation.(16)).to be_failure
end
end

describe '#to_ast' do
it 'returns ast' do
expect(operation.to_ast).to eql(
[:part, [[:predicate, [:odd?, [[:input, Undefined]]]], [:predicate, [:gt?, [[:num, 18], [:input, Undefined]]]]]]
)
end

it 'returns result ast' do
expect(operation.(16).to_ast).to eql(
[:part, [[:predicate, [:odd?, [[:input, 16]]]], [:predicate, [:gt?, [[:num, 18], [:input, 16]]]]]]
)
end

it 'returns result ast with an :id' do
expect(operation.with(id: :age).(16).to_ast).to eql(
[:failure, [:age, [:part, [[:predicate, [:odd?, [[:input, 16]]]], [:predicate, [:gt?, [[:num, 18], [:input, 16]]]]]]]]
)
end
end

describe '#to_s' do
it 'returns string representation' do
expect(operation.to_s).to eql('part(odd?, gt?(18))')
end
end
end