rufus-decision 1.2.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.txt +12 -0
- data/CREDITS.txt +1 -0
- data/LICENSE.txt +21 -0
- data/README.txt +4 -6
- data/Rakefile +80 -0
- data/TODO.txt +8 -0
- data/demo/public/decision.js +7 -0
- data/demo/public/images/arrow.png +0 -0
- data/demo/public/images/ruse_head_bg.png +0 -0
- data/demo/public/in.js +5 -0
- data/demo/public/index.html +140 -0
- data/demo/public/js/fluo-json.js +186 -0
- data/demo/public/js/request.js +23 -0
- data/demo/public/js/ruote-sheets.js +584 -0
- data/demo/start.rb +78 -0
- data/examples/readme_example.rb +51 -0
- data/lib/rufus-decision.rb +1 -1
- data/lib/rufus/decision.rb +1 -681
- data/lib/rufus/{hashes.rb → decision/hashes.rb} +1 -1
- data/lib/rufus/decision/table.rb +679 -0
- data/lib/rufus/decision/version.rb +7 -0
- data/rufus-decision.gemspec +91 -0
- data/test/eval_test.rb +1 -1
- data/test/test_base.rb +1 -0
- metadata +55 -19
data/CHANGELOG.txt
CHANGED
@@ -2,6 +2,18 @@
|
|
2
2
|
= rufus-decision CHANGELOG.txt
|
3
3
|
|
4
4
|
|
5
|
+
== rufus-decision - 1.3.0 released 2010/02/15
|
6
|
+
|
7
|
+
- removing 'require "rubygems"' from lib/ (argh)
|
8
|
+
- moving to jeweler (thanks Kenneth Kalmer)
|
9
|
+
- lib/rufus/decision/ dir
|
10
|
+
|
11
|
+
|
12
|
+
== rufus-decision - 1.2.0 released 2009/09/07
|
13
|
+
|
14
|
+
- issue 1 : made 'bounded' default and added 'unbounded' option
|
15
|
+
|
16
|
+
|
5
17
|
== rufus-decision - 1.1 released 2009/04/25
|
6
18
|
|
7
19
|
- todo #25670 : :ruby_eval settable at table initialization
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
|
2
|
+
Copyright (c) 2006-2010, John Mettraux, jmettraux@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
|
+
|
data/README.txt
CHANGED
@@ -4,16 +4,14 @@
|
|
4
4
|
|
5
5
|
== getting it
|
6
6
|
|
7
|
-
|
7
|
+
gem install rufus-decision
|
8
8
|
|
9
|
-
or at
|
10
|
-
|
11
|
-
http://rubyforge.org/frs/?group_id=4812
|
12
9
|
|
13
10
|
== intro blog post
|
14
11
|
|
15
12
|
http://jmettraux.wordpress.com/2009/04/25/rufus-decision-11-ruby-decision-tables/
|
16
13
|
|
14
|
+
|
17
15
|
== usage
|
18
16
|
|
19
17
|
More info at http://rufus.rubyforge.org/rufus-decision/classes/Rufus/Decision/Table.html but here is a recap.
|
@@ -102,7 +100,7 @@ WARNING though : use at your own risk. CSV loaded from untrusted locations may c
|
|
102
100
|
|
103
101
|
== uninstalling it
|
104
102
|
|
105
|
-
|
103
|
+
gem uninstall rufus-decision
|
106
104
|
|
107
105
|
|
108
106
|
== dependencies
|
@@ -124,7 +122,7 @@ irc.freenode.net #ruote
|
|
124
122
|
|
125
123
|
== issue tracker
|
126
124
|
|
127
|
-
http://
|
125
|
+
http://github.com/jmettraux/rufus-decision/issues
|
128
126
|
|
129
127
|
|
130
128
|
== source
|
data/Rakefile
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
require 'lib/rufus/decision/version.rb'
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'rake'
|
7
|
+
|
8
|
+
|
9
|
+
#
|
10
|
+
# CLEAN
|
11
|
+
|
12
|
+
require 'rake/clean'
|
13
|
+
CLEAN.include('pkg', 'tmp', 'html')
|
14
|
+
task :default => [ :clean ]
|
15
|
+
|
16
|
+
|
17
|
+
#
|
18
|
+
# GEM
|
19
|
+
|
20
|
+
require 'jeweler'
|
21
|
+
|
22
|
+
Jeweler::Tasks.new do |gem|
|
23
|
+
|
24
|
+
gem.version = Rufus::Decision::VERSION
|
25
|
+
gem.name = 'rufus-decision'
|
26
|
+
gem.summary = 'CSV based Ruby decision tables'
|
27
|
+
|
28
|
+
gem.description = %{
|
29
|
+
CSV based Ruby decision tables
|
30
|
+
}
|
31
|
+
gem.email = 'jmettraux@gmail.com'
|
32
|
+
gem.homepage = 'http://github.com/jmettraux/rufus-decision/'
|
33
|
+
gem.authors = [ 'John Mettraux' ]
|
34
|
+
gem.rubyforge_project = 'rufus'
|
35
|
+
|
36
|
+
gem.test_file = 'test/test.rb'
|
37
|
+
|
38
|
+
gem.add_dependency 'rufus-dollar'
|
39
|
+
gem.add_dependency 'rufus-treechecker'
|
40
|
+
gem.add_development_dependency 'yard'
|
41
|
+
gem.add_development_dependency 'jeweler'
|
42
|
+
|
43
|
+
# gemspec spec : http://www.rubygems.org/read/chapter/20
|
44
|
+
end
|
45
|
+
Jeweler::GemcutterTasks.new
|
46
|
+
|
47
|
+
|
48
|
+
#
|
49
|
+
# DOC
|
50
|
+
|
51
|
+
begin
|
52
|
+
|
53
|
+
require 'yard'
|
54
|
+
|
55
|
+
YARD::Rake::YardocTask.new do |doc|
|
56
|
+
doc.options = [
|
57
|
+
'-o', 'html/rufus-decision', '--title',
|
58
|
+
"rufus-decision #{Rufus::Decision::VERSION}"
|
59
|
+
]
|
60
|
+
end
|
61
|
+
|
62
|
+
rescue LoadError
|
63
|
+
|
64
|
+
task :yard do
|
65
|
+
abort "YARD is not available : sudo gem install yard"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
#
|
71
|
+
# TO THE WEB
|
72
|
+
|
73
|
+
task :upload_website => [ :clean, :yard ] do
|
74
|
+
|
75
|
+
account = 'jmettraux@rubyforge.org'
|
76
|
+
webdir = '/var/www/gforge-projects/rufus'
|
77
|
+
|
78
|
+
sh "rsync -azv -e ssh html/rufus-decision #{account}:#{webdir}/"
|
79
|
+
end
|
80
|
+
|
data/TODO.txt
ADDED
Binary file
|
Binary file
|
data/demo/public/in.js
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>testing a decision table</title>
|
5
|
+
<style>
|
6
|
+
|
7
|
+
/* tb rl */
|
8
|
+
/* t r b l */
|
9
|
+
|
10
|
+
body {
|
11
|
+
font-family: "Helvetica Neue Light", Helvetica, Arial, sans-serif;
|
12
|
+
font-size: 12pt;
|
13
|
+
}
|
14
|
+
|
15
|
+
#central {
|
16
|
+
}
|
17
|
+
#left_pane {
|
18
|
+
width: 100px;
|
19
|
+
float: left;
|
20
|
+
}
|
21
|
+
#right_pane {
|
22
|
+
margin-left: 10px;
|
23
|
+
float: left;
|
24
|
+
}
|
25
|
+
|
26
|
+
.table_buttons {
|
27
|
+
font-size: 70%
|
28
|
+
}
|
29
|
+
|
30
|
+
.ruse_cell {
|
31
|
+
border-top: 1px solid #dddddd;
|
32
|
+
border-left: 1px solid #dddddd;
|
33
|
+
border-right: 1px solid #F0F0F0;
|
34
|
+
border-bottom: 1px solid #F0F0F0;
|
35
|
+
margin: 0;
|
36
|
+
padding: 0;
|
37
|
+
text-align: right;
|
38
|
+
}
|
39
|
+
/*.ruse_cell:focus { // not used
|
40
|
+
background-color: #f0f0f0;
|
41
|
+
}*/
|
42
|
+
.focused {
|
43
|
+
background-color: #f0f0f0;
|
44
|
+
}
|
45
|
+
.ruse_headcell {
|
46
|
+
height: 1em;
|
47
|
+
float: left;
|
48
|
+
background: #f0f0f0 url(/images/ruse_head_bg.png) repeat top left;
|
49
|
+
}
|
50
|
+
.ruse_headcell_left {
|
51
|
+
float: left;
|
52
|
+
background: white;
|
53
|
+
height: 1em;
|
54
|
+
width: 1px;
|
55
|
+
}
|
56
|
+
.ruse_headcell_handle {
|
57
|
+
height: 1em;
|
58
|
+
width: 3px;
|
59
|
+
cursor: w-resize;
|
60
|
+
float: right;
|
61
|
+
}
|
62
|
+
</style>
|
63
|
+
|
64
|
+
<script src="js/fluo-json.js"></script>
|
65
|
+
<script src="js/ruote-sheets.js"></script>
|
66
|
+
<script src="js/request.js"></script>
|
67
|
+
</head>
|
68
|
+
|
69
|
+
<body>
|
70
|
+
|
71
|
+
<div id="central">
|
72
|
+
|
73
|
+
<div id="left_pane">
|
74
|
+
<img src="/images/arrow.png" align="right"/>
|
75
|
+
</div>
|
76
|
+
|
77
|
+
<div id="right_pane">
|
78
|
+
|
79
|
+
in : <span class="table_buttons">
|
80
|
+
<a href="#" onclick="RuoteSheets.addRow('sheet_in');">add row</a>
|
81
|
+
<a href="#" onclick="RuoteSheets.addCol('sheet_in');">add col</a>
|
82
|
+
<a href="#" onclick="RuoteSheets.deleteRow('sheet_in');">del row</a>
|
83
|
+
<a href="#" onclick="RuoteSheets.deleteCol('sheet_in');">del col</a>
|
84
|
+
<a href="#" onclick="RuoteSheets.undo('sheet_in');">undo</a>
|
85
|
+
</span>
|
86
|
+
<div id='sheet_in'></div>
|
87
|
+
|
88
|
+
<br/>
|
89
|
+
|
90
|
+
decision table : <span class="table_buttons">
|
91
|
+
<a href="#" onclick="RuoteSheets.addRow('sheet_decision');">add row</a>
|
92
|
+
<a href="#" onclick="RuoteSheets.addCol('sheet_decision');">add col</a>
|
93
|
+
<a href="#" onclick="RuoteSheets.deleteRow('sheet_decision');">del row</a>
|
94
|
+
<a href="#" onclick="RuoteSheets.deleteCol('sheet_decision');">del col</a>
|
95
|
+
<a href="#" onclick="RuoteSheets.undo('sheet_decision');">undo</a>
|
96
|
+
</span>
|
97
|
+
<div id='sheet_decision'></div>
|
98
|
+
|
99
|
+
<br/>
|
100
|
+
|
101
|
+
<input type="submit" onclick='return decide();' value='decide !'/><br />
|
102
|
+
|
103
|
+
<br/>
|
104
|
+
|
105
|
+
out :
|
106
|
+
<div id='sheet_out'></div>
|
107
|
+
|
108
|
+
</div>
|
109
|
+
|
110
|
+
</div>
|
111
|
+
|
112
|
+
<!-- some javascript -->
|
113
|
+
|
114
|
+
<script src="/in.js"></script>
|
115
|
+
<script src="/decision.js"></script>
|
116
|
+
|
117
|
+
<script>
|
118
|
+
|
119
|
+
RuoteSheets.render('sheet_in', data_in);
|
120
|
+
RuoteSheets.render('sheet_decision', data_decision);
|
121
|
+
|
122
|
+
function decide () {
|
123
|
+
|
124
|
+
var data =
|
125
|
+
'[' +
|
126
|
+
fluoToJson(RuoteSheets.toArray('sheet_in')) + ',' +
|
127
|
+
fluoToJson(RuoteSheets.toArray('sheet_decision')) +
|
128
|
+
']';
|
129
|
+
|
130
|
+
var result = httpPost('/decision', data);
|
131
|
+
result = fluoFromJson(result);
|
132
|
+
|
133
|
+
RuoteSheets.render('sheet_out', result);
|
134
|
+
}
|
135
|
+
|
136
|
+
</script>
|
137
|
+
|
138
|
+
</body>
|
139
|
+
</html>
|
140
|
+
|
@@ -0,0 +1,186 @@
|
|
1
|
+
// Copyright (C) 2008 Google Inc.
|
2
|
+
//
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
// you may not use this file except in compliance with the License.
|
5
|
+
// You may obtain a copy of the License at
|
6
|
+
//
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
//
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
// See the License for the specific language governing permissions and
|
13
|
+
// limitations under the License.
|
14
|
+
|
15
|
+
// ruote 'source' :
|
16
|
+
// http://github.com/jmettraux/ruote-fluo/tree/master/public/js/fluo-json.js
|
17
|
+
|
18
|
+
/**
|
19
|
+
* @param {string} json per RFC 4627
|
20
|
+
* @return {Object|Array}
|
21
|
+
* @author Mike Samuel <mikesamuel@gmail.com>
|
22
|
+
*/
|
23
|
+
//var jsonParse = (function () {
|
24
|
+
var fluoFromJson = (function () {
|
25
|
+
var number
|
26
|
+
= '(?:-?\\b(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\\b)';
|
27
|
+
var oneChar = '(?:[^\\0-\\x08\\x0a-\\x1f\"\\\\]'
|
28
|
+
+ '|\\\\(?:[\"/\\\\bfnrt]|u[0-9A-Fa-f]{4}))';
|
29
|
+
var string = '(?:\"' + oneChar + '*\")';
|
30
|
+
|
31
|
+
// Will match a value in a well-formed JSON file.
|
32
|
+
// If the input is not well-formed, may match strangely, but not in an unsafe
|
33
|
+
// way.
|
34
|
+
// Since this only matches value tokens, it does not match whitespace, colons,
|
35
|
+
// or commas.
|
36
|
+
var jsonToken = new RegExp(
|
37
|
+
'(?:false|true|null|[\\{\\}\\[\\]]'
|
38
|
+
+ '|' + number
|
39
|
+
+ '|' + string
|
40
|
+
+ ')', 'g');
|
41
|
+
|
42
|
+
// Matches escape sequences in a string literal
|
43
|
+
var escapeSequence = new RegExp('\\\\(?:([^u])|u(.{4}))', 'g');
|
44
|
+
|
45
|
+
// Decodes escape sequences in object literals
|
46
|
+
var escapes = {
|
47
|
+
'"': '"',
|
48
|
+
'/': '/',
|
49
|
+
'\\': '\\',
|
50
|
+
'b': '\b',
|
51
|
+
'f': '\f',
|
52
|
+
'n': '\n',
|
53
|
+
'r': '\r',
|
54
|
+
't': '\t'
|
55
|
+
};
|
56
|
+
function unescapeOne(_, ch, hex) {
|
57
|
+
return ch ? escapes[ch] : String.fromCharCode(parseInt(hex, 16));
|
58
|
+
}
|
59
|
+
|
60
|
+
// A non-falsy value that coerces to the empty string when used as a key.
|
61
|
+
var EMPTY_STRING = new String('');
|
62
|
+
var SLASH = '\\';
|
63
|
+
|
64
|
+
// Constructor to use based on an open token.
|
65
|
+
var firstTokenCtors = { '{': Object, '[': Array };
|
66
|
+
|
67
|
+
return function (json) {
|
68
|
+
// Split into tokens
|
69
|
+
var toks = json.match(jsonToken);
|
70
|
+
// Construct the object to return
|
71
|
+
var result;
|
72
|
+
var tok = toks[0];
|
73
|
+
if ('{' === tok) {
|
74
|
+
result = {};
|
75
|
+
} else if ('[' === tok) {
|
76
|
+
result = [];
|
77
|
+
} else {
|
78
|
+
throw new Error(tok);
|
79
|
+
}
|
80
|
+
|
81
|
+
// If undefined, the key in an object key/value record to use for the next
|
82
|
+
// value parsed.
|
83
|
+
var key;
|
84
|
+
// Loop over remaining tokens maintaining a stack of uncompleted objects and
|
85
|
+
// arrays.
|
86
|
+
var stack = [result];
|
87
|
+
for (var i = 1, n = toks.length; i < n; ++i) {
|
88
|
+
tok = toks[i];
|
89
|
+
|
90
|
+
var cont;
|
91
|
+
switch (tok.charCodeAt(0)) {
|
92
|
+
default: // sign or digit
|
93
|
+
cont = stack[0];
|
94
|
+
cont[key || cont.length] = +(tok);
|
95
|
+
key = void 0;
|
96
|
+
break;
|
97
|
+
case 0x22: // '"'
|
98
|
+
tok = tok.substring(1, tok.length - 1);
|
99
|
+
if (tok.indexOf(SLASH) !== -1) {
|
100
|
+
tok = tok.replace(escapeSequence, unescapeOne);
|
101
|
+
}
|
102
|
+
cont = stack[0];
|
103
|
+
if (!key) {
|
104
|
+
if (cont instanceof Array) {
|
105
|
+
key = cont.length;
|
106
|
+
} else {
|
107
|
+
key = tok || EMPTY_STRING; // Use as key for next value seen.
|
108
|
+
break;
|
109
|
+
}
|
110
|
+
}
|
111
|
+
cont[key] = tok;
|
112
|
+
key = void 0;
|
113
|
+
break;
|
114
|
+
case 0x5b: // '['
|
115
|
+
cont = stack[0];
|
116
|
+
stack.unshift(cont[key || cont.length] = []);
|
117
|
+
key = void 0;
|
118
|
+
break;
|
119
|
+
case 0x5d: // ']'
|
120
|
+
stack.shift();
|
121
|
+
break;
|
122
|
+
case 0x66: // 'f'
|
123
|
+
cont = stack[0];
|
124
|
+
cont[key || cont.length] = false;
|
125
|
+
key = void 0;
|
126
|
+
break;
|
127
|
+
case 0x6e: // 'n'
|
128
|
+
cont = stack[0];
|
129
|
+
cont[key || cont.length] = null;
|
130
|
+
key = void 0;
|
131
|
+
break;
|
132
|
+
case 0x74: // 't'
|
133
|
+
cont = stack[0];
|
134
|
+
cont[key || cont.length] = true;
|
135
|
+
key = void 0;
|
136
|
+
break;
|
137
|
+
case 0x7b: // '{'
|
138
|
+
cont = stack[0];
|
139
|
+
stack.unshift(cont[key || cont.length] = {});
|
140
|
+
key = void 0;
|
141
|
+
break;
|
142
|
+
case 0x7d: // '}'
|
143
|
+
stack.shift();
|
144
|
+
break;
|
145
|
+
}
|
146
|
+
}
|
147
|
+
// Fail if we've got an uncompleted object.
|
148
|
+
if (stack.length) { throw new Error(); }
|
149
|
+
return result;
|
150
|
+
};
|
151
|
+
})();
|
152
|
+
|
153
|
+
|
154
|
+
//
|
155
|
+
// the following code is ruote-fluo code, under the BSD license.
|
156
|
+
//
|
157
|
+
function fluoToJson (o, quotes) {
|
158
|
+
if (quotes != false) quotes = true;
|
159
|
+
if (o == null) return 'null';
|
160
|
+
var t = (typeof o);
|
161
|
+
if (t == 'undefined') return 'null' // really ?
|
162
|
+
if (t == 'number') return o;
|
163
|
+
if (t == 'string') {
|
164
|
+
//return quotes ? '"' + o + '"' : o;
|
165
|
+
if ( ! quotes) return o;
|
166
|
+
return '"' +
|
167
|
+
o
|
168
|
+
.replace(/"/g, '\\"')
|
169
|
+
.replace(/\r/g, '')
|
170
|
+
.replace(/\n/g, '\\n') +
|
171
|
+
'"';
|
172
|
+
// this global replace works with safari (webkit) and ffox
|
173
|
+
}
|
174
|
+
if (o.constructor.toString().indexOf(' Array()') == 8) {
|
175
|
+
var a = [];
|
176
|
+
for (var i = 0; i < o.length; i++) a.push(fluoToJson(o[i]));
|
177
|
+
return '[' + a.join(', ') + ']';
|
178
|
+
}
|
179
|
+
if (t == 'object') {
|
180
|
+
var a = [];
|
181
|
+
for (var key in o) a.push(fluoToJson(key) + ': ' + fluoToJson(o[key]));
|
182
|
+
return '{' + a.join(', ') + '}';
|
183
|
+
}
|
184
|
+
return o.toString();
|
185
|
+
}
|
186
|
+
|