rubyfca 0.2.10 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/rubyfca.rb CHANGED
@@ -1,35 +1,39 @@
1
+ # frozen_string_literal: true
2
+
1
3
  ## lib/rubyfca.rb -- Formal Concept Analysis tool in Ruby
2
4
  ## Author:: Yoichiro Hasebe (mailto: yohasebe@gmail.com)
3
- ## Kow Kuroda (mailto: kuroda@nict.go.jp)
4
- ## Copyright:: Copyright 2010 Yoichiro Hasebe and Kow Kuroda
5
+ ## Kow Kuroda (mailto: kuroda@nict.go.jp)
6
+ ## Copyright:: Copyright 2009-2023 Yoichiro Hasebe and Kow Kuroda
5
7
  ## License:: GNU GPL version 3
6
8
 
7
- # -*- coding: utf-8 -*-
9
+ require "csv"
10
+ require "optimist"
11
+ require "roo"
8
12
 
9
- require 'csv'
10
- require 'ruby_graphviz'
13
+ require_relative "./rubyfca/ruby_graphviz"
14
+ require_relative "./rubyfca/version"
11
15
 
12
16
  private
13
17
 
14
- ## Take two arrays each consisting of 0s and 1s and create their logical disjunction
18
+ ## Take two arrays each consisting of 0s and 1s and create their logical disjunction
15
19
  def create_and_ary(a, b)
16
20
  return false if (s = a.size) != b.size
17
- result = (0..(s-1)).to_a.map{|i| a[i].to_i & b[i].to_i}
18
- return result
21
+
22
+ (0..(s - 1)).to_a.map { |i| a[i].to_i & b[i].to_i }
19
23
  end
20
24
 
21
25
  ## Take two arrays each consisting of 0s and 1s and create their logical conjunction
22
26
  def create_or_ary(a, b)
23
27
  return false if (s = a.size) != b.size
24
- result = (0..(s-1)).to_a.map{|i| a[i].to_i | b[i].to_i}
25
- return result
28
+
29
+ (0..(s - 1)).to_a.map { |i| a[i].to_i | b[i].to_i }
26
30
  end
27
31
 
28
32
  public
29
33
 
30
34
  ## Take care of errors
31
35
  def showerror(sentence, severity)
32
- if severity == 0
36
+ if severity.zero?
33
37
  puts "Warning: #{sentence} The output may not be meaningful."
34
38
  elsif severity == 1
35
39
  puts "Error: #{sentence} No output generated."
@@ -39,25 +43,27 @@ end
39
43
 
40
44
  ## Basic structure of the code is the same as Fcastone written in Perl by Uta Priss
41
45
  class FormalContext
42
-
43
46
  ## Converte cxt data to three basic structures of objects, attributes, and matrix
44
- def initialize(input, mode, label_contraction = false)
45
- if input.size == 0
46
- showerror("File is empty", 1)
47
- end
47
+ def initialize(input, mode, label_contraction: false)
48
+ showerror("File is empty", 1) if input.empty?
49
+ input = input.gsub(" ", " ")
48
50
  begin
49
51
  case mode
50
52
  when /cxt\z/
51
53
  read_cxt(input)
52
54
  when /csv\z/
53
55
  read_csv(input)
56
+ when /xlsx\z/
57
+ read_xlsx(input)
54
58
  end
55
- rescue => e
56
- showerror("Input data contains a syntax problem.", 1)
59
+ rescue StandardError => e
60
+ pp e.message
61
+ pp e.backtrace
62
+ showerror("Input data contains a syntactic problem.", 1)
57
63
  end
58
64
  @label_contraction = label_contraction
59
65
  end
60
-
66
+
61
67
  ## process cxt data
62
68
  def read_cxt(input)
63
69
  lines = input.split
@@ -65,53 +71,65 @@ class FormalContext
65
71
  if (lines[0] !~ /B/i || (2 * lines[1].to_i + lines[2].to_i + t1) != lines.size)
66
72
  showerror("Wrong cxt format!", 1)
