squib 0.15.3 → 0.16.0.pre.preview1
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 +4 -4
- data/.github/workflows/tests.yml +22 -0
- data/CHANGELOG.md +22 -0
- data/Dockerfile +18 -16
- data/Guardfile +8 -0
- data/README.md +3 -9
- data/RELEASE TODO.md +1 -0
- data/Rakefile +3 -0
- data/lib/squib.rb +3 -1
- data/lib/squib/args/arg_loader.rb +109 -106
- data/lib/squib/args/box.rb +52 -48
- data/lib/squib/args/card_range.rb +26 -24
- data/lib/squib/args/color_validator.rb +4 -9
- data/lib/squib/args/coords.rb +39 -25
- data/lib/squib/args/csv_opts.rb +13 -16
- data/lib/squib/args/dir_validator.rb +7 -12
- data/lib/squib/args/draw.rb +69 -68
- data/lib/squib/args/embed_adjust.rb +12 -15
- data/lib/squib/args/embed_key.rb +6 -11
- data/lib/squib/args/hand_special.rb +25 -25
- data/lib/squib/args/import.rb +54 -27
- data/lib/squib/args/input_file.rb +22 -26
- data/lib/squib/args/paint.rb +30 -31
- data/lib/squib/args/paragraph.rb +95 -93
- data/lib/squib/args/save_batch.rb +50 -48
- data/lib/squib/args/scale_box.rb +43 -39
- data/lib/squib/args/sheet.rb +147 -149
- data/lib/squib/args/showcase_special.rb +32 -32
- data/lib/squib/args/sprue_file.rb +30 -30
- data/lib/squib/args/svg_special.rb +26 -26
- data/lib/squib/args/transform.rb +48 -54
- data/lib/squib/args/typographer.rb +88 -92
- data/lib/squib/args/unit_conversion.rb +6 -8
- data/lib/squib/args/xywh_shorthands.rb +56 -0
- data/lib/squib/builtin/projects/advanced/config.yml +4 -3
- data/lib/squib/builtin/projects/basic/config.yml +4 -3
- data/lib/squib/conf.rb +5 -0
- data/lib/squib/deck.rb +34 -12
- data/lib/squib/dsl/background.rb +35 -0
- data/lib/squib/dsl/circle.rb +39 -0
- data/lib/squib/dsl/csv.rb +42 -0
- data/lib/squib/dsl/curve.rb +35 -0
- data/lib/squib/dsl/cut_zone.rb +47 -0
- data/lib/squib/dsl/ellipse.rb +37 -0
- data/lib/squib/dsl/grid.rb +35 -0
- data/lib/squib/{api → dsl}/groups.rb +0 -0
- data/lib/squib/dsl/hand.rb +42 -0
- data/lib/squib/dsl/line.rb +35 -0
- data/lib/squib/dsl/png.rb +56 -0
- data/lib/squib/dsl/polygon.rb +36 -0
- data/lib/squib/dsl/rect.rb +37 -0
- data/lib/squib/dsl/safe_zone.rb +48 -0
- data/lib/squib/dsl/save.rb +21 -0
- data/lib/squib/dsl/save_pdf.rb +50 -0
- data/lib/squib/dsl/save_png.rb +47 -0
- data/lib/squib/dsl/save_sheet.rb +53 -0
- data/lib/squib/dsl/showcase.rb +43 -0
- data/lib/squib/dsl/star.rb +37 -0
- data/lib/squib/dsl/svg.rb +62 -0
- data/lib/squib/dsl/text.rb +54 -0
- data/lib/squib/dsl/text_embed.rb +78 -0
- data/lib/squib/dsl/triangle.rb +35 -0
- data/lib/squib/{api → dsl}/units.rb +10 -0
- data/lib/squib/dsl/xlsx.rb +40 -0
- data/lib/squib/dsl/yaml.rb +40 -0
- data/lib/squib/errors_warnings/warn_unexpected_params.rb +14 -0
- data/lib/squib/graphics/save_images.rb +3 -3
- data/lib/squib/graphics/save_sprue.rb +14 -2
- data/lib/squib/graphics/text.rb +37 -9
- data/lib/squib/import/csv_importer.rb +45 -0
- data/lib/squib/import/quantity_exploder.rb +18 -0
- data/lib/squib/import/xlsx_importer.rb +28 -0
- data/lib/squib/import/yaml_importer.rb +30 -0
- data/lib/squib/layout_parser.rb +24 -7
- data/lib/squib/sprues/crop_line.rb +6 -6
- data/lib/squib/sprues/crop_line_dash.rb +6 -6
- data/lib/squib/sprues/sprue.rb +16 -14
- data/lib/squib/version.rb +1 -1
- data/samples/autoscale_font/_autoscale_font.rb +77 -8
- data/samples/colors/_switch_color.rb +2 -2
- data/samples/data/_excel.rb +1 -1
- data/samples/ranges/_ranges.rb +1 -1
- data/samples/saves/_save_filenames.rb +4 -0
- data/samples/saves/_save_pdf.rb +1 -1
- data/samples/saves/_saves.rb +2 -1
- data/samples/shapes/_draw_shapes.rb +2 -2
- data/samples/sprues/_builtin_sprues.rb +1 -0
- data/samples/sprues/_fold_sheet.rb +4 -1
- data/samples/text/_text.rb +6 -1
- data/samples/text/_text_options.rb +2 -1
- data/samples/units/_cells.rb +51 -0
- data/samples/units/_shorthands.rb +49 -0
- data/samples/units/_units.rb +7 -0
- data/squib.gemspec +11 -5
- metadata +120 -33
- data/.travis.yml +0 -17
- data/appveyor.yml +0 -25
- data/lib/squib/api/background.rb +0 -15
- data/lib/squib/api/data.rb +0 -137
- data/lib/squib/api/image.rb +0 -49
- data/lib/squib/api/save.rb +0 -83
- data/lib/squib/api/shapes.rb +0 -124
- data/lib/squib/api/text.rb +0 -25
- data/lib/squib/api/text_embed.rb +0 -71
data/lib/squib/args/import.rb
CHANGED
@@ -1,40 +1,67 @@
|
|
1
|
-
|
1
|
+
module Squib::Args
|
2
|
+
module_function def extract_import(opts)
|
3
|
+
# note how we don't use ArgLoader here because it's way more complex than
|
4
|
+
# what we need here. Don't need layouts or singleton expansion, so...
|
5
|
+
# ...let's just do it ourselves.
|
6
|
+
Import.parameters.each { |p, value| opts[p] = value unless opts.key? p }
|
7
|
+
return Import.new.load! opts
|
8
|
+
end
|
2
9
|
|
3
|
-
|
4
|
-
|
5
|
-
|
10
|
+
class Import
|
11
|
+
|
12
|
+
def self.parameters
|
13
|
+
{
|
14
|
+
data: nil,
|
15
|
+
explode: 'qty',
|
16
|
+
file: nil,
|
17
|
+
sheet: 0,
|
18
|
+
strip: true,
|
19
|
+
}
|
20
|
+
end
|
6
21
|
|
7
|
-
|
8
|
-
include ArgLoader
|
22
|
+
attr_accessor *(self.parameters.keys)
|
9
23
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
}
|
14
|
-
end
|
24
|
+
def self.expanding_parameters
|
25
|
+
[] # none of them
|
26
|
+
end
|
15
27
|
|
16
|
-
|
17
|
-
|
18
|
-
|
28
|
+
def self.params_with_units
|
29
|
+
[] # none of them
|
30
|
+
end
|
19
31
|
|
20
|
-
|
21
|
-
|
22
|
-
|
32
|
+
def load!(opts)
|
33
|
+
@strip = validate_strip opts[:strip]
|
34
|
+
@explode = validate_explode opts[:explode]
|
35
|
+
@file = validate_file opts[:file]
|
36
|
+
@data = validate_data opts[:data]
|
37
|
+
@sheet = opts[:sheet]
|
38
|
+
return self
|
39
|
+
end
|
23
40
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
41
|
+
def validate_strip(arg)
|
42
|
+
raise 'Strip must be true or false' unless arg == true || arg == false
|
43
|
+
arg
|
44
|
+
end
|
28
45
|
|
29
|
-
|
30
|
-
|
31
|
-
|
46
|
+
def validate_explode(arg)
|
47
|
+
arg.to_s
|
48
|
+
end
|
32
49
|
|
33
|
-
|
34
|
-
|
35
|
-
|
50
|
+
def validate_file(arg)
|
51
|
+
return nil if arg.nil?
|
52
|
+
raise "File #{File.expand_path(arg)} does not exist!" unless File.exists?(arg)
|
53
|
+
File.expand_path(arg)
|
54
|
+
end
|
36
55
|
|
56
|
+
def strip?
|
57
|
+
strip
|
58
|
+
end
|
59
|
+
|
60
|
+
def validate_data(arg)
|
61
|
+
return nil if arg.nil?
|
62
|
+
arg.to_s
|
37
63
|
end
|
38
64
|
|
39
65
|
end
|
66
|
+
|
40
67
|
end
|
@@ -1,37 +1,33 @@
|
|
1
1
|
require_relative 'arg_loader'
|
2
2
|
|
3
|
-
module Squib
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
class InputFile
|
8
|
-
include ArgLoader
|
9
|
-
|
10
|
-
def initialize(dsl_method_default = {})
|
11
|
-
@dsl_method_default = dsl_method_default
|
12
|
-
end
|
3
|
+
module Squib::Args
|
4
|
+
module_function def extract_input_file(opts, deck, dsl_method_default = {})
|
5
|
+
InputFile.new(dsl_method_default).extract!(opts, deck)
|
6
|
+
end
|
13
7
|
|
14
|
-
|
15
|
-
|
16
|
-
sheet: 0,
|
17
|
-
}
|
18
|
-
end
|
8
|
+
class InputFile
|
9
|
+
include ArgLoader
|
19
10
|
|
20
|
-
|
21
|
-
|
22
|
-
|
11
|
+
def initialize(dsl_method_default = {})
|
12
|
+
@dsl_method_default = dsl_method_default
|
13
|
+
end
|
23
14
|
|
24
|
-
|
25
|
-
|
26
|
-
|
15
|
+
def self.parameters
|
16
|
+
{ file: nil }
|
17
|
+
end
|
27
18
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
File.expand_path(arg)
|
32
|
-
end
|
19
|
+
def self.expanding_parameters
|
20
|
+
parameters.keys # all of them
|
21
|
+
end
|
33
22
|
|
23
|
+
def self.params_with_units
|
24
|
+
[] # none of them
|
34
25
|
end
|
35
26
|
|
27
|
+
def validate_file(arg, _i)
|
28
|
+
return nil if arg.nil?
|
29
|
+
raise "File #{File.expand_path(arg)} does not exist!" unless File.exists?(arg)
|
30
|
+
File.expand_path(arg)
|
31
|
+
end
|
36
32
|
end
|
37
33
|
end
|
data/lib/squib/args/paint.rb
CHANGED
@@ -2,43 +2,42 @@ require 'cairo'
|
|
2
2
|
require_relative 'arg_loader'
|
3
3
|
require_relative 'color_validator'
|
4
4
|
|
5
|
-
module Squib
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
{ alpha: 1.0,
|
14
|
-
blend: :none,
|
15
|
-
mask: nil,
|
16
|
-
}
|
17
|
-
end
|
18
|
-
|
19
|
-
|
20
|
-
def self.expanding_parameters
|
21
|
-
parameters.keys # all of them are expandable
|
22
|
-
end
|
5
|
+
module Squib::Args
|
6
|
+
module_function def extract_paint(opts, deck)
|
7
|
+
Paint.new(deck.custom_colors).extract!(opts, deck)
|
8
|
+
end
|
9
|
+
|
10
|
+
class Paint
|
11
|
+
include ArgLoader
|
12
|
+
include ColorValidator
|
23
13
|
|
24
|
-
|
25
|
-
|
26
|
-
|
14
|
+
def initialize(custom_colors)
|
15
|
+
@custom_colors = custom_colors
|
16
|
+
end
|
27
17
|
|
28
|
-
|
29
|
-
|
30
|
-
|
18
|
+
def self.parameters
|
19
|
+
{ alpha: 1.0,
|
20
|
+
blend: :none,
|
21
|
+
mask: nil,
|
22
|
+
}
|
23
|
+
end
|
31
24
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end
|
25
|
+
def self.expanding_parameters
|
26
|
+
parameters.keys # all of them are expandable
|
27
|
+
end
|
36
28
|
|
37
|
-
|
38
|
-
|
39
|
-
|
29
|
+
def self.params_with_units
|
30
|
+
[]
|
31
|
+
end
|
40
32
|
|
33
|
+
def validate_alpha(arg, _i)
|
34
|
+
raise 'alpha must respond to to_f' unless arg.respond_to? :to_f
|
35
|
+
arg.to_f
|
36
|
+
end
|
41
37
|
|
38
|
+
def validate_mask(arg, _i)
|
39
|
+
colorify(arg, @custom_colors)
|
42
40
|
end
|
41
|
+
|
43
42
|
end
|
44
43
|
end
|
data/lib/squib/args/paragraph.rb
CHANGED
@@ -1,116 +1,118 @@
|
|
1
1
|
require_relative '../constants'
|
2
2
|
require_relative 'arg_loader'
|
3
3
|
|
4
|
-
module Squib
|
5
|
-
# @api private
|
6
|
-
module Args
|
7
|
-
|
8
|
-
class Paragraph
|
9
|
-
include ArgLoader
|
10
|
-
|
11
|
-
|
12
|
-
def self.parameters
|
13
|
-
{ align: :left,
|
14
|
-
str: 'Hello, World!',
|
15
|
-
font: :use_set,
|
16
|
-
font_size: nil,
|
17
|
-
markup: false,
|
18
|
-
justify: false,
|
19
|
-
wrap: true,
|
20
|
-
ellipsize: :end,
|
21
|
-
spacing: nil,
|
22
|
-
valign: :top,
|
23
|
-
hint: :off
|
24
|
-
}
|
25
|
-
end
|
4
|
+
module Squib::Args
|
26
5
|
|
27
|
-
|
28
|
-
|
29
|
-
|
6
|
+
module_function def extract_para(opts, deck, dsl_method_defaults = {})
|
7
|
+
Paragraph.new(deck.font).extract!(opts, deck)
|
8
|
+
end
|
30
9
|
|
31
|
-
|
32
|
-
|
33
|
-
|
10
|
+
class Paragraph
|
11
|
+
include ArgLoader
|
12
|
+
|
13
|
+
def self.parameters
|
14
|
+
{ align: :left,
|
15
|
+
str: 'Hello, World!',
|
16
|
+
font: :use_set,
|
17
|
+
font_size: nil,
|
18
|
+
markup: false,
|
19
|
+
justify: false,
|
20
|
+
wrap: true,
|
21
|
+
ellipsize: :end,
|
22
|
+
spacing: nil,
|
23
|
+
valign: :top,
|
24
|
+
hint: :off
|
25
|
+
}
|
26
|
+
end
|
34
27
|
|
35
|
-
|
36
|
-
|
37
|
-
|
28
|
+
def self.expanding_parameters
|
29
|
+
parameters.keys # all of them
|
30
|
+
end
|
38
31
|
|
39
|
-
|
40
|
-
|
41
|
-
|
32
|
+
def self.params_with_units
|
33
|
+
[] # none of them
|
34
|
+
end
|
42
35
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
arg
|
47
|
-
end
|
36
|
+
def initialize(deck_font)
|
37
|
+
@deck_font = deck_font
|
38
|
+
end
|
48
39
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
Pango::Alignment::LEFT
|
53
|
-
when 'right'
|
54
|
-
Pango::Alignment::RIGHT
|
55
|
-
when 'center'
|
56
|
-
Pango::Alignment::CENTER
|
57
|
-
else
|
58
|
-
raise ArgumentError, 'align must be one of: center, left, right'
|
59
|
-
end
|
60
|
-
end
|
40
|
+
def validate_str(arg, _i)
|
41
|
+
arg.to_s
|
42
|
+
end
|
61
43
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
Pango::WrapMode::CHAR
|
68
|
-
when 'word_char', 'true'
|
69
|
-
Pango::WrapMode::WORD_CHAR
|
70
|
-
else
|
71
|
-
raise ArgumentError, 'wrap must be one of: word, char, word_char, true, or false'
|
72
|
-
end
|
73
|
-
end
|
44
|
+
def validate_font(arg, _i)
|
45
|
+
arg = @deck_font if arg == :use_set
|
46
|
+
arg = Squib::DEFAULT_FONT if arg == :default
|
47
|
+
arg
|
48
|
+
end
|
74
49
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
else
|
86
|
-
raise ArgumentError, 'ellipsize must be one of: none, start, middle, end, true, or false'
|
87
|
-
end
|
50
|
+
def validate_align(arg, _i)
|
51
|
+
case arg.to_s.downcase.strip
|
52
|
+
when 'left'
|
53
|
+
Pango::Alignment::LEFT
|
54
|
+
when 'right'
|
55
|
+
Pango::Alignment::RIGHT
|
56
|
+
when 'center'
|
57
|
+
Pango::Alignment::CENTER
|
58
|
+
else
|
59
|
+
raise ArgumentError, 'align must be one of: center, left, right'
|
88
60
|
end
|
61
|
+
end
|
89
62
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
63
|
+
def validate_wrap(arg, _i)
|
64
|
+
case arg.to_s.downcase.strip
|
65
|
+
when 'word'
|
66
|
+
Pango::WrapMode::WORD
|
67
|
+
when 'char', 'false'
|
68
|
+
Pango::WrapMode::CHAR
|
69
|
+
when 'word_char', 'true'
|
70
|
+
Pango::WrapMode::WORD_CHAR
|
71
|
+
else
|
72
|
+
raise ArgumentError, 'wrap must be one of: word, char, word_char, true, or false'
|
97
73
|
end
|
74
|
+
end
|
98
75
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
76
|
+
def validate_ellipsize(arg, _i)
|
77
|
+
case arg.to_s.downcase.strip
|
78
|
+
when 'none', 'false'
|
79
|
+
Pango::EllipsizeMode::NONE
|
80
|
+
when 'start'
|
81
|
+
Pango::EllipsizeMode::START
|
82
|
+
when 'middle'
|
83
|
+
Pango::EllipsizeMode::MIDDLE
|
84
|
+
when 'end', 'true'
|
85
|
+
Pango::EllipsizeMode::END
|
86
|
+
when 'autoscale'
|
87
|
+
:autoscale
|
88
|
+
else
|
89
|
+
raise ArgumentError, 'ellipsize must be one of: none, start, middle, end, true, false or autoscale'
|
103
90
|
end
|
91
|
+
end
|
104
92
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
93
|
+
def validate_justify(arg, _i)
|
94
|
+
case arg
|
95
|
+
when nil, true, false
|
96
|
+
arg
|
97
|
+
else
|
98
|
+
raise ArgumentError, 'justify must be one of: nil, true, or false'
|
111
99
|
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def validate_spacing(arg, _i)
|
103
|
+
return nil if arg.nil?
|
104
|
+
raise ArgumentError, 'spacing must be a number or nil' unless arg.respond_to? :to_f
|
105
|
+
arg.to_f * Pango::SCALE
|
106
|
+
end
|
112
107
|
|
108
|
+
def validate_valign(arg, _i)
|
109
|
+
if %w(top middle bottom).include? arg.to_s.downcase
|
110
|
+
arg.to_s.downcase
|
111
|
+
else
|
112
|
+
raise ArgumentError, 'valign must be one of: top, middle, bottom'
|
113
|
+
end
|
113
114
|
end
|
114
115
|
|
115
116
|
end
|
117
|
+
|
116
118
|
end
|
@@ -1,63 +1,65 @@
|
|
1
1
|
require_relative 'arg_loader'
|
2
2
|
require_relative 'dir_validator'
|
3
3
|
|
4
|
-
module Squib
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
include ArgLoader
|
9
|
-
include DirValidator
|
10
|
-
|
11
|
-
def initialize
|
12
|
-
end
|
4
|
+
module Squib::Args
|
5
|
+
module_function def extract_save_batch(opts, deck)
|
6
|
+
SaveBatch.new.extract! opts, deck
|
7
|
+
end
|
13
8
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
count_format: '%02d',
|
18
|
-
dir: '_output',
|
19
|
-
prefix: 'card_',
|
20
|
-
rotate: false,
|
21
|
-
trim_radius: 0,
|
22
|
-
trim: 0,
|
23
|
-
}
|
24
|
-
end
|
9
|
+
class SaveBatch
|
10
|
+
include ArgLoader
|
11
|
+
include DirValidator
|
25
12
|
|
26
|
-
|
27
|
-
|
28
|
-
end
|
13
|
+
def initialize
|
14
|
+
end
|
29
15
|
|
30
|
-
|
31
|
-
|
32
|
-
|
16
|
+
def self.parameters
|
17
|
+
{
|
18
|
+
angle: 0,
|
19
|
+
count_format: '%02d',
|
20
|
+
dir: '_output',
|
21
|
+
prefix: 'card_',
|
22
|
+
rotate: false,
|
23
|
+
suffix: '',
|
24
|
+
trim_radius: 0,
|
25
|
+
trim: 0,
|
26
|
+
}
|
27
|
+
end
|
33
28
|
|
34
|
-
|
35
|
-
|
36
|
-
|
29
|
+
def self.expanding_parameters
|
30
|
+
self.parameters.keys # all of them
|
31
|
+
end
|
37
32
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
angle[i] = 0.5 * Math::PI
|
42
|
-
return true
|
43
|
-
when :counterclockwise
|
44
|
-
angle[i] = 1.5 * Math::PI
|
45
|
-
return true
|
46
|
-
when false
|
47
|
-
false
|
48
|
-
else
|
49
|
-
raise 'invalid option to rotate: only [true, false, :clockwise, :counterclockwise]'
|
50
|
-
end
|
51
|
-
end
|
33
|
+
def self.params_with_units
|
34
|
+
[:trim, :trim_radius]
|
35
|
+
end
|
52
36
|
|
53
|
-
|
54
|
-
|
55
|
-
|
37
|
+
def validate_dir(arg, _i)
|
38
|
+
ensure_dir_created(arg)
|
39
|
+
end
|
56
40
|
|
57
|
-
|
58
|
-
|
41
|
+
def validate_rotate(arg, i)
|
42
|
+
case arg
|
43
|
+
when true, :clockwise
|
44
|
+
angle[i] = 0.5 * Math::PI
|
45
|
+
return true
|
46
|
+
when :counterclockwise
|
47
|
+
angle[i] = 1.5 * Math::PI
|
48
|
+
return true
|
49
|
+
when false
|
50
|
+
false
|
51
|
+
else
|
52
|
+
raise 'invalid option to rotate: only [true, false, :clockwise, :counterclockwise]'
|
59
53
|
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def full_filename(i)
|
57
|
+
"#{dir[i]}/#{prefix[i]}#{count_format[i] % i}#{suffix[i]}.png"
|
58
|
+
end
|
60
59
|
|
60
|
+
def summary
|
61
|
+
"#{dir[0]}/#{prefix[0]}_*#{suffix[0]}"
|
61
62
|
end
|
63
|
+
|
62
64
|
end
|
63
65
|
end
|