claus 0.1.0
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.
- data/README.md +25 -0
- data/lib/claus.rb +89 -0
- data/test/helper.rb +2 -0
- data/test/test_claus.rb +43 -0
- metadata +65 -0
data/README.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Claus
|
2
|
+
|
3
|
+
Simple rule expression using a combination of Array and Hash.
|
4
|
+
|
5
|
+
## Example
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
|
9
|
+
require 'claus'
|
10
|
+
claus = Claus.new(foo: 1, bar: 2)
|
11
|
+
claus.match(foo: 1, bar: 2) #=> true
|
12
|
+
claus.match(foo: 1, bar: 3) #=> false
|
13
|
+
claus.match(foo: 1, bar: 2, baz: 3) #=> true
|
14
|
+
|
15
|
+
claus = Claus.new(foo: 1, bar: 1..2)
|
16
|
+
claus.match(foo: 1, bar: 2) #=> true
|
17
|
+
|
18
|
+
claus = Claus.new([{foo:1}, {bar:2}])
|
19
|
+
claus.match?(foo: 1) #=> true
|
20
|
+
claus.match?(bar: 2) #=> true
|
21
|
+
claus.match?(baz: 3) #=> false
|
22
|
+
```
|
23
|
+
|
24
|
+
# License
|
25
|
+
[Creative Commons Attribution - CC BY](http://creativecommons.org/licenses/by/3.0)
|
data/lib/claus.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
# Santa Claus tells ya if you're naughty or nice.
|
2
|
+
#
|
3
|
+
# @example
|
4
|
+
# claus = Claus.new(foo: 1, bar: 2)
|
5
|
+
# claus.match(foo: 1, bar: 2) #=> true
|
6
|
+
# claus.match(foo: 1, bar: 3) #=> false
|
7
|
+
# claus.match(foo: 1, bar: 2, baz: 3) #=> true
|
8
|
+
#
|
9
|
+
# claus = Claus.new(foo: 1, bar: 1..2)
|
10
|
+
# claus.match(foo: 1, bar: 2) #=> true
|
11
|
+
#
|
12
|
+
# claus = Claus.new([{foo:1}, {bar:2}])
|
13
|
+
# claus.match?(foo: 1) #=> true
|
14
|
+
# claus.match?(bar: 2) #=> true
|
15
|
+
# claus.match?(baz: 3) #=> false
|
16
|
+
#
|
17
|
+
class Claus
|
18
|
+
def initialize expression
|
19
|
+
@ast = compile(expression)
|
20
|
+
end
|
21
|
+
|
22
|
+
def match? value
|
23
|
+
@ast.match?(value)
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
def compile expression
|
28
|
+
case expression
|
29
|
+
when Hash then AST::Hash.new(expression)
|
30
|
+
when Array then AST::List.new(expression)
|
31
|
+
else raise ArgumentError, "invalid expression at #{expression}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
module AST
|
36
|
+
class Node
|
37
|
+
attr_reader :ast
|
38
|
+
def initialize expression
|
39
|
+
@ast = compile(expression)
|
40
|
+
end
|
41
|
+
|
42
|
+
def compile expression
|
43
|
+
expression
|
44
|
+
end
|
45
|
+
|
46
|
+
def match? value
|
47
|
+
ast == value
|
48
|
+
end
|
49
|
+
end # Node
|
50
|
+
|
51
|
+
# TODO: refactor case statements
|
52
|
+
class Hash < Node
|
53
|
+
def compile expression
|
54
|
+
expression.each_with_object({}) do |(k, v), h|
|
55
|
+
case v
|
56
|
+
when ::Hash then h[k] = Hash.new(v)
|
57
|
+
when ::Array then h[k] = List.new(v)
|
58
|
+
when ::Range then h[k] = List.new(v)
|
59
|
+
else h[k] = Node.new(v)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def match? value
|
65
|
+
ast.each do |k, node|
|
66
|
+
return false unless ::Hash === value && node.match?(value[k])
|
67
|
+
end
|
68
|
+
true
|
69
|
+
end
|
70
|
+
end # Hash
|
71
|
+
|
72
|
+
class List < Node
|
73
|
+
def compile expression
|
74
|
+
expression.map do |v|
|
75
|
+
case v
|
76
|
+
when ::Hash then Hash.new(v)
|
77
|
+
when ::Array then List.new(v)
|
78
|
+
when ::Range then List.new(v)
|
79
|
+
else Node.new(v)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def match? value
|
85
|
+
!!ast.find {|node| node.match?(value)}
|
86
|
+
end
|
87
|
+
end # List
|
88
|
+
end # AST
|
89
|
+
end # Claus
|
data/test/helper.rb
ADDED
data/test/test_claus.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe 'claus' do
|
4
|
+
it 'should match simple AND conditions' do
|
5
|
+
assert_equal true, Claus.new(foo: 1).match?(foo: 1)
|
6
|
+
assert_equal false, Claus.new(foo: 1).match?(foo: 2)
|
7
|
+
assert_equal false, Claus.new(foo: 1).match?(bar: 2)
|
8
|
+
assert_equal false, Claus.new(foo: 1).match?(2)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should match multiple AND conditions' do
|
12
|
+
assert_equal true, Claus.new(foo: 1, bar: 2).match?(foo: 1, bar: 2)
|
13
|
+
assert_equal false, Claus.new(foo: 1, bar: 2).match?(foo: 1, bar: 3)
|
14
|
+
assert_equal false, Claus.new(foo: 1, bar: 2).match?(foo: 1)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should match simple OR conditions' do
|
18
|
+
assert_equal true, Claus.new([1, 2]).match?(1)
|
19
|
+
assert_equal true, Claus.new(foo: [1, 2]).match?(foo: 1)
|
20
|
+
assert_equal true, Claus.new(foo: [1, 2]).match?(foo: 2)
|
21
|
+
assert_equal true, Claus.new(foo: 1..2).match?(foo: 1)
|
22
|
+
assert_equal true, Claus.new(foo: 1..2).match?(foo: 2)
|
23
|
+
assert_equal false, Claus.new(foo: [1, 2]).match?(foo: 3)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should match composite AND OR clauses' do
|
27
|
+
assert_equal true, Claus.new(foo: 1, bar: 1..2).match?(foo: 1, bar: 2)
|
28
|
+
assert_equal false, Claus.new(foo: 1, bar: 1..2).match?(foo: 1, bar: 3)
|
29
|
+
assert_equal false, Claus.new(foo: 1, bar: 1..2).match?(foo: 1)
|
30
|
+
assert_equal true, Claus.new([{foo: 1}, {bar: 1..2}]).match?(foo: 1)
|
31
|
+
assert_equal true, Claus.new([{foo: 1}, {bar: 1..2}]).match?(bar: 1)
|
32
|
+
assert_equal false, Claus.new([{foo: 1}, {bar: 1..2}]).match?(foo: 2)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should match some wickedly nested ones too' do
|
36
|
+
claus = Claus.new(foo: 1, bar: [{baz: 2}, {bar: 1..2}])
|
37
|
+
|
38
|
+
assert_equal true, claus.match?(foo: 1, bar: {baz: 2})
|
39
|
+
assert_equal true, claus.match?(foo: 1, bar: {bar: 1})
|
40
|
+
assert_equal false, claus.match?(foo: 1, bar: {baz: 3})
|
41
|
+
assert_equal false, claus.match?(foo: 1)
|
42
|
+
end
|
43
|
+
end
|
metadata
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: claus
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Bharanee Rathna
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-05-18 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
description: simple hash / array based rule matching engine
|
31
|
+
email:
|
32
|
+
- deepfryed@gmail.com
|
33
|
+
executables: []
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
files:
|
37
|
+
- test/helper.rb
|
38
|
+
- test/test_claus.rb
|
39
|
+
- lib/claus.rb
|
40
|
+
- README.md
|
41
|
+
homepage: http://github.com/deepfryed/claus
|
42
|
+
licenses: []
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
none: false
|
49
|
+
requirements:
|
50
|
+
- - ! '>='
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ! '>='
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
requirements: []
|
60
|
+
rubyforge_project:
|
61
|
+
rubygems_version: 1.8.23
|
62
|
+
signing_key:
|
63
|
+
specification_version: 3
|
64
|
+
summary: simple hash / array based rule expression
|
65
|
+
test_files: []
|