rflare 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (7) hide show
  1. checksums.yaml +7 -0
  2. data/.gemtest +0 -0
  3. data/Rakefile +8 -0
  4. data/bin/rflare +59 -0
  5. data/lib/rflare.rb +181 -0
  6. data/test/test_rflare.rb +290 -0
  7. metadata +79 -0
@@ -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
File without changes
@@ -0,0 +1,8 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << 'test'
5
+ end
6
+
7
+ desc "Run tests"
8
+ task :default => :test
@@ -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
+
@@ -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
+
@@ -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