67
73
  end
68
- @objects = lines[t1..(lines[1].to_i + t1 - 1)]
69
- @attributes = lines[(lines[1].to_i + t1) .. (lines[1].to_i + lines[2].to_i + t1 - 1)]
70
- lines = lines[(lines[1].to_i + lines[2].to_i + t1) .. lines.size]
71
- @matrix = changecrosssymbol("X", "\\.", lines)
74
+ @objects = lines[t1..(lines[1].to_i + t1 - 1)]
75
+ @attributes = lines[(lines[1].to_i + t1)..(lines[1].to_i + lines[2].to_i + t1 - 1)]
76
+ lines = lines[(lines[1].to_i + lines[2].to_i + t1)..lines.size]
77
+ @matrix = changecrosssymbol("X", "\\.", lines)
72
78
  end
73
-
79
+
74
80
  # process csv data using the standard csv library
75
81
  def read_csv(input)
76
82
  input = remove_blank(input)
77
83
  data = CSV.parse(input)
78
- @objects = trim_ary(data.transpose.first[1..-1])
79
- @attributes = trim_ary(data.first[1..-1])
84
+ @objects = trim_ary(data.transpose.first[1..])
85
+ @attributes = trim_ary(data.first[1..])
86
+ @matrix = []
87
+ data[1..].each do |line|
88
+ @matrix << line[1..].collect { |cell| /x/i =~ cell ? 1 : 0 }
89
+ end
90
+ end
91
+
92
+ # process xlsx data using the roo gem
93
+ def read_xlsx(file_path)
94
+ xlsx = Roo::Spreadsheet.open(file_path, extension: :xlsx)
95
+ @objects = xlsx.sheet(0).column(1)[1..]
96
+ @attributes = xlsx.sheet(0).row(1)[1..]
80
97
  @matrix = []
81
- data[1..-1].each do |line|
82
- @matrix << line[1..-1].collect { |cell| /x/i =~ cell ? 1 : 0 }
98
+ (2..xlsx.sheet(0).last_row).each do |row|
99
+ matrix_row = []
100
+ (2..xlsx.sheet(0).last_column).each do |col|
101
+ matrix_row << (xlsx.sheet(0).cell(row, col) =~ /x/i ? 1 : 0)
102
+ end
103
+ @matrix << matrix_row
83
104
  end
84
105
  end
85
-
106
+
86
107
  def remove_blank(input)
87
- blank_removed = ""
88
- input.each do |line|
89
- unless /^\s*$/ =~ line
90
- blank_removed << line
108
+ blank_removed = +""
109
+ input.split("\n").each do |line|
110
+ line = line.strip
111
+ unless /\A\s*\z/ =~ line
112
+ blank_removed << line + "\n"
91
113
  end
92
114
  end
93
115
  blank_removed
94
116
  end
95
-
117
+
96
118
  def trim_ary(ary)
97
- newary = ary.collect do |cell|
98
- cell.strip
99
- end
100
- newary
119
+ ary.map(&:strip)
101
120
  end
102
-
121
+
103
122
  ## Apply a formal concept analysis on the matrix
104
- def calculate
123
+ def calcurate
105
124
  @concepts, @extM, @intM = ganter_alg(@matrix)
106
125
  @relM, @reltrans, @rank = create_rel(@intM)
107
126
  @gammaM, @muM = gammaMu(@extM, @intM, @matrix)
108
127
  end
109
128
 
110
- ## This is an implementation of an algorithm described by Bernhard Ganter
129
+ ## This is an implementation of an algorithm described by Bernhard Ganter
111
130
  ## in "Two basic algorithms in concept analysis." Technische Hochschule
112
131
  ## Darmstadt, FB4-Preprint, 831, 1984.
113
132
  def ganter_alg(matrix)
114
-
115
133
  m = matrix
116
134
 
117
135
  ## all arrays except @idx are arrays of arrays of 0's and 1's
@@ -122,25 +140,25 @@ class FormalContext
122
140
  int_B = []
