rudelo 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/.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