pangrid 0.3.0 → 0.5.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 +5 -5
- data/lib/pangrid/frontend/webrick.rb +24 -5
- data/lib/pangrid/plugin.rb +5 -3
- data/lib/pangrid/plugins/acrosslite.rb +4 -0
- data/lib/pangrid/plugins/csv.rb +3 -0
- data/lib/pangrid/plugins/excel.rb +3 -0
- data/lib/pangrid/plugins/exolve.rb +128 -0
- data/lib/pangrid/plugins/json.rb +78 -0
- data/lib/pangrid/plugins/png.rb +25 -0
- data/lib/pangrid/plugins/qxw.rb +49 -0
- data/lib/pangrid/plugins/reddit.rb +5 -0
- data/lib/pangrid/plugins/text.rb +4 -1
- data/lib/pangrid/version.rb +1 -1
- data/lib/pangrid/xw.rb +43 -5
- metadata +26 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 33b68bb3ffccf5e357c16637245e4488b3541070737f171e2773e035ccdfbcfd
|
4
|
+
data.tar.gz: 05b0853605cccef29232acb6f96e1a53e1356e830e85a7c158595c426c179fbf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '00778b056954ae735461bc4782e5c9aae027ac6d0b2928a6253507afa8113abbc26ee908ba4e68b49c42d128a15b38406604eb6303e6214af88969824c2854b9'
|
7
|
+
data.tar.gz: de0cd413482c54b815ecf2eb97b41511b1a309858d61a2e3571cfff98fb1d6478fda2d5f4c332142253ae25969fa682dca97ddcb8d68d49f39b162f248614630
|
@@ -26,11 +26,30 @@ class Servlet < WEBrick::HTTPServlet::AbstractServlet
|
|
26
26
|
rescue Exception => e
|
27
27
|
out = e.inspect
|
28
28
|
end
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
29
|
+
|
30
|
+
response.header['Access-Control-Allow-Origin'] = '*'
|
31
|
+
|
32
|
+
case request.path
|
33
|
+
when "/"
|
34
|
+
template = IO.read(TEMPLATE)
|
35
|
+
response.status = 200
|
36
|
+
response.content_type = "text/html"
|
37
|
+
response.body = template % out
|
38
|
+
when "/blob"
|
39
|
+
response.status = 200
|
40
|
+
response.content_type = "application/octet-stream"
|
41
|
+
response.body = out
|
42
|
+
when "/json"
|
43
|
+
if request.query["to"] == "json"
|
44
|
+
response.status = 200
|
45
|
+
response.content_type = "text/json"
|
46
|
+
response.body = out
|
47
|
+
else
|
48
|
+
response.status = 200
|
49
|
+
response.content_type = "text/json"
|
50
|
+
response.body = '{ "error" : "non-json format requested" }'
|
51
|
+
end
|
52
|
+
end
|
34
53
|
end
|
35
54
|
end
|
36
55
|
|
data/lib/pangrid/plugin.rb
CHANGED
@@ -35,6 +35,8 @@ class Plugin
|
|
35
35
|
FAILED = []
|
36
36
|
MISSING_DEPS = {}
|
37
37
|
|
38
|
+
DESCRIPTION = ""
|
39
|
+
|
38
40
|
def self.inherited(subclass)
|
39
41
|
name = class_to_name(subclass.name)
|
40
42
|
#puts "Registered #{subclass} as #{name}"
|
@@ -62,13 +64,13 @@ class Plugin
|
|
62
64
|
|
63
65
|
def self.list_all
|
64
66
|
puts "-------------------------------------------------------"
|
65
|
-
puts "Available plugins:"
|
67
|
+
puts "Available plugins (F = from, T = to):"
|
66
68
|
puts "-------------------------------------------------------"
|
67
69
|
REGISTRY.keys.sort.each do |name|
|
68
70
|
plugin = REGISTRY[name]
|
69
71
|
provides = [:read, :write].select {|m| plugin.method_defined? m}
|
70
|
-
provides = provides.map {|m| {read: '
|
71
|
-
puts "
|
72
|
+
provides = provides.map {|m| {read: 'F', write: 'T'}[m]}.join
|
73
|
+
puts " #{name} [#{provides}]".ljust(30) + plugin.const_get(:DESCRIPTION)
|
72
74
|
end
|
73
75
|
if !MISSING_DEPS.empty?
|
74
76
|
puts
|
@@ -75,6 +75,8 @@ class AcrossLiteBinary < Plugin
|
|
75
75
|
# crossword, checksums
|
76
76
|
attr_accessor :xw, :cs
|
77
77
|
|
78
|
+
DESCRIPTION = "AcrossLite binary format (.puz)"
|
79
|
+
|
78
80
|
HEADER_FORMAT = "v A12 v V2 A4 v2 A12 c2 v3"
|
79
81
|
HEADER_CHECKSUM_FORMAT = "c2 v3"
|
80
82
|
EXT_HEADER_FORMAT = "A4 v2"
|
@@ -359,6 +361,8 @@ class AcrossLiteText < Plugin
|
|
359
361
|
|
360
362
|
attr_accessor :xw, :rebus
|
361
363
|
|
364
|
+
DESCRIPTION = "AcrossLite text format"
|
365
|
+
|
362
366
|
def initialize
|
363
367
|
@xw = XWord.new
|
364
368
|
end
|
data/lib/pangrid/plugins/csv.rb
CHANGED
@@ -0,0 +1,128 @@
|
|
1
|
+
# Exolve (https://github.com/viresh-ratnakar/exolve) is a browser-based
|
2
|
+
# crossword solver that allows you to embed the crossword and solving software
|
3
|
+
# in a single HTML file.
|
4
|
+
|
5
|
+
module Pangrid
|
6
|
+
|
7
|
+
module ExolveWriter
|
8
|
+
def write(xw)
|
9
|
+
headers = format_headers(xw)
|
10
|
+
across, down = xw.number
|
11
|
+
grid = format_grid(xw)
|
12
|
+
ac = format_clues(across, xw.across_clues)
|
13
|
+
dn = format_clues(down, xw.down_clues)
|
14
|
+
across = ["exolve-across:"] + indent(ac)
|
15
|
+
down = ["exolve-down:"] + indent(dn)
|
16
|
+
grid = ["exolve-grid:"] + indent(grid)
|
17
|
+
body = headers + grid + across + down
|
18
|
+
out = ["exolve-begin"] + indent(body) + ["exolve-end"]
|
19
|
+
out.join("\n")
|
20
|
+
end
|
21
|
+
|
22
|
+
def format_headers(xw)
|
23
|
+
headers = [
|
24
|
+
'id', 'replace-with-unique-id',
|
25
|
+
'title', xw.title,
|
26
|
+
'setter', xw.author,
|
27
|
+
'width', xw.width,
|
28
|
+
'height', xw.height,
|
29
|
+
'copyright', xw.copyright,
|
30
|
+
'prelude', xw.preamble
|
31
|
+
]
|
32
|
+
headers.each_slice(2).select {|k, v| v}.map {|k, v| "exolve-#{k}: #{v}"}
|
33
|
+
end
|
34
|
+
|
35
|
+
def format_clues(numbers, clues)
|
36
|
+
numbers.zip(clues).map {|n, c| "#{n.to_s.rjust(2)} #{c}"}
|
37
|
+
end
|
38
|
+
|
39
|
+
def indent(lines)
|
40
|
+
lines.map {|x| " " + x}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
module ExolveReader
|
45
|
+
def read(data)
|
46
|
+
s = data.each_line.map(&:rstrip)
|
47
|
+
first = s.index('exolve-begin')
|
48
|
+
check("exolve-begin missing") { first }
|
49
|
+
last = s.index('exolve-end')
|
50
|
+
check("exolve-end missing") { last }
|
51
|
+
lines = s[(first + 1)...last]
|
52
|
+
xw = XWord.new
|
53
|
+
s = sections(lines)
|
54
|
+
s.each do |_, field, data|
|
55
|
+
if %w(title setter copyright prelude).include? field
|
56
|
+
xw[field] = data
|
57
|
+
elsif %w(height width).include? field
|
58
|
+
xw[field] = data.to_i
|
59
|
+
elsif %(across down).include? field
|
60
|
+
xw["#{field}_clues"] = data
|
61
|
+
elsif field == "grid"
|
62
|
+
xw.solution = parse_grid(data)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
xw
|
66
|
+
end
|
67
|
+
|
68
|
+
def sections(lines)
|
69
|
+
headers = lines.each.with_index.map do |l, i|
|
70
|
+
m = l.match(/^(\s+)exolve-(\w+):(.*)$/)
|
71
|
+
if m
|
72
|
+
_, indent, field, data = m.to_a
|
73
|
+
[i, field, data.strip]
|
74
|
+
else
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
end
|
78
|
+
headers.compact!
|
79
|
+
headers.push([lines.length, "", ""])
|
80
|
+
headers.each_cons(2) do |i, j|
|
81
|
+
if i[2].empty?
|
82
|
+
i[2] = lines[(i[0] + 1)...j[0]].map(&:strip)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
headers.pop
|
86
|
+
headers
|
87
|
+
end
|
88
|
+
|
89
|
+
def parse_grid_char(char)
|
90
|
+
case char
|
91
|
+
when '0'; :null
|
92
|
+
when '.'; :black
|
93
|
+
else; char
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def parse_grid(lines)
|
98
|
+
grid = lines.map(&:strip).map {|x| x.split(//)}
|
99
|
+
grid.map do |col|
|
100
|
+
col.map do |c|
|
101
|
+
Cell.new(:solution => parse_grid_char(c))
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
class ExolveFilled < Plugin
|
108
|
+
include ExolveReader
|
109
|
+
include ExolveWriter
|
110
|
+
|
111
|
+
DESCRIPTION = "Exolve writer with solutions"
|
112
|
+
|
113
|
+
def format_grid(xw)
|
114
|
+
xw.to_array(:black => '.', :null => '0').map(&:join)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class ExolveBlank < Plugin
|
119
|
+
include ExolveWriter
|
120
|
+
|
121
|
+
DESCRIPTION = "Exolve writer without solutions"
|
122
|
+
|
123
|
+
def format_grid(xw)
|
124
|
+
xw.to_array(:black => '.', :null => '0') {|c| 0}.map(&:join)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
end # module Pangrid
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# Json
|
2
|
+
#
|
3
|
+
# cell = { x : int, y : int, contents : string }
|
4
|
+
#
|
5
|
+
# xword = { rows : int,
|
6
|
+
# cols : int,
|
7
|
+
# cells : [cell],
|
8
|
+
# across : [string]
|
9
|
+
# down : [string]
|
10
|
+
# }
|
11
|
+
|
12
|
+
require 'json'
|
13
|
+
|
14
|
+
module Pangrid
|
15
|
+
|
16
|
+
class Json < Plugin
|
17
|
+
|
18
|
+
DESCRIPTION = "Simple JSON format"
|
19
|
+
|
20
|
+
def write(xw)
|
21
|
+
cells = []
|
22
|
+
(0 ... xw.height).each do |y|
|
23
|
+
(0 ... xw.width).each do |x|
|
24
|
+
cell = xw.solution[y][x]
|
25
|
+
s = case cell.solution
|
26
|
+
when :black; '#'
|
27
|
+
when :null; ''
|
28
|
+
when Rebus; cell.solution.inspect
|
29
|
+
else; cell.solution
|
30
|
+
end
|
31
|
+
|
32
|
+
cells.push({ x: x, y: y, contents: s })
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
h = {
|
37
|
+
rows: xw.height,
|
38
|
+
cols: xw.width,
|
39
|
+
cells: cells,
|
40
|
+
across: xw.across_clues,
|
41
|
+
down: xw.down_clues
|
42
|
+
}
|
43
|
+
|
44
|
+
::JSON.generate(h)
|
45
|
+
end
|
46
|
+
|
47
|
+
def read(data)
|
48
|
+
json = ::JSON.parse(data)
|
49
|
+
xw = XWord.new
|
50
|
+
|
51
|
+
xw.height = json['rows']
|
52
|
+
xw.width = json['cols']
|
53
|
+
xw.solution = Array.new(xw.height) { Array.new(xw.width) }
|
54
|
+
json['cells'].each do |c|
|
55
|
+
cell = Cell.new
|
56
|
+
s = c['contents']
|
57
|
+
cell.solution =
|
58
|
+
case s
|
59
|
+
when ""; :null
|
60
|
+
when "#"; :black
|
61
|
+
else
|
62
|
+
if s.length == 1
|
63
|
+
s
|
64
|
+
else
|
65
|
+
Rebus.new s
|
66
|
+
end
|
67
|
+
end
|
68
|
+
x, y = c['x'], c['y']
|
69
|
+
xw.solution[y][x] = cell
|
70
|
+
end
|
71
|
+
xw.across_clues = json['across']
|
72
|
+
xw.down_clues = json['down']
|
73
|
+
xw
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'chunky_png'
|
2
|
+
|
3
|
+
module Pangrid
|
4
|
+
|
5
|
+
class PNGThumbnail < Plugin
|
6
|
+
|
7
|
+
def write(xw)
|
8
|
+
black = ChunkyPNG::Color::BLACK
|
9
|
+
white = ChunkyPNG::Color::WHITE
|
10
|
+
red = ChunkyPNG::Color::rgb(255, 0, 0)
|
11
|
+
grey = ChunkyPNG::Color::rgb(128, 128, 128)
|
12
|
+
scale = 4
|
13
|
+
png = ChunkyPNG::Image.new(64, 64, ChunkyPNG::Color::TRANSPARENT)
|
14
|
+
xw.each_cell_with_coords do |x, y, cell|
|
15
|
+
c = cell.black? ? black : white
|
16
|
+
png.rect(
|
17
|
+
x * scale, y * scale, (x + 1) * scale, (y + 1) * scale,
|
18
|
+
stroke_color = grey, fill_color = c)
|
19
|
+
end
|
20
|
+
png.rect(0, 0, xw.width * scale, xw.height * scale, stroke_color = red)
|
21
|
+
png.to_blob
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end # module Pangrid
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# Qxw savefile for rectangular grids [http://www.quinapalus.com/qxw.html]
|
2
|
+
|
3
|
+
module Pangrid
|
4
|
+
|
5
|
+
QXW_GRID_ERROR = "Could not read grid from .qxw file"
|
6
|
+
|
7
|
+
class Qxw < Plugin
|
8
|
+
|
9
|
+
DESCRIPTION = "QXW grid reader (rectangular grids only)"
|
10
|
+
|
11
|
+
def read(data)
|
12
|
+
xw = XWord.new
|
13
|
+
lines = data.lines.map(&:chomp)
|
14
|
+
gp = lines.find {|x| x =~ /^GP( \d+){6}$/}
|
15
|
+
check("Could not read grid size from .qxw file") { gp }
|
16
|
+
type, w, h, _ = gp.scan(/\d+/).map(&:to_i)
|
17
|
+
check("Only rectangular grids are supported") { type == 0 }
|
18
|
+
xw.width = w
|
19
|
+
xw.height = h
|
20
|
+
xw.solution = []
|
21
|
+
grid = lines.select {|x| x =~ /^SQ /}
|
22
|
+
grid.each do |line|
|
23
|
+
parts = line.scan(/\w+/)
|
24
|
+
check(QXW_GRID_ERROR) { parts.length == 6 || parts.length == 7 }
|
25
|
+
col, row, b, c = parts[1].to_i, parts[2].to_i, parts[5].to_i, parts[6]
|
26
|
+
cell = Cell.new
|
27
|
+
if b == 1
|
28
|
+
cell.solution = :black
|
29
|
+
elsif c == nil
|
30
|
+
cell.solution = :null
|
31
|
+
else
|
32
|
+
cell.solution = c
|
33
|
+
end
|
34
|
+
xw.solution[row] ||= []
|
35
|
+
xw.solution[row][col] = cell
|
36
|
+
end
|
37
|
+
check(QXW_GRID_ERROR) { xw.solution.length == xw.height }
|
38
|
+
check(QXW_GRID_ERROR) { xw.solution.all? {|i| i.compact.length == xw.width} }
|
39
|
+
|
40
|
+
# Placeholder clues
|
41
|
+
_, _, words_a, words_d = xw.number(true)
|
42
|
+
xw.across_clues = words_a.map {|i| "[#{i}]" }
|
43
|
+
xw.down_clues = words_d.map {|i| "[#{i}]" }
|
44
|
+
|
45
|
+
xw
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end # module Pangrid
|
@@ -34,6 +34,8 @@ end
|
|
34
34
|
class RedditFilled < Plugin
|
35
35
|
include RedditWriter
|
36
36
|
|
37
|
+
DESCRIPTION = "Reddit format (with filled squares)"
|
38
|
+
|
37
39
|
def write(xw)
|
38
40
|
write_xw(xw)
|
39
41
|
end
|
@@ -48,6 +50,8 @@ end
|
|
48
50
|
class RedditBlank < Plugin
|
49
51
|
include RedditWriter
|
50
52
|
|
53
|
+
DESCRIPTION = "Reddit format (with blank squares)"
|
54
|
+
|
51
55
|
def write(xw)
|
52
56
|
write_xw(xw)
|
53
57
|
end
|
@@ -88,6 +92,7 @@ class RedditBlank < Plugin
|
|
88
92
|
#
|
89
93
|
# we have to be careful about leading/trailing |s
|
90
94
|
ix = lines.find_index {|row| row =~ /^[|\s-]+$/}
|
95
|
+
check("Could not find grid") { not ix.nil? }
|
91
96
|
width = lines[ix].gsub(/\s/, '').split('|').reject(&:empty?).length
|
92
97
|
lines = [lines[(ix - 1)]] + lines[(ix + 1) .. -1]
|
93
98
|
grid = lines.take_while {|i| is_grid_row(i)}
|
data/lib/pangrid/plugins/text.rb
CHANGED
@@ -9,6 +9,9 @@
|
|
9
9
|
module Pangrid
|
10
10
|
|
11
11
|
class Text < Plugin
|
12
|
+
|
13
|
+
DESCRIPTION = "Basic text format (mostly for debugging)"
|
14
|
+
|
12
15
|
def write(xw)
|
13
16
|
across, down = xw.number
|
14
17
|
rows = xw.to_array(:black => '#', :null => '.')
|
@@ -27,7 +30,7 @@ class Text < Plugin
|
|
27
30
|
end
|
28
31
|
|
29
32
|
def format_clues(numbers, clues, indent)
|
30
|
-
numbers.zip(clues).map {|n, c| " "*indent + "#{n}. #{c}"}.join("\n")
|
33
|
+
numbers.zip(clues).map {|n, c| " "*indent + "#{n.to_s.rjust(2)}. #{c}"}.join("\n")
|
31
34
|
end
|
32
35
|
end
|
33
36
|
|
data/lib/pangrid/version.rb
CHANGED
data/lib/pangrid/xw.rb
CHANGED
@@ -53,6 +53,15 @@ class Cell
|
|
53
53
|
rebus? ? solution.to_char : solution
|
54
54
|
end
|
55
55
|
|
56
|
+
def to_w
|
57
|
+
case solution
|
58
|
+
when :black; '#'
|
59
|
+
when :null; '.'
|
60
|
+
when Rebus; solution.inspect
|
61
|
+
else; solution
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
56
65
|
def inspect
|
57
66
|
case solution
|
58
67
|
when :black; '#'
|
@@ -86,19 +95,40 @@ class XWord < OpenStruct
|
|
86
95
|
boundary?(x, y - 1) && !boundary?(x, y) && !boundary?(x, y + 1)
|
87
96
|
end
|
88
97
|
|
89
|
-
|
98
|
+
# Word starting from a square
|
99
|
+
def word_from(x, y, dir)
|
100
|
+
s = ""
|
101
|
+
while !boundary?(x, y) do
|
102
|
+
s += solution[y][x].to_w
|
103
|
+
if dir == :across
|
104
|
+
x += 1
|
105
|
+
else
|
106
|
+
y += 1
|
107
|
+
end
|
108
|
+
end
|
109
|
+
s
|
110
|
+
end
|
111
|
+
|
112
|
+
def number(words = false)
|
90
113
|
n, across, down = 1, [], []
|
114
|
+
words_a, words_d = [], []
|
91
115
|
(0 ... height).each do |y|
|
92
116
|
(0 ... width).each do |x|
|
93
|
-
|
94
|
-
|
117
|
+
if across? x, y
|
118
|
+
across << n
|
119
|
+
words_a << word_from(x, y, :across)
|
120
|
+
end
|
121
|
+
if down? x, y
|
122
|
+
down << n
|
123
|
+
words_d << word_from(x, y, :down)
|
124
|
+
end
|
95
125
|
if across.last == n || down.last == n
|
96
126
|
solution[y][x].number = n
|
97
127
|
n += 1
|
98
128
|
end
|
99
129
|
end
|
100
130
|
end
|
101
|
-
[across, down]
|
131
|
+
words ? [across, down, words_a, words_d] : [across, down]
|
102
132
|
end
|
103
133
|
|
104
134
|
def each_cell
|
@@ -109,6 +139,14 @@ class XWord < OpenStruct
|
|
109
139
|
end
|
110
140
|
end
|
111
141
|
|
142
|
+
def each_cell_with_coords
|
143
|
+
(0 ... height).each do |y|
|
144
|
+
(0 ... width).each do |x|
|
145
|
+
yield [x, y, solution[y][x]]
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
112
150
|
# {:black => char, :null => char} -> Any[][]
|
113
151
|
def to_array(opts = {})
|
114
152
|
opts = {:black => '#', :null => ' '}.merge(opts)
|
@@ -137,7 +175,7 @@ class XWord < OpenStruct
|
|
137
175
|
if c.rebus?
|
138
176
|
r = c.solution
|
139
177
|
if self.rebus[s]
|
140
|
-
sym,
|
178
|
+
sym, _ = self.rebus[s]
|
141
179
|
r.symbol = sym.to_s
|
142
180
|
else
|
143
181
|
k += 1
|
metadata
CHANGED
@@ -1,16 +1,30 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pangrid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Martin DeMello
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
13
|
-
|
11
|
+
date: 2021-10-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: webrick
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.7.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.7.0
|
27
|
+
description:
|
14
28
|
email: martindemello@gmail.com
|
15
29
|
executables:
|
16
30
|
- pangrid
|
@@ -29,6 +43,10 @@ files:
|
|
29
43
|
- lib/pangrid/plugins/acrosslite.rb
|
30
44
|
- lib/pangrid/plugins/csv.rb
|
31
45
|
- lib/pangrid/plugins/excel.rb
|
46
|
+
- lib/pangrid/plugins/exolve.rb
|
47
|
+
- lib/pangrid/plugins/json.rb
|
48
|
+
- lib/pangrid/plugins/png.rb
|
49
|
+
- lib/pangrid/plugins/qxw.rb
|
32
50
|
- lib/pangrid/plugins/reddit.rb
|
33
51
|
- lib/pangrid/plugins/text.rb
|
34
52
|
- lib/pangrid/utils.rb
|
@@ -38,7 +56,7 @@ homepage: https://github.com/martindemello/pangrid
|
|
38
56
|
licenses:
|
39
57
|
- MIT
|
40
58
|
metadata: {}
|
41
|
-
post_install_message:
|
59
|
+
post_install_message:
|
42
60
|
rdoc_options:
|
43
61
|
- "--main"
|
44
62
|
- README
|
@@ -55,9 +73,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
55
73
|
- !ruby/object:Gem::Version
|
56
74
|
version: '0'
|
57
75
|
requirements: []
|
58
|
-
|
59
|
-
|
60
|
-
signing_key:
|
76
|
+
rubygems_version: 3.2.22
|
77
|
+
signing_key:
|
61
78
|
specification_version: 4
|
62
79
|
summary: A crossword file format converter
|
63
80
|
test_files: []
|