123
141
  endkey = []
124
142
  temp = []
125
-
143
+
126
144
  ## the level in the lattice from the top
127
- lvl = 0
145
+ lvl = 0
128
146
  ## is lower than the index of leftmost attr
129
- idx[lvl] = -1
147
+ idx[lvl] = -1
130
148
  ## number of attr. and objs
131
- anzM = m.size
132
- ## only needed for initialization
149
+ anzM = m.size
150
+ ## only needed for initialization
133
151
  anzG = m[0].size
134
152
  ## initialize extA[0] = [1,...,1]
135
- anzG.times do |i|
136
- if !ext_A[0]
153
+ anzG.times do
154
+ unless ext_A[0]
137
155
  ext_A[0] = []
138
156
  end
139
157
  ext_A[0] << 1
140
158
  end
141
159
  ## initialize extB[0] = [0,...,0]
142
- anzM.times do |i|
143
- if !int_B[0]
160
+ anzM.times do
161
+ unless int_B[0]
144
162
  int_B[0] = []
145
163
  end
146
164
  int_B[0] << 0
@@ -148,7 +166,7 @@ class FormalContext
148
166
  anzCpt = 0
149
167
  extension[0] = ext_A[0]
150
168
  intension[0] = int_B[0]
151
- anzM.times do |i|
169
+ anzM.times do
152
170
  endkey << 1
153
171
  end
154
172
 
@@ -157,13 +175,11 @@ class FormalContext
157
175
  (anzM - 1).downto(0) do |i|
158
176
  breakkey = false
159
177
  if (int_B[lvl][i] != 1)
160
- while (i < idx[lvl])
161
- lvl -= 1
162
- end
178
+ lvl -= 1 while (i < idx[lvl])
163
179
  idx[lvl + 1] = i
164
180
  ext_A[lvl + 1] = create_and_ary(ext_A[lvl], m[i])
165
181
  0.upto(i - 1) do |j|
166
- if(!breakkey && int_B[lvl][j] != 1)
182
+ if (!breakkey && int_B[lvl][j] != 1)
167
183
  temp = create_and_ary(ext_A[lvl + 1], m[j])
168
184
  if temp == ext_A[lvl + 1]
169
185
  breakkey = true
@@ -173,7 +189,7 @@ class FormalContext
173
189
  unless breakkey
174
190
  int_B[lvl + 1] = int_B[lvl].dup
175
191
  int_B[lvl + 1][i] = 1
176
- (i+1).upto(anzM - 1) do |k|
192
+ (i + 1).upto(anzM - 1) do |k|
177
193
  if int_B[lvl + 1][k] != 1
178
194
  temp = create_and_ary(ext_A[lvl + 1], m[k])
179
195
  if temp == ext_A[lvl + 1]
@@ -187,7 +203,7 @@ class FormalContext
187
203
  intension[anzCpt] = int_B[lvl]
188
204
  break
189
205
  end
190
- end
206
+ end
191
207
  end
192
208
  end
193
209
 
@@ -207,7 +223,7 @@ class FormalContext
207
223
  [c, intension, extension]
208
224
  end
209
225
 
210
- ## Output arrayconsists of the following:
226
+ ## Output arrayconsists of the following:
211
227
  ## r (subconcept superconcept relation)
212
228
  ## rt (trans. closure of r)
213
229
  ## s (ranked concepts)
@@ -224,11 +240,11 @@ class FormalContext
224
240
  unless r[i]
225
241
  r[i] = []
226
242
  end
227
- r[i][j] = 0;
243
+ r[i][j] = 0
228
244
  unless rt[i]
229
245
  rt[i] = []
230
246
  end
231
- rt[i][j] = 0;
247
+ rt[i][j] = 0
232
248
  end
233
249
  end
234
250
 
@@ -258,16 +274,15 @@ class FormalContext
258
274
  s[rank[i]] = []
259
275
  end
260
276
  s[rank[i]] << i
261
- end
277
+ end
262
278
  s = s.collect do |i|
