rudelo 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rudelo.gemspec
4
+ gemspec
5
+
6
+ gem 'rufus-decision', git: 'https://github.com/jmettraux/rufus-decision.git'
7
+ group :development do
8
+ gem "guard-rspec"
9
+ end
data/Guardfile ADDED
@@ -0,0 +1,5 @@
1
+ guard 'rspec' do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Michael Johnston
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,60 @@
1
+ # Rudelo
2
+
3
+ Provides Rufus::Decision::Matchers::SetLogic, for use with [rufus-decision][1]
4
+
5
+ ## Installation
6
+
7
+ In Gemfile:
8
+
9
+ # until 1.4.0 is published
10
+ gem 'rufus-decision', git: 'https://github.com/jmettraux/rufus-decision.git'
11
+
12
+ gem 'rudelo', git: "git://github.com/lastobelus/rudelo.git"
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ ## Usage
19
+
20
+ TABLE = Rufus::Decision::Table.new(%{
21
+ in:group, out:situation
22
+ $(bob jeff mary alice ralph) & $in #= 2, company
23
+ $(bob jeff mary alice ralph) & $in same-as $in #= 3, crowd
24
+ $(bob jeff mary alice ralph) >= $in #> 3, exclusive-party
25
+ $(bob jeff mary alice ralph) & $in < $in #> 5, PARTY!
26
+ $(bob jeff mary alice ralph) & $in < $in #> 3, party
27
+ })
28
+ TABLE.matchers.unshift Rudelo::Matchers::SetLogic.new
29
+
30
+ table.transform({'group' => "bob alice"})
31
+ #=> {'group' => "bob alice", 'situation' => "company"}
32
+
33
+ table.transform({'group' => "bob alice jeff"})
34
+ #=> {'group' => "bob alice jeff", 'situation' => "crowd"})
35
+
36
+ table.transform({'group' => "bob alice jeff ralph"})
37
+ #=> {'group' => "bob alice jeff ralph", 'situation' => "exclusive-party"}
38
+
39
+ table.transform({'group' => "bob alice jeff don"})
40
+ #=> {'group' => "bob alice jeff don", 'situation' => "party"}
41
+
42
+ table.transform({'group' => "bob alice jeff mary ralph don"})
43
+ #=> {'group' => "bob alice jeff mary ralph don", 'situation' => "PARTY!"}
44
+
45
+ table.transform({'group' => "bob alice jeff mary don bev"})
46
+ #=> {'group' => "bob alice jeff mary don bev", 'situation' => "PARTY!"}
47
+
48
+
49
+
50
+ ## Contributing
51
+
52
+ 1. Fork it
53
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
54
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
55
+ 4. Push to the branch (`git push origin my-new-feature`)
56
+ 5. Create new Pull Request
57
+
58
+ [1]: https://github.com/jmettraux/rufus-decision
59
+ [2]: https://github.com/lastobelus/rufus-decision/tree/pluggable_matchers
60
+ [3]: https://github.com/lastobelus/rufus-decision
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,100 @@
1
+ # Set Logic Matcher
2
+
3
+ The SetLogic matcher allows a decision table cell to match based on set logic between the decision table and the corresponding entry in the hash being transformed.
4
+
5
+
6
+ ## Set Conversion on input hash
7
+
8
+ When a decision table cell contains a set expression, the corresponding value in the input is first converted to a set according to the following rules:
9
+
10
+ $(bob, mary) => Set["bob", "mary"]
11
+ $(bob mary, jeff) => Set["bob mary", "jeff"]
12
+ $('bob rob', jeff) => Set["bob rob", "jeff"]
13
+ ${r: ruby_code} => eval ruby code, ignoring unless it returns a set
14
+ ${other_column} => apply set conversion to other_column
15
+ $(${c1}, ${c2}) => Set["c1 contents", "c2 contents"]
16
+ bob, mary => Set["bob", "mary"]
17
+ bob mary => Set["bob mary"]
18
+ 'bob, mary', jeff => Set["bob, mary", "jeff"]
19
+
20
+ ## Decision Table Cell Syntax
21
+
22
+ 1. The SetLogic matcher applies to any cell matching the regex:
23
+
24
+ ```ruby
25
+ /^\$\((.*)\)/
26
+ ```
27
+
28
+ ### Set Values
29
+
30
+ set logic expressions can contain Sets expressed in the following ways:
31
+
32
+ $in => the hash element with this column name, with set conversion applied
33
+ $(${c1}) => the hash element named c1, with set conversion applied
34
+ $(${in:c1}) => the row element named in:c1, assumed to be Decision Table Cell Syntax
35
+ $(${out:c1}) => the row element named out:c1, with set conversion applied
36
+ $() => empty set
37
+ $(bob, mary) => Set["bob", "mary"]
38
+ $(bob mary, jeff) => Set["bob mary", "jeff"]
39
+ $('bob rob', jeff) => Set["bob rob", "jeff"]
40
+ $(*) => The universal set
41
+ $(r: ruby_code) => eval ruby code, ignoring unless it returns a set
42
+
43
+ ### Set Expressions
44
+ Set expressions can use the following operators:
45
+
46
+ #### Cardinality Operators
47
+
48
+ #=
49
+ cardinality-equals => size of set ==
50
+ #<
51
+ cardinality-less-than => size of set is less than X
52
+ #>
53
+ cardinality-greater-than => cardinality >
54
+
55
+ #### Set Construction Operators
56
+
57
+ &|intersection => intersection of sets
58
+ +|union => union of sets
59
+ -|difference => difference of sets
60
+ ^|exclusive => (set1 + set2) - (set1 & set2)
61
+
62
+ #### Set Logic Operators
63
+
64
+ <|proper_subset => proper_subset of sets
65
+ >|proper_superset => proper_superset of sets
66
+ <=|subset => subset of sets
67
+ >=|superset => superset of sets
68
+ =|same-as => equivalent sets
69
+
70
+ special case: if the cell contains only a set, it is equivalent to
71
+ $in <= $(cell)
72
+
73
+ #### Set Expression Rules
74
+
75
+ * set expressions are evaluated left to right. There is no grouping; multiple columns in the decision table can be used to accomplish precedence
76
+ * cardinality operators must always be the last operator if present
77
+ * a cell can contain only one cardinality operator; use multiple cells for logic
78
+ * set construction operators may not follow set logic operators
79
+ * when a cardinality operator follows a logic operator, the sense is (set expression) && (cardinality expression applied to rightmost set in set expression).
80
+ * when a cardinality operator follows a (series of) set construction operator(s) it applies to the final constructed set
81
+ * if a cell contains a cardinality operator it matches the input if the cardinality expression is true
82
+ * if a cell contains a set logic operator it matches the input if the set logic expression is true
83
+ * if a cell contains set construction operators only it matches the input if the set expression results in a non-empty set
84
+ * if a cell contains no operators it matches the input if it is a superset of the corresponding input value, ie, it is equivalent to
85
+
86
+ $in <= $(cell)
87
+
88
+ ## Examples
89
+
90
+ $(bob jeff mary) & $in #= 2
91
+ => does not match (bob, jeff, mary)
92
+ => matches (bob, mary) or (jeff, mary) etc.
93
+ => does not match (bob) or (jeff) or (mary)
94
+
95
+ $(bob, jeff, mary)
96
+ => matches (bob)
97
+ => matches (jeff, mary)
98
+ => matches (bob, jeff, mary)
99
+ => does not match (ralph, bob)
100
+
@@ -0,0 +1,68 @@
1
+ #--
2
+ # Copyright (c) 2013, Michael Johnston, lastobelus@gmail.com
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #
22
+ #++
23
+
24
+ require 'rufus/decision/matcher'
25
+ require 'rudelo/parsers/set_logic_parser'
26
+ require 'rudelo/parsers/set_logic_transform'
27
+
28
+ module Rudelo
29
+ module Matchers
30
+ class SetLogic < Rufus::Decision::Matcher
31
+
32
+
33
+ def matches?(cell, value)
34
+ in_set = value_transform.apply(value_parser.parse(value))
35
+ ast(cell).eval(in_set)
36
+ end
37
+
38
+ def cell_substitution?
39
+ true
40
+ end
41
+
42
+ def asts
43
+ @asts ||= {}
44
+ end
45
+
46
+ def logic_parser
47
+ @logic_parser ||= Rudelo::Parsers::SetLogicParser.new
48
+ end
49
+
50
+ def logic_transform
51
+ @logic_transform ||= Rudelo::Parsers::SetLogicTransform.new
52
+ end
53
+
54
+ def value_parser
55
+ @value_parser ||= Rudelo::Parsers::SetValueParser.new
56
+ end
57
+
58
+ def value_transform
59
+ @value_transform ||= Rudelo::Parsers::SetLogicTransform.new
60
+ end
61
+
62
+ def ast(cell)
63
+ asts[cell] ||= logic_transform.apply(logic_parser.parse(cell))
64
+ end
65
+
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,88 @@
1
+ require 'parslet'
2
+ require 'rudelo/parsers/set_value_parser'
3
+
4
+ module Rudelo
5
+ module Parsers
6
+
7
+ module Atom
8
+ include Parslet
9
+ rule(:digit) { match("[0-9]") }
10
+
11
+ rule(:integer) do
12
+ str("-").maybe >> match("[1-9]") >> digit.repeat
13
+ end
14
+ end
15
+
16
+ class SetLogicParser < Parslet::Parser
17
+ include Space
18
+ include Set
19
+ include Atom
20
+
21
+ rule(:in_set) { str("$in").as(:in_set)}
22
+ rule(:set ) { explicit_set | in_set }
23
+
24
+ ######## Cardinality Expressions ########
25
+ rule(:cardinality_eq) { str('#=') | str('cardinality-equals') }
26
+ rule(:cardinality_gt) { str('#>') | str('cardinality-greater-than') }
27
+ rule(:cardinality_lt) { str('#<') | str('cardinality-less-than') }
28
+ rule(:cardinality_operator) {
29
+ cardinality_eq | cardinality_gt | cardinality_lt
30
+ }
31
+ rule(:cardinality_expression) {(
32
+ cardinality_operator.as(:op) >>
33
+ space? >>
34
+ integer.as(:qty)
35
+ ).as(:cardinality_expression)
36
+ }
37
+
38
+ ######## Set Construction Expressions ########
39
+
40
+ rule(:set_construction_operator){
41
+ spaced_op?('&') | spaced_op('intersection') |
42
+ spaced_op?('+') | spaced_op('union') |
43
+ spaced_op?('-') | spaced_op('difference') |
44
+ spaced_op?('^') | spaced_op('exclusive')
45
+ }
46
+ rule(:set_op){
47
+ (set_construction_operator.as(:left) >> set.as(:right)).as(:set_op)
48
+ }
49
+ rule(:set_construction_expression){
50
+ (set.as(:left) >> set_op.repeat(1).as(:right)).as(:set_construction_expression)
51
+ }
52
+
53
+
54
+ ######## Set Logic Expressions ########
55
+ rule(:set_expression) { set_construction_expression | set }
56
+ rule(:set_logic_operator){
57
+ spaced_op?('<', '=') | spaced_op('proper-subset') |
58
+ spaced_op?('>', '=') | spaced_op('proper-superset') |
59
+ spaced_op?('<=') | spaced_op('subset') |
60
+ spaced_op?('>=') | spaced_op('superset') |
61
+ spaced_op?('=') | spaced_op('same-as')
62
+ }
63
+ rule(:set_logic_expression){
64
+ (set_expression.as(:left) >> set_logic_operator >> set.as(:right)).as(:set_logic_expression)
65
+ }
66
+
67
+
68
+
69
+
70
+ rule(:match_expression) { space? >> (
71
+
72
+ set_logic_expression.as(:left) >>
73
+ (space >> cardinality_expression.as(:right)).maybe |
74
+
75
+ set_construction_expression.as(:left) >>
76
+ (space >> cardinality_expression.as(:right)).maybe |
77
+
78
+ cardinality_expression.as(:right)
79
+
80
+ ).as(:match_expression) |
81
+
82
+ explicit_set.as(:superset_match_expression) >> space? }
83
+
84
+ root(:match_expression)
85
+
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,160 @@
1
+ require 'rudelo/parsers/set_value_transform'
2
+
3
+ class Set
4
+ def eval
5
+ self
6
+ end
7
+ end
8
+
9
+ module Rudelo
10
+ module Parsers
11
+
12
+
13
+ class SetLogicTransform < Rudelo::Parsers::SetValueTransform
14
+
15
+
16
+ attr_accessor :in_set, :set_value
17
+ def initialize(in_set=::Set.new, &block)
18
+ @in_set = in_set.dup # the dup is vital because we are going to be pointery later
19
+ @set_value = SetValueTransform.new
20
+ super()
21
+ end
22
+
23
+ def apply(obj, context=nil)
24
+ super @set_value.apply(obj, context), {in_set: @in_set}
25
+ end
26
+
27
+ SetLogicExpr = Struct.new(:left, :op, :right) {
28
+ def eval
29
+ left.eval.send(op, right)
30
+ end
31
+ def set
32
+ right
33
+ end
34
+ }
35
+
36
+ SetConstructionExpr = Struct.new(:left, :right) {
37
+ def eval
38
+ right.reduce(left){|left, set_op| set_op.eval(left) }
39
+ end
40
+ }
41
+
42
+ SetOp = Struct.new(:op, :right) {
43
+ def eval(set)
44
+ set.send(op, right)
45
+ end
46
+ }
47
+
48
+ CardinalityExpr = Struct.new(:op, :qty) {
49
+ def eval(set)
50
+ set.size.send(op, qty)
51
+ end
52
+ def empty?
53
+ false
54
+ end
55
+ }
56
+
57
+ class EmptyExpr
58
+ def eval(arg=nil)
59
+ true
60
+ end
61
+ def empty?
62
+ true
63
+ end
64
+ end
65
+
66
+ MatchExpr = Struct.new(:left, :right, :in_set) {
67
+ def eval(in_set_override=nil)
68
+ # I've always wondered what these replace methods were for
69
+ # and now I know. To make references more pointery.
70
+ # The purpose of this is so we can construct a transform
71
+ # once and use it with different values for in_set.
72
+ in_set.replace(in_set_override) unless in_set_override.nil?
73
+ lvalue = left.eval
74
+ case lvalue
75
+ when ::Set
76
+ right.empty? ? (lvalue.size > 0) : right.eval(lvalue)
77
+ when ::TrueClass, ::FalseClass
78
+ lvalue && right.eval(left.set)
79
+ else
80
+ nil
81
+ end
82
+ end
83
+ }
84
+
85
+ rule(cardinality_expression: subtree(:expr)){
86
+ CardinalityExpr.new(expr[:op], expr[:qty])
87
+ }
88
+
89
+ def self.translate_op(op)
90
+ case op
91
+ when '#=', 'cardinality-equals',
92
+ 'same-as', '='; :"=="
93
+ when '#<','cardinality-less-than'; :<
94
+ when '<','proper-subset'; :proper_subset?
95
+ when '#>','cardinality-greater-than'; :>
96
+ when '>', 'proper-superset'; :proper_superset?
97
+ when '<=', 'subset'; :subset?
98
+ when '>=', 'superset'; :superset?
99
+ when 'intersection'; :&
100
+ when 'union'; :+
101
+ when 'difference'; :-
102
+ when 'exclusive'; :'^'
103
+ else
104
+ op.to_sym
105
+ end
106
+ end
107
+
108
+ rule(op: simple(:op)){
109
+ SetLogicTransform.translate_op(op)
110
+ }
111
+
112
+ rule(qty: simple(:qty)){ qty.to_i }
113
+
114
+ rule(op: simple(:op), qty: simple(:qty)){
115
+ {op: SetLogicTransform.translate_op(op), qty: qty.to_i}
116
+ }
117
+
118
+ rule(in_set: simple(:x)){ in_set }
119
+
120
+ rule(set_logic_expression: subtree(:expr)){
121
+ SetLogicExpr.new(
122
+ expr[:left],
123
+ SetLogicTransform.translate_op(expr[:op]),
124
+ expr[:right]
125
+ )
126
+ }
127
+
128
+ rule(set_construction_expression: subtree(:expr)){
129
+ SetConstructionExpr.new(
130
+ expr[:left],
131
+ expr[:right]
132
+ )
133
+ }
134
+
135
+ rule(set_op: subtree(:expr)){
136
+ SetOp.new(
137
+ expr[:left],
138
+ expr[:right]
139
+ )
140
+ }
141
+
142
+ rule(match_expression: subtree(:expr)){
143
+ MatchExpr.new(
144
+ expr[:left] || in_set,
145
+ expr[:right] || EmptyExpr.new,
146
+ in_set
147
+ )
148
+ }
149
+
150
+ rule(superset_match_expression: simple(:set)){
151
+ MatchExpr.new(
152
+ SetLogicExpr.new(in_set, :subset?, set),
153
+ EmptyExpr.new,
154
+ in_set
155
+ )
156
+ }
157
+
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,68 @@
1
+ require 'parslet'
2
+
3
+ module Rudelo
4
+ module Parsers
5
+
6
+ module Space
7
+ include Parslet
8
+ rule(:space) { match["\t "].repeat(1) }
9
+ rule(:space?) { space.maybe }
10
+ def spaced_op(s)
11
+ space >> str(s).as(:op) >> space
12
+ end
13
+ def spaced_op?(s, protect=nil)
14
+ if protect
15
+ # this is necessary when some ops are substrings of
16
+ # other ops. if you have '>' and '>=', use
17
+ # spaced_op?('>', '=') for '>'
18
+ space? >> str(protect).absent? >> str(s).as(:op) >> space? >> str(protect).absent?
19
+ else
20
+ space? >> str(s).as(:op) >> space?
21
+ end
22
+ end
23
+ end
24
+
25
+ module Set
26
+ include Parslet
27
+
28
+ rule(:comma) { str(',') }
29
+ rule(:comma?) { str(',').maybe }
30
+ rule(:quote) { match '"' }
31
+ rule(:open_set) { str('$(') }
32
+ rule(:close_set) { str(')') }
33
+
34
+ rule(:unquoted_element) {
35
+ (close_set.absent? >> space.absent? >> comma.absent? >> any).
36
+ repeat(1).as(:element) }
37
+ rule(:quoted_element) {
38
+ (quote >>
39
+ (quote.absent? >> any).repeat.
40
+ as(:element) >>
41
+ quote)}
42
+ rule(:element) { quoted_element | unquoted_element }
43
+ rule(:element_delimiter) { (comma | space) >> space? }
44
+
45
+
46
+ rule(:bare_element_list) {
47
+ (element >> (element_delimiter >> element).repeat).
48
+ as(:element_list)
49
+ }
50
+
51
+ rule(:explicit_set) {
52
+ open_set >> space? >>
53
+ bare_element_list >>
54
+ space? >>
55
+ close_set
56
+ }
57
+ end
58
+
59
+ class SetValueParser < Parslet::Parser
60
+ include Space
61
+ include Set
62
+
63
+ rule(:set_value) { explicit_set | bare_element_list }
64
+ root(:set_value)
65
+
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,10 @@
1
+ module Rudelo
2
+ module Parsers
3
+ class SetValueTransform < Parslet::Transform
4
+ rule(:element => simple(:element)) { element.to_s }
5
+ rule(:element_list => subtree(:element_list)) do
6
+ ::Set[*element_list]
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ module Rudelo
2
+ VERSION = "0.1.0"
3
+ end
data/lib/rudelo.rb ADDED
@@ -0,0 +1,6 @@
1
+ require "rudelo/version"
2
+ require "rudelo/matchers/set_logic"
3
+
4
+ module Rudelo
5
+ # Your code goes here...
6
+ end
data/rudelo.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rudelo/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rudelo"
8
+ spec.version = Rudelo::VERSION
9
+ spec.authors = ["Michael Johnston"]
10
+ spec.email = ["lastobelus@mac.com"]
11
+ spec.description = %q{Set Logic Matcher for rufus-decision}
12
+ spec.summary = %q{Set Logic Matcher for rufus-decision}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "rufus-decision", "~> 1.4"
22
+ spec.add_dependency "parslet"
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "rspec"
26
+ end