at_coder_friends 0.6.8 → 0.7.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 +4 -4
- data/.gitattributes +1 -0
- data/.github/workflows/codeql-analysis.yml +70 -0
- data/.github/workflows/ruby.yml +38 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +8 -1
- data/CHANGELOG.md +23 -4
- data/Gemfile.lock +43 -43
- data/README.md +15 -92
- data/at_coder_friends.gemspec +4 -3
- data/lib/at_coder_friends/cli.rb +13 -28
- data/lib/at_coder_friends/config_loader.rb +2 -4
- data/lib/at_coder_friends/generator/any_builtin.rb +22 -0
- data/lib/at_coder_friends/generator/base.rb +55 -6
- data/lib/at_coder_friends/generator/c_builtin.rb +22 -0
- data/lib/at_coder_friends/generator/cxx_builtin.rb +9 -241
- data/lib/at_coder_friends/generator/fragment.rb +106 -0
- data/lib/at_coder_friends/generator/main.rb +20 -14
- data/lib/at_coder_friends/generator/ruby_builtin.rb +13 -166
- data/lib/at_coder_friends/parser/binary.rb +12 -5
- data/lib/at_coder_friends/parser/constraints.rb +8 -8
- data/lib/at_coder_friends/parser/input_format.rb +19 -15
- data/lib/at_coder_friends/parser/modulo.rb +5 -7
- data/lib/at_coder_friends/parser/section_wrapper.rb +3 -5
- data/lib/at_coder_friends/parser/sections.rb +2 -2
- data/lib/at_coder_friends/path_util.rb +1 -1
- data/lib/at_coder_friends/problem.rb +5 -5
- data/lib/at_coder_friends/scraping/agent.rb +1 -1
- data/lib/at_coder_friends/scraping/authentication.rb +3 -3
- data/lib/at_coder_friends/scraping/session.rb +1 -1
- data/lib/at_coder_friends/scraping/tasks.rb +6 -8
- data/lib/at_coder_friends/test_runner/base.rb +1 -2
- data/lib/at_coder_friends/test_runner/judge.rb +3 -3
- data/lib/at_coder_friends/version.rb +1 -1
- data/lib/at_coder_friends.rb +3 -0
- data/templates/any_builtin.md.erb +30 -0
- data/templates/any_builtin_fragments.yml +5 -0
- data/templates/c_builtin.c.erb +36 -0
- data/templates/c_builtin_fragments.yml +127 -0
- data/templates/csharp_sample.cs.erb +29 -0
- data/templates/csharp_sample_fragments.yml +159 -0
- data/templates/java_sample.java.erb +25 -0
- data/templates/java_sample_fragments.yml +98 -0
- data/templates/ruby_builtin_fragments.yml +93 -0
- metadata +26 -6
- data/.travis.yml +0 -16
- data/docs/CONFIGURATION.md +0 -421
@@ -1,13 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'yaml'
|
3
4
|
require 'erb'
|
4
5
|
|
5
6
|
module AtCoderFriends
|
6
7
|
module Generator
|
7
|
-
Attributes = Struct.new(:file_ext, :
|
8
|
+
Attributes = Struct.new(:file_ext, :template, :fragments)
|
8
9
|
|
9
10
|
# common behavior of generators
|
10
11
|
class Base
|
12
|
+
ACF_HOME = File.realpath(File.join(__dir__, '..', '..', '..'))
|
13
|
+
TMPL_DIR = File.join(ACF_HOME, 'templates')
|
14
|
+
|
11
15
|
attr_reader :cfg, :pbm
|
12
16
|
|
13
17
|
def initialize(cfg = nil)
|
@@ -15,21 +19,36 @@ module AtCoderFriends
|
|
15
19
|
end
|
16
20
|
|
17
21
|
def process(pbm)
|
18
|
-
pbm.add_src(
|
22
|
+
pbm.add_src(config_file_ext, generate(pbm))
|
19
23
|
end
|
20
24
|
|
21
25
|
def generate(pbm)
|
22
26
|
@pbm = pbm
|
23
|
-
|
24
|
-
src = ERB.new(
|
27
|
+
template = File.read(config_template)
|
28
|
+
src = ERB.new(template, trim_mode: '-').result(binding)
|
25
29
|
src = render(src) if respond_to?(:render)
|
26
30
|
src
|
27
31
|
end
|
28
32
|
|
29
|
-
def
|
30
|
-
|
33
|
+
def fragments
|
34
|
+
@fragments ||= YAML.load_file(config_fragments)
|
35
|
+
end
|
36
|
+
|
37
|
+
def config_file_ext
|
38
|
+
cfg['file_ext']&.to_sym || attrs.file_ext
|
31
39
|
end
|
32
40
|
|
41
|
+
def config_template
|
42
|
+
template = cfg['template'] || cfg['default_template'] || attrs.template
|
43
|
+
template.sub(/\A@/, TMPL_DIR)
|
44
|
+
end
|
45
|
+
|
46
|
+
def config_fragments
|
47
|
+
fragments = cfg['fragments'] || attrs.fragments
|
48
|
+
fragments.sub(/\A@/, TMPL_DIR)
|
49
|
+
end
|
50
|
+
|
51
|
+
# deprecated, use ERB syntax
|
33
52
|
def embed_lines(src, pat, lines)
|
34
53
|
re = Regexp.escape(pat)
|
35
54
|
src.gsub(
|
@@ -38,5 +57,35 @@ module AtCoderFriends
|
|
38
57
|
)
|
39
58
|
end
|
40
59
|
end
|
60
|
+
|
61
|
+
module ConstFragmentMixin
|
62
|
+
def gen_consts
|
63
|
+
pbm.constants.map { |c| gen_const(c) }
|
64
|
+
end
|
65
|
+
|
66
|
+
def gen_const(c)
|
67
|
+
ConstFragment.new(c, fragments['constant']).generate
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
module DeclFragmentMixin
|
72
|
+
def gen_decls
|
73
|
+
pbm.formats.map { |inpdef| gen_decl(inpdef).split("\n") }.flatten
|
74
|
+
end
|
75
|
+
|
76
|
+
def gen_decl(inpdef)
|
77
|
+
InputFormatFragment.new(inpdef, fragments['declaration']).generate
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
module InputFragmentMixin
|
82
|
+
def gen_inputs
|
83
|
+
pbm.formats.map { |inpdef| gen_input(inpdef).split("\n") }.flatten
|
84
|
+
end
|
85
|
+
|
86
|
+
def gen_input(inpdef)
|
87
|
+
InputFormatFragment.new(inpdef, fragments['input']).generate
|
88
|
+
end
|
89
|
+
end
|
41
90
|
end
|
42
91
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AtCoderFriends
|
4
|
+
module Generator
|
5
|
+
# generates C source from problem description
|
6
|
+
class CBuiltin < Base
|
7
|
+
include ConstFragmentMixin
|
8
|
+
include DeclFragmentMixin
|
9
|
+
include InputFragmentMixin
|
10
|
+
|
11
|
+
ACF_HOME = File.realpath(File.join(__dir__, '..', '..', '..'))
|
12
|
+
TMPL_DIR = File.join(ACF_HOME, 'templates')
|
13
|
+
TEMPLATE = File.join(TMPL_DIR, 'c_builtin.c.erb')
|
14
|
+
FRAGMENTS = File.join(TMPL_DIR, 'c_builtin_fragments.yml')
|
15
|
+
ATTRS = Attributes.new(:c, TEMPLATE, FRAGMENTS)
|
16
|
+
|
17
|
+
def attrs
|
18
|
+
ATTRS
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -2,256 +2,24 @@
|
|
2
2
|
|
3
3
|
module AtCoderFriends
|
4
4
|
module Generator
|
5
|
-
# generates C++ constants
|
6
|
-
module CxxBuiltinConstGen
|
7
|
-
def gen_const(c)
|
8
|
-
v = cnv_const_value(c.value)
|
9
|
-
if c.type == :max
|
10
|
-
"const int #{c.name.upcase}_MAX = #{v};"
|
11
|
-
else
|
12
|
-
"const int MOD = #{v};"
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def cnv_const_value(v)
|
17
|
-
v
|
18
|
-
.sub(/\b10\^/, '1e')
|
19
|
-
.sub(/\b2\^/, '1<<')
|
20
|
-
.gsub(',', "'")
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
# generates C++ variable declarations
|
25
|
-
module CxxBuiltinDeclGen
|
26
|
-
TYPE_TBL = {
|
27
|
-
number: 'int',
|
28
|
-
decimal: 'double',
|
29
|
-
string: 'char',
|
30
|
-
char: 'char'
|
31
|
-
}.tap { |h| h.default = 'int' }
|
32
|
-
def gen_decl(inpdef)
|
33
|
-
if inpdef.components
|
34
|
-
inpdef.components.map { |cmp| gen_decl(cmp) }
|
35
|
-
else
|
36
|
-
case inpdef.container
|
37
|
-
when :single
|
38
|
-
gen_single_decl(inpdef)
|
39
|
-
when :harray
|
40
|
-
gen_harray_decl(inpdef)
|
41
|
-
when :varray
|
42
|
-
gen_varray_decl(inpdef)
|
43
|
-
when :matrix, :vmatrix, :hmatrix
|
44
|
-
gen_matrix_decl(inpdef)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def gen_single_decl(inpdef)
|
50
|
-
names, cols = inpdef.vars.transpose
|
51
|
-
if cols.uniq.size == 1 && cols[0] != :string
|
52
|
-
type = TYPE_TBL[cols[0]]
|
53
|
-
dcl = names.join(', ')
|
54
|
-
"#{type} #{dcl};"
|
55
|
-
else
|
56
|
-
inpdef.vars.map do |v, item|
|
57
|
-
type = TYPE_TBL[item]
|
58
|
-
dcl = v
|
59
|
-
dcl += "[#{v.upcase}_MAX + 1]" if item == :string
|
60
|
-
"#{type} #{dcl};"
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
def gen_harray_decl(inpdef)
|
66
|
-
type = TYPE_TBL[inpdef.item]
|
67
|
-
v = inpdef.names[0]
|
68
|
-
sz = gen_arr_size(inpdef.size)[0]
|
69
|
-
case inpdef.item
|
70
|
-
when :number, :decimal
|
71
|
-
"#{type} #{v}[#{sz}];"
|
72
|
-
when :string
|
73
|
-
"#{type} #{v}[#{sz}][#{v.upcase}_MAX + 1];"
|
74
|
-
when :char
|
75
|
-
"#{type} #{v}[#{sz} + 1];"
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def gen_varray_decl(inpdef)
|
80
|
-
sz = gen_arr_size(inpdef.size)[0]
|
81
|
-
inpdef.vars.map do |v, item|
|
82
|
-
type = TYPE_TBL[item]
|
83
|
-
dcl = "#{v}[#{sz}]"
|
84
|
-
dcl += "[#{v.upcase}_MAX + 1]" if item == :string
|
85
|
-
"#{type} #{dcl};"
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
def gen_matrix_decl(inpdef)
|
90
|
-
sz1, sz2 = gen_arr_size(inpdef.size)
|
91
|
-
inpdef.vars.map do |v, item|
|
92
|
-
type = TYPE_TBL[item]
|
93
|
-
dcl = "#{v}[#{sz1}]"
|
94
|
-
dcl += item == :char ? "[#{sz2} + 1]" : "[#{sz2}]"
|
95
|
-
dcl += "[#{v.upcase}_MAX + 1]" if item == :string
|
96
|
-
"#{type} #{dcl};"
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def gen_arr_size(szs)
|
101
|
-
szs.map { |sz| sz.gsub(/([a-z][a-z0-9_]*)/i, '\1_MAX').upcase }
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
# generates C++ input source
|
106
|
-
module CxxBuiltinInputGen
|
107
|
-
SCANF_FMTS = [
|
108
|
-
'scanf("%<fmt>s", %<addr>s);',
|
109
|
-
'REP(i, %<sz1>s) scanf("%<fmt>s", %<addr>s);',
|
110
|
-
'REP(i, %<sz1>s) REP(j, %<sz2>s) scanf("%<fmt>s", %<addr>s);'
|
111
|
-
].freeze
|
112
|
-
SCANF_FMTS_CMB = {
|
113
|
-
varray_matrix:
|
114
|
-
[
|
115
|
-
<<~TEXT,
|
116
|
-
REP(i, %<sz1>s) {
|
117
|
-
scanf("%<fmt1>s", %<addr1>s);
|
118
|
-
scanf("%<fmt2>s", %<addr2>s);
|
119
|
-
}
|
120
|
-
TEXT
|
121
|
-
<<~TEXT
|
122
|
-
REP(i, %<sz1>s) {
|
123
|
-
scanf("%<fmt1>s", %<addr1>s);
|
124
|
-
REP(j, %<sz2>s[i]) scanf("%<fmt2>s", %<addr2>s);
|
125
|
-
}
|
126
|
-
TEXT
|
127
|
-
],
|
128
|
-
matrix_varray:
|
129
|
-
[
|
130
|
-
<<~TEXT,
|
131
|
-
REP(i, %<sz1>s) {
|
132
|
-
scanf("%<fmt1>s", %<addr1>s);
|
133
|
-
scanf("%<fmt2>s", %<addr2>s);
|
134
|
-
}
|
135
|
-
TEXT
|
136
|
-
<<~TEXT
|
137
|
-
REP(i, %<sz1>s) {
|
138
|
-
REP(j, %<sz2>s) scanf("%<fmt1>s", %<addr1>s);
|
139
|
-
scanf("%<fmt2>s", %<addr2>s);
|
140
|
-
}
|
141
|
-
TEXT
|
142
|
-
]
|
143
|
-
}.tap { |h| h.default = h[:varray_matrix] }
|
144
|
-
FMT_FMTS = {
|
145
|
-
number: '%d',
|
146
|
-
decimal: '%lf',
|
147
|
-
string: '%s',
|
148
|
-
char: '%s'
|
149
|
-
}.tap { |h| h.default = h[:number] }
|
150
|
-
SINGLE_ADDR_FMTS = {
|
151
|
-
number: '&%<v>s',
|
152
|
-
decimal: '&%<v>s',
|
153
|
-
string: '%<v>s'
|
154
|
-
}.tap { |h| h.default = h[:number] }
|
155
|
-
ARRAY_ADDR_FMTS = {
|
156
|
-
number: '%<v>s + i',
|
157
|
-
decimal: '%<v>s + i',
|
158
|
-
string: '%<v>s[i]',
|
159
|
-
char: '%<v>s'
|
160
|
-
}.tap { |h| h.default = h[:number] }
|
161
|
-
MATRIX_ADDR_FMTS = {
|
162
|
-
number: '&%<v>s[i][j]',
|
163
|
-
decimal: '&%<v>s[i][j]',
|
164
|
-
string: '%<v>s[i][j]',
|
165
|
-
char: '%<v>s[i]'
|
166
|
-
}.tap { |h| h.default = h[:number] }
|
167
|
-
ADDR_FMTS = {
|
168
|
-
single: SINGLE_ADDR_FMTS,
|
169
|
-
harray: ARRAY_ADDR_FMTS,
|
170
|
-
varray: ARRAY_ADDR_FMTS,
|
171
|
-
matrix: MATRIX_ADDR_FMTS,
|
172
|
-
vmatrix: MATRIX_ADDR_FMTS,
|
173
|
-
hmatrix: MATRIX_ADDR_FMTS
|
174
|
-
}.tap { |h| h.default = h[:single] }
|
175
|
-
|
176
|
-
def gen_input(inpdef)
|
177
|
-
if inpdef.components
|
178
|
-
gen_cmb_input(inpdef)
|
179
|
-
else
|
180
|
-
gen_plain_input(inpdef)
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
def gen_plain_input(inpdef)
|
185
|
-
scanf = SCANF_FMTS[inpdef.size.size - (inpdef.item == :char ? 1 : 0)]
|
186
|
-
sz1, sz2 = inpdef.size
|
187
|
-
fmt, addr = scanf_params(inpdef)
|
188
|
-
format(scanf, sz1: sz1, sz2: sz2, fmt: fmt, addr: addr)
|
189
|
-
end
|
190
|
-
|
191
|
-
def gen_cmb_input(inpdef)
|
192
|
-
scanf = SCANF_FMTS_CMB.dig(
|
193
|
-
inpdef.container, inpdef.item == :char ? 0 : 1
|
194
|
-
)
|
195
|
-
sz1 = inpdef.size[0]
|
196
|
-
sz2 = inpdef.size[1].split('_')[0]
|
197
|
-
fmt1, addr1, fmt2, addr2 =
|
198
|
-
inpdef.components.map { |cmp| scanf_params(cmp) }.flatten
|
199
|
-
format(
|
200
|
-
scanf,
|
201
|
-
sz1: sz1, sz2: sz2,
|
202
|
-
fmt1: fmt1, addr1: addr1,
|
203
|
-
fmt2: fmt2, addr2: addr2
|
204
|
-
).split("\n")
|
205
|
-
end
|
206
|
-
|
207
|
-
def scanf_params(inpdef)
|
208
|
-
[scanf_fmt(inpdef), scanf_addr(inpdef)]
|
209
|
-
end
|
210
|
-
|
211
|
-
def scanf_fmt(inpdef)
|
212
|
-
inpdef.vars.map { |(_v, item)| FMT_FMTS[item] }.join
|
213
|
-
end
|
214
|
-
|
215
|
-
def scanf_addr(inpdef)
|
216
|
-
inpdef.vars.map do |(v, item)|
|
217
|
-
addr_fmt = ADDR_FMTS.dig(inpdef.container, item)
|
218
|
-
format(addr_fmt, v: v)
|
219
|
-
end.join(', ')
|
220
|
-
end
|
221
|
-
end
|
222
|
-
|
223
5
|
# generates C++ source from problem description
|
224
|
-
class CxxBuiltin <
|
225
|
-
|
226
|
-
|
227
|
-
include CxxBuiltinInputGen
|
228
|
-
|
229
|
-
ACF_HOME = File.realpath(File.join(__dir__, '..', '..', '..'))
|
230
|
-
TMPL_DIR = File.join(ACF_HOME, 'templates')
|
231
|
-
DEFAULT_TMPL = File.join(TMPL_DIR, 'cxx_builtin.cxx.erb')
|
232
|
-
ATTRS = Attributes.new(:cxx, DEFAULT_TMPL)
|
6
|
+
class CxxBuiltin < CBuiltin
|
7
|
+
TEMPLATE = File.join(TMPL_DIR, 'cxx_builtin.cxx.erb')
|
8
|
+
ATTRS = Attributes.new(:cxx, TEMPLATE, FRAGMENTS)
|
233
9
|
|
234
10
|
def attrs
|
235
11
|
ATTRS
|
236
12
|
end
|
237
13
|
|
14
|
+
def gen_const(c)
|
15
|
+
ConstFragment.new(c, fragments['cxx_constant']).generate
|
16
|
+
end
|
17
|
+
|
18
|
+
# deprecated, use ERB syntax
|
238
19
|
def render(src)
|
239
20
|
src = embed_lines(src, '/*** CONSTS ***/', gen_consts)
|
240
21
|
src = embed_lines(src, '/*** DCLS ***/', gen_decls)
|
241
|
-
|
242
|
-
src
|
243
|
-
end
|
244
|
-
|
245
|
-
def gen_consts(constants = pbm.constants)
|
246
|
-
constants.map { |c| gen_const(c) }
|
247
|
-
end
|
248
|
-
|
249
|
-
def gen_decls(inpdefs = pbm.formats)
|
250
|
-
inpdefs.map { |inpdef| gen_decl(inpdef) }.flatten
|
251
|
-
end
|
252
|
-
|
253
|
-
def gen_inputs(inpdefs = pbm.formats)
|
254
|
-
inpdefs.map { |inpdef| gen_input(inpdef) }.flatten
|
22
|
+
embed_lines(src, '/*** INPUTS ***/', gen_inputs)
|
255
23
|
end
|
256
24
|
end
|
257
25
|
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'erb'
|
4
|
+
|
5
|
+
module AtCoderFriends
|
6
|
+
module Generator
|
7
|
+
# base class for code fragment generator
|
8
|
+
class FragmentBase
|
9
|
+
attr_reader :templates
|
10
|
+
|
11
|
+
def initialize(obj, templates)
|
12
|
+
@obj = obj
|
13
|
+
@templates = templates
|
14
|
+
end
|
15
|
+
|
16
|
+
def render(*keys)
|
17
|
+
keys = keys.map(&:to_s)
|
18
|
+
template = templates.dig(*keys) || (raise AppError, "fragment key #{keys} not found")
|
19
|
+
|
20
|
+
return ERB.new(template, trim_mode: '-').result(binding) if template.is_a?(String)
|
21
|
+
|
22
|
+
if template.is_a?(Hash)
|
23
|
+
sub_key_props = template['__key'] || (raise AppError, "'__key' not found in fragment hash #{keys}")
|
24
|
+
sub_keys = sub_key_props.map { |k| send(k) }
|
25
|
+
return render(*keys, *sub_keys)
|
26
|
+
end
|
27
|
+
|
28
|
+
raise AppError, "can't render fragment #{keys}"
|
29
|
+
end
|
30
|
+
|
31
|
+
# delegate method calls to obj
|
32
|
+
def method_missing(name, *args, &block)
|
33
|
+
if @obj.respond_to?(name)
|
34
|
+
@obj.send(name, *args, &block)
|
35
|
+
elsif templates.key?(name.to_s)
|
36
|
+
render(name)
|
37
|
+
else
|
38
|
+
super
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def respond_to_missing?(name, include_private = false)
|
43
|
+
@obj.respond_to?(name, include_private) ||
|
44
|
+
templates.key?(name.to_s) ||
|
45
|
+
super
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
@obj.to_s
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# base class for constant declaration generator
|
54
|
+
class ConstFragment < FragmentBase
|
55
|
+
def generate
|
56
|
+
render(type)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# base class for variable declaration generator
|
61
|
+
class InputFormatFragment < FragmentBase
|
62
|
+
def generate
|
63
|
+
render(:main)
|
64
|
+
end
|
65
|
+
|
66
|
+
def vs
|
67
|
+
names
|
68
|
+
end
|
69
|
+
|
70
|
+
def v
|
71
|
+
vs[0]
|
72
|
+
end
|
73
|
+
|
74
|
+
def sz1
|
75
|
+
size[0]
|
76
|
+
end
|
77
|
+
alias sz sz1
|
78
|
+
|
79
|
+
def sz2
|
80
|
+
size[1]
|
81
|
+
end
|
82
|
+
|
83
|
+
def delims
|
84
|
+
delim.chars
|
85
|
+
end
|
86
|
+
|
87
|
+
def vars
|
88
|
+
@vars ||= super.map do |v, item|
|
89
|
+
var = Problem::InputFormat.new(
|
90
|
+
container: container,
|
91
|
+
names: [v],
|
92
|
+
item: item,
|
93
|
+
size: size
|
94
|
+
)
|
95
|
+
self.class.new(var, templates)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def components
|
100
|
+
@components ||= super&.map do |cmp|
|
101
|
+
self.class.new(cmp, templates)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -12,34 +12,40 @@ module AtCoderFriends
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def process(pbm)
|
15
|
-
generators = ctx.config
|
15
|
+
generators = ctx.config['generators'] || []
|
16
16
|
generators.each do |gen_name|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
puts e.backtrace
|
24
|
-
end
|
17
|
+
gen_obj = load_obj(gen_name)
|
18
|
+
gen_obj.process(pbm)
|
19
|
+
rescue StandardError => e
|
20
|
+
puts "an error occurred in generator:#{gen_name}."
|
21
|
+
puts e
|
22
|
+
puts e.backtrace
|
25
23
|
end
|
26
24
|
end
|
27
25
|
|
28
26
|
def load_obj(gen_name)
|
29
27
|
@cache[gen_name] ||= begin
|
30
|
-
|
28
|
+
cls_name = gen_name.split('_')[0]
|
29
|
+
gen_class = load_class(cls_name)
|
31
30
|
gen_cnf = config_for(gen_name)
|
32
31
|
gen_class.new(gen_cnf)
|
33
32
|
end
|
34
33
|
end
|
35
34
|
|
36
35
|
def load_class(gen_name)
|
37
|
-
|
38
|
-
|
39
|
-
end
|
36
|
+
snake_gen_name = to_snake(gen_name)
|
37
|
+
require "at_coder_friends/generator/#{snake_gen_name}" unless AtCoderFriends::Generator.const_defined?(gen_name)
|
40
38
|
AtCoderFriends::Generator.const_get(gen_name)
|
41
39
|
rescue LoadError
|
42
|
-
raise AppError,
|
40
|
+
raise AppError, <<~MSG
|
41
|
+
Error: Failed to load plugin.
|
42
|
+
The '#{gen_name}' plugin could not be found. To use this plugin, please install the required gem by following these steps:
|
43
|
+
|
44
|
+
1. Open a terminal or command prompt.
|
45
|
+
2. Run the following command:
|
46
|
+
gem install at_coder_friends-generator-#{snake_gen_name}
|
47
|
+
3. Once the above command completes, please run the program again.
|
48
|
+
MSG
|
43
49
|
end
|
44
50
|
|
45
51
|
def config_for(gen_name)
|