263
- i ? i : [0]
279
+ i || [0]
264
280
  end
265
281
 
266
282
  [r, rt, s]
267
- end
283
+ end
268
284
 
269
285
  def gammaMu(extent, intent, cxt)
270
-
271
286
  gamma = []
272
287
  mu = []
273
288
  invcxt = []
@@ -281,31 +296,31 @@ class FormalContext
281
296
  0.upto(intent.size - 1) do |j|
282
297
  0.upto(cxt.size - 1) do |i|
283
298
  gamma[i] = [] unless gamma[i]
284
- if cxt[i] == intent[j]
285
- gamma[i][j] = 2
286
- elsif (!@label_contraction && create_or_ary(cxt[i], intent[j]) == cxt[i])
287
- gamma[i][j] = 1
288
- else
289
- gamma[i][j] = 0
290
- end
299
+ gamma[i][j] = if cxt[i] == intent[j]
300
+ 2
301
+ elsif (!@label_contraction && create_or_ary(cxt[i], intent[j]) == cxt[i])
302
+ 1
303
+ else
304
+ 0
305
+ end
291
306
  end
292
-
307
+
293
308
  0.upto(invcxt.size - 1) do |i|
294
309
  # next unless invcxt[i]
295
310
  mu[i] = [] unless mu[i]
296
- if invcxt[i] == extent[j]
297
- mu[i][j] = 2
298
- elsif (!@label_contraction && create_or_ary(invcxt[i], extent[j]) == invcxt[i])
299
- mu[i][j] = 1
300
- else
301
- mu[i][j] = 0
302
- end
311
+ mu[i][j] = if invcxt[i] == extent[j]
312
+ 2
313
+ elsif (!@label_contraction && create_or_ary(invcxt[i], extent[j]) == invcxt[i])
314
+ 1
315
+ else
316
+ 0
317
+ end
303
318
  end
304
319
  end
305
-
320
+
306
321
  [gamma, mu]
307
322
  end
308
-
323
+
309
324
  def changecrosssymbol(char1, char2, lns)
310
325
  rel = []
311
326
  lns.each do |ln|
@@ -326,36 +341,36 @@ class FormalContext
326
341
  ## Generate Graphviz dot data (not creating a file)
327
342
  ## For options, see 'rubyfca'
328
343
  def generate_dot(opts)
