rflare 1.0.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.
- checksums.yaml +7 -0
- data/.gemtest +0 -0
- data/Rakefile +8 -0
- data/bin/rflare +59 -0
- data/lib/rflare.rb +181 -0
- data/test/test_rflare.rb +290 -0
- metadata +79 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3d3aaf86caee415439c6b079238eb08f64f99361
|
4
|
+
data.tar.gz: 8577586e4feef9089da739591cc7ba6fe0ac13cf
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e322a3d7229063a594df73674c2fbb149fcc7a1b8a0a3041cdfcfc2511982ef9c91faf6d08b115949e3c958c069bd6c91cc7513f70a589a98e715c82d20f9197
|
7
|
+
data.tar.gz: eda58c277896406c7d880bc77684b412b19820c27448e8186d75281b280c967aff40faf0db1a74d084b8f4d7c09f3f9c80c566033e393082c41c69be75f4f474
|
data/.gemtest
ADDED
File without changes
|
data/Rakefile
ADDED
data/bin/rflare
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'csv'
|
4
|
+
require 'optparse'
|
5
|
+
require 'tgf'
|
6
|
+
require 'rflare'
|
7
|
+
|
8
|
+
csv_opts = {}
|
9
|
+
nodes, edges = [], []
|
10
|
+
|
11
|
+
OptionParser.new do |opts|
|
12
|
+
opts.banner = "Usage: rflare [options]"
|
13
|
+
|
14
|
+
opts.on("-F", "--field-separator [FS]") do |fs|
|
15
|
+
csv_opts[:col_sep] = fs
|
16
|
+
end
|
17
|
+
|
18
|
+
opts.on("-Q", "--quote-char [QC]") do |qc|
|
19
|
+
csv_opts[:quote_char] = qc
|
20
|
+
end
|
21
|
+
|
22
|
+
opts.on("-f", "--file [file]") do |file|
|
23
|
+
nodes, edges = TGF.parse(File(q))
|
24
|
+
end
|
25
|
+
|
26
|
+
opts.on("-e", "--evaluate [query]") do |query|
|
27
|
+
nodes, edges = TGF.parse(query, ';')
|
28
|
+
end
|
29
|
+
end.parse!
|
30
|
+
|
31
|
+
def rows_and_cols str
|
32
|
+
str.nil? ? [nil, nil] : str.split(/\s+/, 2)
|
33
|
+
end
|
34
|
+
|
35
|
+
out = CSV.new $stdout, csv_opts
|
36
|
+
ARGV.each do |csv_file|
|
37
|
+
ss = RFlare::Spreadsheet.new(CSV.read(csv_file, csv_opts))
|
38
|
+
my_nodes = nodes.map { |node|
|
39
|
+
if node.label.nil?
|
40
|
+
RFlare::Node.new node.id, nil, nil, nil, ss.row_bounds, ss.col_bounds
|
41
|
+
else
|
42
|
+
m = /^\/((?:.*\\\/)*.*)\/\s*(.+)?/.match node.label
|
43
|
+
rows, columns = rows_and_cols m[2]
|
44
|
+
RFlare::Node.new node.id, m[1], rows, columns, ss.row_bounds, ss.col_bounds
|
45
|
+
end
|
46
|
+
}
|
47
|
+
my_edges = edges.map { |edge|
|
48
|
+
rows, columns = rows_and_cols edge.label
|
49
|
+
RFlare::Edge.new edge.from, edge.to, rows, columns
|
50
|
+
}
|
51
|
+
root = my_nodes[0]
|
52
|
+
|
53
|
+
RFlare::Results.new(my_edges, my_nodes, ss, root).each { |match|
|
54
|
+
n = my_nodes.select {|node| node.id[0] != '_'}
|
55
|
+
out << n.map {|node| match[node.id] || ''}
|
56
|
+
}
|
57
|
+
end
|
58
|
+
out.flush
|
59
|
+
|
data/lib/rflare.rb
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
|
2
|
+
class Array
|
3
|
+
def bounds; 0 ... size; end
|
4
|
+
end
|
5
|
+
|
6
|
+
class Range
|
7
|
+
def cap num
|
8
|
+
num < min ? min : (num > max ? max : num)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module RFlare
|
13
|
+
|
14
|
+
class Square
|
15
|
+
def initialize rows, columns
|
16
|
+
@rows, @columns = rows, columns
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :rows, :columns
|
20
|
+
include Enumerable
|
21
|
+
|
22
|
+
def each
|
23
|
+
@rows.each {|row|
|
24
|
+
@columns.each {|col|
|
25
|
+
yield row, col
|
26
|
+
}
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
def include? row, col
|
31
|
+
@rows.include? row and @columns.include? col
|
32
|
+
end
|
33
|
+
|
34
|
+
def == other
|
35
|
+
@rows == other.rows and @columns == other.columns
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Node
|
40
|
+
def initialize id, match, rows, columns, row_bounds, col_bounds
|
41
|
+
@id = id || '_'
|
42
|
+
@match = Regexp.new(match || '.*')
|
43
|
+
@valid = Square.new(
|
44
|
+
Spec.new(rows || '0:*').range(0, row_bounds),
|
45
|
+
Spec.new(columns || '0:*').range(0, col_bounds))
|
46
|
+
end
|
47
|
+
|
48
|
+
attr_reader :id, :match, :valid
|
49
|
+
|
50
|
+
def matches ss, row, col
|
51
|
+
@valid.include? row, col and (ss[row,col] || '').to_s =~ @match
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class Spec
|
56
|
+
def initialize spec
|
57
|
+
@spec = spec.to_s
|
58
|
+
raise "invalid spec '#{@spec}'" if @spec !~ /[+-]?([0-9]+|\*)(:([+-]?([0-9]+|\*))?)?/
|
59
|
+
end
|
60
|
+
|
61
|
+
# + or - means relative to num, otherwise absolute
|
62
|
+
def range num, bounds
|
63
|
+
bits = @spec.split ":", 2
|
64
|
+
s = bits.size == 1 ? @spec : bits[0]
|
65
|
+
e = bits.size == 1 ? @spec : bits[1]
|
66
|
+
range_start(num, bounds, s) .. range_end(num, bounds, e)
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def range_start num, bounds, spec
|
72
|
+
if spec == '+*' or spec == '*'
|
73
|
+
num + 1
|
74
|
+
elsif spec == '-*'
|
75
|
+
bounds.min
|
76
|
+
elsif spec[0] == '+' or spec[0] == '-'
|
77
|
+
offset = spec[1, spec.length - 1].to_i
|
78
|
+
spec[0] == '+' ? (num + offset) : (num - offset)
|
79
|
+
elsif spec == ''
|
80
|
+
bounds.min
|
81
|
+
else
|
82
|
+
spec.to_i
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def range_end num, bounds, spec
|
87
|
+
if spec == '+*' or spec == '*' or spec == ""
|
88
|
+
bounds.max
|
89
|
+
elsif spec == '-*'
|
90
|
+
num - 1
|
91
|
+
elsif spec[0] == '+' or spec[0] == '-'
|
92
|
+
offset = spec[1, spec.length - 1].to_i
|
93
|
+
spec[0] == '+' ? (num + offset) : (num - offset)
|
94
|
+
else
|
95
|
+
spec.to_i
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class Edge
|
101
|
+
def initialize from, to, vert, horiz
|
102
|
+
@from, @to = from, to
|
103
|
+
@vert = Spec.new(vert || '+0')
|
104
|
+
@horiz = Spec.new(horiz || '+0')
|
105
|
+
end
|
106
|
+
|
107
|
+
attr_reader :from, :to
|
108
|
+
|
109
|
+
def get_square row, col, row_bounds, col_bounds
|
110
|
+
Square.new(
|
111
|
+
@vert.range(row, row_bounds),
|
112
|
+
@horiz.range(col, col_bounds))
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
class Spreadsheet
|
117
|
+
def initialize arr_of_rows
|
118
|
+
@data = arr_of_rows
|
119
|
+
@row_bounds = @data.bounds
|
120
|
+
@col_bounds = @data.empty? ? (0...0) : @data[0].bounds
|
121
|
+
end
|
122
|
+
|
123
|
+
def [] row, col
|
124
|
+
if @row_bounds.include? row and @col_bounds.include? col
|
125
|
+
@data[row][col]
|
126
|
+
else
|
127
|
+
nil
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
attr_reader :row_bounds, :col_bounds
|
132
|
+
end
|
133
|
+
|
134
|
+
class Results
|
135
|
+
def initialize edges, nodes, ss, root
|
136
|
+
@ss, @root = ss, root
|
137
|
+
@edges_byfrom = Hash.new {|h,k| h[k] = [] }
|
138
|
+
edges.each {|edge| @edges_byfrom[edge.from] << edge }
|
139
|
+
@nodes_byid = Hash.new
|
140
|
+
nodes.each {|node| @nodes_byid[node.id] = node}
|
141
|
+
end
|
142
|
+
|
143
|
+
include Enumerable
|
144
|
+
|
145
|
+
# start at root, looking for trees
|
146
|
+
def each
|
147
|
+
@root.valid.each {|row, col|
|
148
|
+
matches(row, col, @root).each { |match| yield match }
|
149
|
+
}
|
150
|
+
end
|
151
|
+
|
152
|
+
private
|
153
|
+
|
154
|
+
def matches row, col, node
|
155
|
+
return [] if !node.matches(@ss, row, col)
|
156
|
+
|
157
|
+
me = {node.id => @ss[row,col]}
|
158
|
+
edges = @edges_byfrom[node.id]
|
159
|
+
return [me] if edges.empty?
|
160
|
+
|
161
|
+
# get array with dims:
|
162
|
+
# (1) each edge from this
|
163
|
+
# -- we flatten the square-for-each-edge dim
|
164
|
+
# (2) each match (a Hash) from edge
|
165
|
+
edge_matches = edges.map do |edge|
|
166
|
+
to = @nodes_byid[edge.to]
|
167
|
+
sq = edge.get_square(row, col, @ss.row_bounds, @ss.col_bounds)
|
168
|
+
paths = sq.flat_map do |sq_row, sq_col|
|
169
|
+
matches sq_row, sq_col, to
|
170
|
+
end
|
171
|
+
paths.select {|a| !a.empty?}
|
172
|
+
end
|
173
|
+
|
174
|
+
[me].product(*edge_matches).map do |assignments_arr|
|
175
|
+
assignments_arr.inject Hash.new, :merge
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
data/test/test_rflare.rb
ADDED
@@ -0,0 +1,290 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'rflare'
|
3
|
+
|
4
|
+
class Hash
|
5
|
+
def <=> other
|
6
|
+
k = keys.sort <=> other.keys.sort
|
7
|
+
return k if k != 0
|
8
|
+
keys.sort.each {|key|
|
9
|
+
k = self[key] <=> other[key]
|
10
|
+
return k if k != 0
|
11
|
+
}
|
12
|
+
0
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class FlashRangeTest < Minitest::Test
|
17
|
+
def test_in
|
18
|
+
assert_equal 4, (2..5).cap(4)
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_after
|
22
|
+
assert_equal 5, (2..5).cap(7)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_before
|
26
|
+
assert_equal 2, (2..5).cap(1)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class FlashSquareTest < Minitest::Test
|
31
|
+
def test_include_in
|
32
|
+
assert RFlare::Square.new(2..4, 3..5).include? 3, 4
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_include_out
|
36
|
+
refute RFlare::Square.new(2..4, 3..5).include? 1, 4
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_enumerate
|
40
|
+
assert_equal [[2,3], [2,4], [2,5], [3,3], [3,4], [3,5]], RFlare::Square.new(2..3, 3..5).to_a
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_equals
|
44
|
+
assert_equal RFlare::Square.new(2..3, 4..5), RFlare::Square.new(2..3, 4..5)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class FlashSpecTest < Minitest::Test
|
49
|
+
def setup
|
50
|
+
@bounds = 0..10
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_single_relativeconst
|
54
|
+
assert_equal (4..4), RFlare::Spec.new('+1').range(3, @bounds)
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_single_plusstar
|
58
|
+
assert_equal (4..@bounds.max), RFlare::Spec.new('+*').range(3, @bounds)
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_single_plusstar2
|
62
|
+
assert_equal (4..@bounds.max), RFlare::Spec.new('*').range(3, @bounds)
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_minusone
|
66
|
+
assert_equal (2..2), RFlare::Spec.new('-1').range(3, @bounds)
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_minusstar
|
70
|
+
assert_equal (@bounds.min..2), RFlare::Spec.new('-*').range(3, @bounds)
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_abs
|
74
|
+
assert_equal (1..1), RFlare::Spec.new('1').range(3, @bounds)
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_range_relativeconst
|
78
|
+
assert_equal (4..6), RFlare::Spec.new('+1:+3').range(3, @bounds)
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_range_star
|
82
|
+
assert_equal (5..@bounds.max), RFlare::Spec.new('+2:+*').range(3, @bounds)
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_invalid
|
86
|
+
assert_raises(RuntimeError) { RFlare::Spec.new 'hat' }
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_range_blank_start
|
90
|
+
assert_equal (@bounds.min..4), RFlare::Spec.new(':4').range(3, @bounds)
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_range_blank_end
|
94
|
+
assert_equal (1..@bounds.max), RFlare::Spec.new('1:').range(3, @bounds)
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
class FlashNodeTest < Minitest::Test
|
100
|
+
def setup
|
101
|
+
@ss = RFlare::Spreadsheet.new [[0,1,2],['a','b','c']]
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_match
|
105
|
+
n = RFlare::Node.new nil, nil, 0, nil, @ss.row_bounds, @ss.col_bounds
|
106
|
+
assert_equal RFlare::Square.new(0..0, 0..2), n.valid
|
107
|
+
assert n.matches @ss, 0, 0
|
108
|
+
assert n.matches @ss, 0, 1
|
109
|
+
assert n.matches @ss, 0, 2
|
110
|
+
refute n.matches @ss, 1, 0
|
111
|
+
refute n.matches @ss, 1, 1
|
112
|
+
refute n.matches @ss, 1, 2
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
class FlashSpreadsheetTest < Minitest::Test
|
117
|
+
def test_empty
|
118
|
+
ss = RFlare::Spreadsheet.new []
|
119
|
+
assert_equal 0...0, ss.row_bounds
|
120
|
+
assert_equal 0...0, ss.col_bounds
|
121
|
+
assert_nil ss[0,0]
|
122
|
+
end
|
123
|
+
|
124
|
+
def test_full
|
125
|
+
ss = RFlare::Spreadsheet.new [[1,2,3],['a','b','c']]
|
126
|
+
assert_equal 0...2, ss.row_bounds
|
127
|
+
assert_equal 0...3, ss.col_bounds
|
128
|
+
assert_equal 1, ss[0,0]
|
129
|
+
assert_equal 'b', ss[1,1]
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class FlashResultsTest < Minitest::Test
|
134
|
+
def setup
|
135
|
+
@ss = RFlare::Spreadsheet.new [
|
136
|
+
[ nil, 'value', 'year', 'value', 'year', 'Comments'],
|
137
|
+
[ 'Albania', 1000, 1950, 930, 1981, 'FRA 1'],
|
138
|
+
[ 'Austria', 3139, 1951, 3177, 1955, 'FRA 3'],
|
139
|
+
[ 'Belgium', 541, 1947, 601, 1950, nil ],
|
140
|
+
['Bulgaria', 2964, 1947, 3259, 1958, 'FRA 1'],
|
141
|
+
[ 'Czech', 2416, 1950, 2503, 1960, 'NC']
|
142
|
+
]
|
143
|
+
end
|
144
|
+
|
145
|
+
def node id, match, rows = nil, columns = nil
|
146
|
+
RFlare::Node.new id, match, rows, columns, @ss.row_bounds, @ss.col_bounds
|
147
|
+
end
|
148
|
+
|
149
|
+
def edge from, to, vert, horiz
|
150
|
+
RFlare::Edge.new from, to, vert, horiz
|
151
|
+
end
|
152
|
+
|
153
|
+
def assert_matches edges, nodes, expected
|
154
|
+
actual = RFlare::Results.new(edges, nodes, @ss, nodes[0]).to_a
|
155
|
+
assert_equal expected.sort, actual.sort
|
156
|
+
end
|
157
|
+
|
158
|
+
# the tests:
|
159
|
+
|
160
|
+
def test_simplecell
|
161
|
+
nodes = [node(3, '^value$')]
|
162
|
+
edges = []
|
163
|
+
expected = [{3 => 'value'}, {3 => 'value'}]
|
164
|
+
assert_matches edges, nodes, expected
|
165
|
+
end
|
166
|
+
|
167
|
+
def test_header
|
168
|
+
nodes = [node(3, '^value$', 0, nil)]
|
169
|
+
edges = []
|
170
|
+
expected = [{3 => 'value'}, {3 => 'value'}]
|
171
|
+
assert_matches edges, nodes, expected
|
172
|
+
end
|
173
|
+
|
174
|
+
def test_oneedge
|
175
|
+
nodes = [node(4,'^[0-9]+$'), node(3, '^value$')]
|
176
|
+
edges = [edge(4, 3, '-*', nil)]
|
177
|
+
expected = [
|
178
|
+
{3 => 'value', 4 => 1000},
|
179
|
+
{3 => 'value', 4 => 3139},
|
180
|
+
{3 => 'value', 4 => 541},
|
181
|
+
{3 => 'value', 4 => 2964},
|
182
|
+
{3 => 'value', 4 => 2416},
|
183
|
+
{3 => 'value', 4 => 930},
|
184
|
+
{3 => 'value', 4 => 3177},
|
185
|
+
{3 => 'value', 4 => 601},
|
186
|
+
{3 => 'value', 4 => 3259},
|
187
|
+
{3 => 'value', 4 => 2503}
|
188
|
+
]
|
189
|
+
assert_matches edges, nodes, expected
|
190
|
+
end
|
191
|
+
|
192
|
+
def test_twoedges
|
193
|
+
nodes = [
|
194
|
+
node('v','^[0-9]+$'),
|
195
|
+
node('vcol', '^value$'),
|
196
|
+
node('y','^19[0-9]{2}$'),
|
197
|
+
]
|
198
|
+
edges = [
|
199
|
+
edge('v', 'vcol', '-*', nil),
|
200
|
+
edge('v', 'y', nil, '+1')
|
201
|
+
]
|
202
|
+
expected = [
|
203
|
+
{'vcol' => 'value', 'v' => 1000, 'y' => 1950},
|
204
|
+
{'vcol' => 'value', 'v' => 3139, 'y' => 1951},
|
205
|
+
{'vcol' => 'value', 'v' => 541, 'y' => 1947},
|
206
|
+
{'vcol' => 'value', 'v' => 2964, 'y' => 1947},
|
207
|
+
{'vcol' => 'value', 'v' => 2416, 'y' => 1950},
|
208
|
+
{'vcol' => 'value', 'v' => 930, 'y' => 1981},
|
209
|
+
{'vcol' => 'value', 'v' => 3177, 'y' => 1955},
|
210
|
+
{'vcol' => 'value', 'v' => 601, 'y' => 1950},
|
211
|
+
{'vcol' => 'value', 'v' => 3259, 'y' => 1958},
|
212
|
+
{'vcol' => 'value', 'v' => 2503, 'y' => 1960}
|
213
|
+
]
|
214
|
+
assert_matches edges, nodes, expected
|
215
|
+
end
|
216
|
+
|
217
|
+
def test_twolevelrecursion
|
218
|
+
nodes = [
|
219
|
+
node('v','^[0-9]+$'),
|
220
|
+
node('vcol', '^value$'),
|
221
|
+
node('y','^19[0-9]{2}$'),
|
222
|
+
node('ycol', '^year$'),
|
223
|
+
]
|
224
|
+
edges = [
|
225
|
+
edge('v', 'vcol', '-*', nil),
|
226
|
+
edge('y', 'ycol', '-*', nil),
|
227
|
+
edge('v', 'y', nil, '+1'),
|
228
|
+
]
|
229
|
+
expected = [
|
230
|
+
{'vcol' => 'value', 'v' => 1000, 'y' => 1950, 'ycol' => 'year'},
|
231
|
+
{'vcol' => 'value', 'v' => 3139, 'y' => 1951, 'ycol' => 'year'},
|
232
|
+
{'vcol' => 'value', 'v' => 541, 'y' => 1947, 'ycol' => 'year'},
|
233
|
+
{'vcol' => 'value', 'v' => 2964, 'y' => 1947, 'ycol' => 'year'},
|
234
|
+
{'vcol' => 'value', 'v' => 2416, 'y' => 1950, 'ycol' => 'year'},
|
235
|
+
{'vcol' => 'value', 'v' => 930, 'y' => 1981, 'ycol' => 'year'},
|
236
|
+
{'vcol' => 'value', 'v' => 3177, 'y' => 1955, 'ycol' => 'year'},
|
237
|
+
{'vcol' => 'value', 'v' => 601, 'y' => 1950, 'ycol' => 'year'},
|
238
|
+
{'vcol' => 'value', 'v' => 3259, 'y' => 1958, 'ycol' => 'year'},
|
239
|
+
{'vcol' => 'value', 'v' => 2503, 'y' => 1960, 'ycol' => 'year'},
|
240
|
+
]
|
241
|
+
assert_matches edges, nodes, expected
|
242
|
+
end
|
243
|
+
|
244
|
+
def test_withrowlimit
|
245
|
+
nodes = [node(3, '^[A-Za-z]+$', '1:*', 0)]
|
246
|
+
edges = []
|
247
|
+
expected = [
|
248
|
+
{3 => 'Albania'},
|
249
|
+
{3 => 'Austria'},
|
250
|
+
{3 => 'Belgium'},
|
251
|
+
{3 => 'Bulgaria'},
|
252
|
+
{3 => 'Czech'},
|
253
|
+
]
|
254
|
+
assert_matches edges, nodes, expected
|
255
|
+
end
|
256
|
+
|
257
|
+
def test_frompaper
|
258
|
+
nodes = [
|
259
|
+
node('c', '^[A-Za-z]+$', '1:*', 0),
|
260
|
+
node('v','^[0-9]+$', '1:*', nil),
|
261
|
+
node('vcol', '^value$', 0, nil),
|
262
|
+
node('y','^19[0-9]{2}$', '1:*', nil),
|
263
|
+
node('ycol', '^year$', 0, nil),
|
264
|
+
node('m','^[A-Za-z 0-9]*$', '1:*', nil),
|
265
|
+
node('mcol', '^Comments$', 0, nil),
|
266
|
+
]
|
267
|
+
edges = [
|
268
|
+
edge('v', 'vcol', '-*', nil),
|
269
|
+
edge('y', 'ycol', '-*', nil),
|
270
|
+
edge('m', 'mcol', '-*', nil),
|
271
|
+
edge('c', 'v', nil, '+*'),
|
272
|
+
edge('v', 'y', nil, '+1'),
|
273
|
+
edge('y', 'm', nil, '+*'),
|
274
|
+
]
|
275
|
+
expected = [
|
276
|
+
{'vcol' => 'value', 'v' => 1000, 'y' => 1950, 'ycol' => 'year', 'c' => 'Albania', 'mcol' => 'Comments', 'm' => 'FRA 1'},
|
277
|
+
{'vcol' => 'value', 'v' => 3139, 'y' => 1951, 'ycol' => 'year', 'c' => 'Austria', 'mcol' => 'Comments', 'm' => 'FRA 3'},
|
278
|
+
{'vcol' => 'value', 'v' => 541, 'y' => 1947, 'ycol' => 'year', 'c' => 'Belgium', 'mcol' => 'Comments', 'm' => nil},
|
279
|
+
{'vcol' => 'value', 'v' => 2964, 'y' => 1947, 'ycol' => 'year', 'c' => 'Bulgaria', 'mcol' => 'Comments', 'm' => 'FRA 1'},
|
280
|
+
{'vcol' => 'value', 'v' => 2416, 'y' => 1950, 'ycol' => 'year', 'c' => 'Czech', 'mcol' => 'Comments', 'm' => 'NC'},
|
281
|
+
{'vcol' => 'value', 'v' => 930, 'y' => 1981, 'ycol' => 'year', 'c' => 'Albania', 'mcol' => 'Comments', 'm' => 'FRA 1'},
|
282
|
+
{'vcol' => 'value', 'v' => 3177, 'y' => 1955, 'ycol' => 'year', 'c' => 'Austria', 'mcol' => 'Comments', 'm' => 'FRA 3'},
|
283
|
+
{'vcol' => 'value', 'v' => 601, 'y' => 1950, 'ycol' => 'year', 'c' => 'Belgium', 'mcol' => 'Comments', 'm' => nil},
|
284
|
+
{'vcol' => 'value', 'v' => 3259, 'y' => 1958, 'ycol' => 'year', 'c' => 'Bulgaria', 'mcol' => 'Comments', 'm' => 'FRA 1'},
|
285
|
+
{'vcol' => 'value', 'v' => 2503, 'y' => 1960, 'ycol' => 'year', 'c' => 'Czech', 'mcol' => 'Comments', 'm' => 'NC'},
|
286
|
+
]
|
287
|
+
assert_matches edges, nodes, expected
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
metadata
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rflare
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Remis
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-01-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: tgf
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10'
|
41
|
+
description: A command-line tool to extract relational data from semi-structured CSV
|
42
|
+
files
|
43
|
+
email: remis.thoughts@gmail.com
|
44
|
+
executables:
|
45
|
+
- rflare
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- ".gemtest"
|
50
|
+
- Rakefile
|
51
|
+
- bin/rflare
|
52
|
+
- lib/rflare.rb
|
53
|
+
- test/test_rflare.rb
|
54
|
+
homepage: https://research.microsoft.com/pubs/214302/flashrelate-tech-report-April2014.pdf
|
55
|
+
licenses:
|
56
|
+
- Apache-2.0
|
57
|
+
metadata: {}
|
58
|
+
post_install_message:
|
59
|
+
rdoc_options: []
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
requirements: []
|
73
|
+
rubyforge_project:
|
74
|
+
rubygems_version: 2.2.2
|
75
|
+
signing_key:
|
76
|
+
specification_version: 4
|
77
|
+
summary: Ruby version of Flare
|
78
|
+
test_files:
|
79
|
+
- test/test_rflare.rb
|