rubyfca 0.2.10 → 0.3.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.
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