329
- index_max_width = @concepts.size.to_s.split(//).size
330
- nodesep = opts[:nodesep] ? opts[:nodesep].to_s : "0.4"
331
- ranksep = opts[:ranksep] ? opts[:ranksep].to_s : "0.2"
332
- clattice = RubyGraphviz.new("clattice", :rankdir => "", :nodesep => nodesep, :ranksep => ranksep)
333
-
334
- if opts[:circle] and opts[:legend]
335
- legend = RubyGraphviz.new("legend", :rankdir => "TB", :lebelloc => "t", :centered => "false")
336
- legend.node_default(:shape => "plaintext")
337
- legend.edge_default(:color => "gray60") if opts[:coloring]
344
+ # index_max_width = @concepts.size.to_s.split(//).size
345
+ nodesep = opts[:nodesep] ? opts[:nodesep].to_s : "0.4"
346
+ ranksep = opts[:ranksep] ? opts[:ranksep].to_s : "0.2"
347
+ clattice = RubyGraphviz.new("clattice", rankdir: "", nodesep: nodesep, ranksep: ranksep)
348
+
349
+ if opts[:circle] && opts[:legend]
350
+ legend = RubyGraphviz.new("legend", rankdir: "TB", lebelloc: "t", centered: "false")
351
+ legend.node_default(shape: "plaintext")
352
+ legend.edge_default(color: "gray60") if opts[:coloring]
338
353
  legends = []
339
354
  end
340
355
 
341
356
  if opts[:circle]
342
- clattice.node_default(:shape => "circle", :style => "filled")
343
- clattice.edge_default(:dir => "none", :minlen => "2")
344
- clattice.edge_default(:color => "gray60") if opts[:coloring]
357
+ clattice.node_default(shape: "circle", style: "filled")
358
+ clattice.edge_default(dir: "none", minlen: "2")
359
+ clattice.edge_default(color: "gray60") if opts[:coloring]
345
360
  else
346
- clattice.node_default(:shape => "record", :margin => "0.2,0.055")
347
- clattice.edge_default(:dir => "none")
348
- clattice.edge_default(:color => "gray60") if opts[:coloring]
361
+ clattice.node_default(shape: "record", margin: "0.2,0.055")
362
+ clattice.edge_default(dir: "none")
363
+ clattice.edge_default(color: "gray60") if opts[:coloring]
349
364
  end
350
-
365
+
351
366
  0.upto(@concepts.size - 1) do |i|
352
367
  objfull = []
353
- attrfull = []
368
+ attrfull = []
354
369
  0.upto(@gammaM.size - 1) do |j|
355
370
  if @gammaM[j][i] == 2
356
- # pointing finger does not appear correctly in eps...
371
+ # pointing finger does not appear correctly in eps...
357
372
  # obj = opts[:full] ? @objects[j] + " " + [0x261C].pack("U") : @objects[j]
358
- obj = opts[:full] ? @objects[j] + "*" : @objects[j]
373
+ obj = opts[:full] ? @objects[j].to_s + "*" : @objects[j].to_s
359
374
  objfull << obj
360
375
  elsif @gammaM[j][i] == 1
361
376
  objfull << @objects[j]
@@ -363,9 +378,9 @@ class FormalContext
363
378
  end
364
379
  0.upto(@muM.size - 1) do |k|
365
380
  if @muM[k][i] == 2
366
- # pointing finger does not appear correctly in eps...
381
+ # pointing finger does not appear correctly in eps...
367
382
  # att = opts[:full] ? @attributes[k] + " " + [0x261C].pack("U") : @attributes[k]
368
- att = opts[:full] ? @attributes[k] + "*" : @attributes[k]
383
+ att = opts[:full] ? @attributes[k].to_s + "*" : @attributes[k].to_s
369
384
  attrfull << att
370
385
  elsif @muM[k][i] == 1
371
386
  attrfull << @attributes[k]
@@ -374,21 +389,21 @@ class FormalContext
374
389
 
375
390
  concept_id = i + 1
376
391
 
377
- attr_str = attrfull.join('<br />')
392
+ attr_str = attrfull.join("<br />")
378
393
  attr_str = attr_str == "" ? " " : attr_str
379
-
380
- if opts[:coloring] == 0 or /\A\s+\z/ =~ attr_str
394
+
395
+ if opts[:coloring].zero? || /\A\s+\z/ =~ attr_str
381
396
  attr_color = "white"
382
397
  elsif opts[:coloring] == 1
383
398
  attr_color = "lightblue"
384
399
  elsif opts[:coloring] == 2
385
400
  attr_color = "gray87"
386
401
  end
387
-
388
- obj_str = objfull.join('<br />')
402
+
403
+ obj_str = objfull.join("<br />")
389
404
  obj_str = obj_str == "" ? " " : obj_str
390
405
 
391
- if opts[:coloring] == 0 or /\A\s+\z/ =~ obj_str
406
+ if opts[:coloring].zero? || /\A\s+\z/ =~ obj_str
392
407
  obj_color = "white"
393
408
  elsif opts[:coloring] == 1
394
409
  obj_color = "pink"
@@ -396,37 +411,36 @@ class FormalContext
396
411
  obj_color = "gray92"
397
412
  end
398
413
 
399
- label = "<<table border=\"0\" cellborder=\"1\" cellspacing=\"0\">" +
400
- "<tr><td balign=\"left\" align=\"left\" bgcolor=\"#{attr_color}\">#{attr_str}</td></tr>" +
401
- "<tr><td balign=\"left\" align=\"left\" bgcolor=\"#{obj_color}\">#{obj_str}</td></tr>" +
402
- "</table>>"
414
+ label = "<<table border=\"0\" cellborder=\"1\" cellspacing=\"0\">" \
415
+ "<tr><td balign=\"left\" align=\"left\" bgcolor=\"#{attr_color}\">#{attr_str}</td></tr>" \
416
+ "<tr><td balign=\"left\" align=\"left\" bgcolor=\"#{obj_color}\">#{obj_str}</td></tr>" \
417
+ "</table>>"
403
418
 
404
- if opts[:circle] and opts[:legend]
419
+ if opts[:circle] && opts[:legend]
405
420
 
406
- leg = "<<table border=\"0\" cellborder=\"1\" cellspacing=\"0\">" +
407
- "<tr><td rowspan=\"2\">#{concept_id}</td><td balign=\"left\" align=\"left\" bgcolor=\"#{attr_color}\">#{attr_str}</td></tr>" +
408
- "<tr><td balign=\"left\" align=\"left\" bgcolor=\"#{obj_color}\">#{obj_str}</td></tr>" +
409
- "</table>>"
421
+ leg = "<<table border=\"0\" cellborder=\"1\" cellspacing=\"0\">" \
422
+ "<tr><td rowspan=\"2\">#{concept_id}</td><td balign=\"left\" align=\"left\" bgcolor=\"#{attr_color}\">#{attr_str}</td></tr>" \
423
+ "<tr><td balign=\"left\" align=\"left\" bgcolor=\"#{obj_color}\">#{obj_str}</td></tr>" \
424
+ "</table>>"
410
425
 
411
- if !attrfull.empty? or !objfull.empty?
412
- legend.node("cl#{concept_id}k", :label => concept_id, :style => "invis")
413
- legend.node("cl#{concept_id}v", :label => leg, :fillcolor => "white")
414
- legend.rank("cl#{concept_id}k", "cl#{concept_id}v", :style => "invis", :length => "0.0")
426
+ if !attrfull.empty? || !objfull.empty?
427
+ legend.node("cl#{concept_id}k", label: concept_id, style: "invis")
428
+ legend.node("cl#{concept_id}v", label: leg, fillcolor: "white")
429
+ legend.rank("cl#{concept_id}k", "cl#{concept_id}v", style: "invis", length: "0.0")
415
430
  if legends[-1]
416
- legend.edge("cl#{legends[-1]}k", "cl#{concept_id}k", :style => "invis", :length => "0.0")
431
+ legend.edge("cl#{legends[-1]}k", "cl#{concept_id}k", style: "invis", length: "0.0")
417
432
  end
418
433
  legends << concept_id
419
434
  end
420
435
  end
421
-
436
+
422
437
  if opts[:circle]
423
- clattice.node("c#{i}", :width => "0.5", :fontsize => "14.0", :label => concept_id)
438
+ clattice.node("c#{i}", width: "0.5", fontsize: "14.0", label: concept_id)
424
439
  else
425
- clattice.node("c#{i}", :label => label, :shape => "plaintext",
426
- :height => "0.0", :width => "0.0", :margin => "0.0")
440
+ clattice.node("c#{i}", label: label, shape: "plaintext", height: "0.0", width: "0.0", margin: "0.0")
427
441
  end
428
442
  end
429
-
443
+
430
444
  0.upto(@relM.size - 1) do |i|
431
445
  0.upto(@relM.size - 1) do |j|
432
446
  if @relM[i][j] == 1
@@ -434,11 +448,11 @@ class FormalContext
434
448
  end
435
449
  end
436
450
  end
437
-
438
- clattice.subgraph(legend) if opts[:circle] and opts[:legend]
451
+
452
+ clattice.subgraph(legend) if opts[:circle] && opts[:legend]
439
453
  clattice.to_dot
440
454
  end
441
-
455
+
442
456
  ## Generate an actual graphic file (Graphviz dot needs to be installed properly)
443
457
  def generate_img(outfile, image_type, opts)
444
458
  dot = generate_dot(opts)
@@ -446,15 +460,14 @@ class FormalContext
446
460
  if isthere_dot !~ /dot.*version/i
447
461
  showerror("Graphviz's dot program cannot be found.", 1)
448
462
  else
449
- if opts[:straight]
450
- cmd = "dot | neato -n -T#{image_type} -o#{outfile} 2>rubyfca.log"
451
- else
452
- cmd = "dot -T#{image_type} -o#{outfile} 2>rubyfca.log"
453
- end
454
- IO.popen(cmd, 'r+') do |io|
463
+ cmd = if opts[:straight]
464
+ "dot | neato -n -T#{image_type} -o#{outfile} 2>rubyfca.log"
465
+ else
466
+ "dot -T#{image_type} -o#{outfile} 2>rubyfca.log"
467
+ end
468
+ IO.popen(cmd, "r+") do |io|
455
469
  io.puts dot
456
470
  end
457
471
  end
458
- end
472
+ end
459
473
  end
460
-
data/rubyfca.gemspec CHANGED
@@ -1,60 +1,25 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
- # -*- encoding: utf-8 -*-
1
+ # frozen_string_literal: true
5
2
 
6
- Gem::Specification.new do |s|
7
- s.name = %q{rubyfca}
8
- s.version = "0.2.10"
3
+ require File.expand_path("lib/rubyfca/version", __dir__)
9
4
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Yoichiro Hasebe"]
12
- s.date = %q{2010-01-17}
13
- s.default_executable = %q{rubyfca}
14
- s.description = %q{Command line Formal Concept Ananlysis (FCA) tool written in Ruby}
15
- s.email = %q{yohasebe@gmail.com}
16
- s.executables = ["rubyfca"]
17
- s.extra_rdoc_files = [
18
- "LICENSE",
19
- "README.rdoc"
20
- ]
21
- s.files = [
22
- ".document",
23
- ".gitignore",
24
- "LICENSE",
25
- "README.rdoc",
26
- "Rakefile",
27
- "VERSION",
28
- "bin/rubyfca",
29
- "lib/ruby_graphviz.rb",
30
- "lib/rubyfca.rb",
31
- "lib/trollop.rb",
32
- "rubyfca.gemspec",
33
- "test/rubyfca_test.rb",
34
- "test/test_data.cxt",
35
- "test/test_helper.rb"
36
- ]
37
- s.homepage = %q{http://github.com/yohasebe/rubyfca}
38
- s.rdoc_options = ["--charset=UTF-8"]
39
- s.require_paths = ["lib"]
40
- s.rubygems_version = %q{1.3.5}
41
- s.summary = %q{Command line Formal Concept Ananlysis (FCA) tool written in Ruby}
42
- s.test_files = [
43
- "test/rubyfca_test.rb",
44
- "test/test_helper.rb"
45
- ]
5
+ Gem::Specification.new do |gem|
6
+ gem.name = "rubyfca"
7
+ gem.version = RubyFCA::VERSION
8
+ gem.authors = ["Yoichiro Hasebe", "Kow Kuroda"]
9
+ gem.email = ["yohasebe@gmail.com"]
10
+ gem.summary = "Command line FCA tool written in Ruby"
11
+ gem.description = "Command line Formal Concept Analysis (FCA) tool written in Ruby"
12
+ gem.homepage = "http://github.com/yohasebe/rubyfca"
46
13
 
47
- if s.respond_to? :specification_version then
48
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
49
- s.specification_version = 3
14
+ gem.required_ruby_version = ">= 2.6.10"
15
+ gem.licenses = ["GPL-3.0"]
16
+ gem.files = `git ls-files`.split("\n")
17
+ gem.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
18
+ gem.require_paths = ["lib"]
50
19
 
51
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
52
- s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
53
- else
54
- s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
55
- end
56
- else
57
- s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
58
- end
59
- end
20
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
60
21
 
22
+ gem.add_dependency "optimist"
23
+ gem.add_dependency "roo"
24
+ gem.add_development_dependency "minitest"
25
+ end
Binary file
Binary file
Binary file
Binary file