decision_table 0.0.1

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,7 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .redcar
6
+ vendor/ruby
7
+ bin
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in decision_table.gemspec
4
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "decision_table/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "decision_table"
7
+ s.version = DecisionTable::VERSION
8
+ s.authors = ["Chris Nelson"]
9
+ s.email = ["superchrisnelson@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{Table driven decision logic in ruby}
12
+ s.description = %q{If you have decision algorithm that can be easily expressed in a table, this gem can help}
13
+
14
+ s.rubyforge_project = "decision_table"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ s.add_development_dependency "rspec"
23
+ s.add_development_dependency "rspec-given"
24
+ s.add_runtime_dependency "activesupport"
25
+ end
@@ -0,0 +1,37 @@
1
+ module DecisionTable
2
+ class Rule
3
+
4
+ attr_accessor :criteria, :result
5
+
6
+ def initialize(criteria, result)
7
+ @criteria = criteria
8
+ @result = result
9
+ end
10
+
11
+ def applies?(candidate)
12
+ criteria.all? do |k, v|
13
+ v.blank? || matches?(candidate, k, v)
14
+ end
15
+ end
16
+
17
+ def matches?(candidate, key, value)
18
+ if value.to_s == "true"
19
+ candidate.send(key)
20
+ elsif value.to_s == "false"
21
+ !candidate.send(key)
22
+ else
23
+ candidate.send(key) == value
24
+ end
25
+ end
26
+
27
+ def self.parse(rule_string)
28
+ criteria = {}
29
+ rule_string.split(",").each do |crit|
30
+ k, v = crit.split(":")
31
+ criteria[k] = v
32
+ end
33
+ Rule.new(criteria, true)
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,18 @@
1
+ module DecisionTable
2
+ class Ruleset
3
+ attr_accessor :rules
4
+ def initialize(rules)
5
+ @rules = rules
6
+ end
7
+
8
+ def evaluate(candidate)
9
+ rule = rules.detect { |rule| rule.applies?(candidate) }
10
+ rule.result if rule
11
+ end
12
+
13
+ def self.parse_csv(csv)
14
+ data = FasterCSV.parse(csv)
15
+ keys = data.shift
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ module DecisionTable
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,8 @@
1
+ require "active_support/core_ext"
2
+ require "decision_table/version"
3
+ require "decision_table/rule"
4
+ require "decision_table/ruleset"
5
+
6
+ module DecisionTable
7
+ # Your code goes here...
8
+ end
data/spec/rule_spec.rb ADDED
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe DecisionTable::Rule do
4
+ Given(:candidate) { double(:candidate, :foo => "bar", :bing => "baz") }
5
+
6
+ describe("applies") do
7
+ context "when all values match" do
8
+ Given(:rule) { DecisionTable::Rule.new({:foo => "bar", :bing => "baz"}, true) }
9
+ Then { rule.applies?(candidate).should be_true}
10
+ end
11
+
12
+ context "when all specified values match" do
13
+ Given(:rule ) { DecisionTable::Rule.new({:foo => "bar", :bing => ""}, true) }
14
+ Then { rule.applies?(candidate).should be_true}
15
+ end
16
+
17
+ context "when values don't match" do
18
+ Given(:rule) { DecisionTable::Rule.new({:foo => "not bar", :bing => "baz"}, true) }
19
+ Then { rule.applies?(candidate).should be_false}
20
+ end
21
+
22
+ end
23
+
24
+ describe("parsing rule") do
25
+ Given(:rule_string) { "foo:bar,bing:baz" }
26
+ When(:rule) { DecisionTable::Rule.parse(rule_string) }
27
+ Then { rule.applies?(candidate).should be_true }
28
+
29
+ context "with boolean rules" do
30
+ Given(:candidate) { double(:candidate, foo: true, bar: false) }
31
+ Given(:non_matching) { double(:candidate, foo: nil, bar: true) }
32
+ Given(:rule) {DecisionTable::Rule.parse("foo:true,bar:false") }
33
+ Then { rule.applies?(candidate).should be_true }
34
+ Then { rule.applies?(non_matching).should be_false }
35
+ end
36
+ end
37
+
38
+ end
data/spec/rules.csv ADDED
@@ -0,0 +1,3 @@
1
+ foo,bar,result
2
+ bork,,false
3
+ bing,baz,true
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ module DecisionTable
4
+ describe Ruleset do
5
+ Given(:candidate) { double(:candidate, foo: "bar", baz: "bing")}
6
+ describe "decide" do
7
+ context "one applicable rule" do
8
+ Given(:ruleset) do
9
+ rule = Rule.new({foo: "bar"}, "result")
10
+ Ruleset.new([rule])
11
+ end
12
+ Then { ruleset.evaluate(candidate).should == "result" }
13
+ end
14
+ context "two applicable rules" do
15
+ Given(:first_rule) { first_rule = Rule.new({baz: "bing"}, "first_result") }
16
+ Given(:second_rule) { first_rule = Rule.new({foo: "bar"}, "second_result") }
17
+ Given(:ruleset) { Ruleset.new [first_rule, second_rule] }
18
+ Then { ruleset.evaluate(candidate).should == "first_result"}
19
+ end
20
+ context "no applicable rules" do
21
+ Given(:rule) { Rule.new({baz: "not bing"}, "first_result") }
22
+ Then { Ruleset.new([rule]).evaluate(candidate).should be_nil }
23
+ end
24
+ end
25
+
26
+ describe "parsing CSV" do
27
+ pending "maybe later"
28
+ # Given(:csv_data) { File.read(File.join(File.dirname(__FILE__), "rules.csv")) }
29
+ # When(:ruleset) { Ruleset.parse_csv(csv_data) }
30
+ # Then { ruleset.rules.size.should == 2}
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'rspec/given'
4
+ require 'decision_table' # and any other gems you need
5
+
6
+ RSpec.configure do |config|
7
+ # some (optional) config here
8
+ end
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: decision_table
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Chris Nelson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-04-13 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &70177089993400 !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: *70177089993400
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec-given
27
+ requirement: &70177089992480 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70177089992480
36
+ - !ruby/object:Gem::Dependency
37
+ name: activesupport
38
+ requirement: &70177089991940 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70177089991940
47
+ description: If you have decision algorithm that can be easily expressed in a table,
48
+ this gem can help
49
+ email:
50
+ - superchrisnelson@gmail.com
51
+ executables: []
52
+ extensions: []
53
+ extra_rdoc_files: []
54
+ files:
55
+ - .gitignore
56
+ - Gemfile
57
+ - Rakefile
58
+ - decision_table.gemspec
59
+ - lib/decision_table.rb
60
+ - lib/decision_table/rule.rb
61
+ - lib/decision_table/ruleset.rb
62
+ - lib/decision_table/version.rb
63
+ - spec/rule_spec.rb
64
+ - spec/rules.csv
65
+ - spec/ruleset_spec.rb
66
+ - spec/spec_helper.rb
67
+ homepage: ''
68
+ licenses: []
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ! '>='
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ requirements: []
86
+ rubyforge_project: decision_table
87
+ rubygems_version: 1.8.17
88
+ signing_key:
89
+ specification_version: 3
90
+ summary: Table driven decision logic in ruby
91
+ test_files:
92
+ - spec/rule_spec.rb
93
+ - spec/rules.csv
94
+ - spec/ruleset_spec.rb
95
+ - spec/spec_helper.rb