errbit_plugin 0.6.0 → 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 +5 -5
- data/.envrc +1 -0
- data/.github/workflows/jruby.yml +37 -0
- data/.github/workflows/rspec.yml +35 -0
- data/.gitignore +2 -2
- data/.rspec +4 -0
- data/.rubocop.yml +367 -0
- data/.ruby-version +1 -0
- data/.standard.yml +1 -0
- data/Gemfile +12 -5
- data/Gemfile.lock +102 -0
- data/README.md +9 -3
- data/Rakefile +2 -0
- data/errbit_plugin.gemspec +20 -18
- data/lib/errbit_plugin/issue_tracker.rb +2 -0
- data/lib/errbit_plugin/{validate_issue_tracker.rb → issue_tracker_validator.rb} +7 -5
- data/lib/errbit_plugin/none_issue_tracker.rb +57 -0
- data/lib/errbit_plugin/registry.rb +6 -3
- data/lib/errbit_plugin/version.rb +3 -1
- data/lib/errbit_plugin.rb +4 -2
- data/spec/errbit_plugin/issue_tracker_validator_spec.rb +517 -0
- data/spec/errbit_plugin/none_issue_tracker_spec.rb +31 -0
- data/spec/errbit_plugin/registry_spec.rb +17 -15
- data/spec/spec_helper.rb +19 -20
- metadata +24 -50
- data/.coveralls.yml +0 -2
- data/.travis.yml +0 -15
- data/Guardfile +0 -8
- data/lib/errbit_plugin/issue_trackers/none.rb +0 -31
- data/spec/errbit_plugin/validate_issue_tracker_spec.rb +0 -255
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ErrbitPlugin
|
2
|
-
class
|
4
|
+
class IssueTrackerValidator
|
3
5
|
def initialize(klass)
|
4
6
|
@klass = klass
|
5
7
|
@errors = []
|
@@ -17,11 +19,11 @@ module ErrbitPlugin
|
|
17
19
|
private
|
18
20
|
|
19
21
|
def good_inherit?
|
20
|
-
|
22
|
+
if @klass.ancestors.include?(ErrbitPlugin::IssueTracker)
|
23
|
+
true
|
24
|
+
else
|
21
25
|
add_errors(:not_inherited)
|
22
26
|
false
|
23
|
-
else
|
24
|
-
true
|
25
27
|
end
|
26
28
|
end
|
27
29
|
|
@@ -55,7 +57,7 @@ module ErrbitPlugin
|
|
55
57
|
@instance ||= @klass.new({})
|
56
58
|
end
|
57
59
|
|
58
|
-
def add_errors(key, value=nil)
|
60
|
+
def add_errors(key, value = nil)
|
59
61
|
@errors << [key, value].compact
|
60
62
|
end
|
61
63
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ErrbitPlugin
|
4
|
+
class NoneIssueTracker < IssueTracker
|
5
|
+
def self.label
|
6
|
+
"none"
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.note
|
10
|
+
"When no issue tracker has been configured, you will be able to " \
|
11
|
+
"leave comments on errors."
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.fields
|
15
|
+
{}
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.icons
|
19
|
+
@icons ||= {
|
20
|
+
create: ["image/png", read_static_file("none_create.png")],
|
21
|
+
goto: ["image/png", read_static_file("none_create.png")],
|
22
|
+
inactive: ["image/png", read_static_file("none_inactive.png")]
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.read_static_file(file)
|
27
|
+
File.read(File.expand_path(File.join(
|
28
|
+
File.dirname(__FILE__), "..", "..", "static", file
|
29
|
+
)))
|
30
|
+
end
|
31
|
+
|
32
|
+
##
|
33
|
+
# The NoneIssueTracker is mark like configured? false because it not valid
|
34
|
+
# like a real IssueTracker
|
35
|
+
def configured?
|
36
|
+
false
|
37
|
+
end
|
38
|
+
|
39
|
+
def errors
|
40
|
+
{}
|
41
|
+
end
|
42
|
+
|
43
|
+
def url
|
44
|
+
""
|
45
|
+
end
|
46
|
+
|
47
|
+
def create_issue(*)
|
48
|
+
false
|
49
|
+
end
|
50
|
+
|
51
|
+
def close_issue(*)
|
52
|
+
false
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
ErrbitPlugin::Registry.add_issue_tracker(ErrbitPlugin::NoneIssueTracker)
|
@@ -1,5 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ErrbitPlugin
|
2
4
|
class IncompatibilityError < StandardError; end
|
5
|
+
|
3
6
|
class AlreadyRegisteredError < StandardError; end
|
4
7
|
|
5
8
|
module Registry
|
@@ -13,12 +16,12 @@ module ErrbitPlugin
|
|
13
16
|
"issue_tracker '#{key}' already registered"
|
14
17
|
end
|
15
18
|
|
16
|
-
|
19
|
+
validator = IssueTrackerValidator.new(klass)
|
17
20
|
|
18
|
-
if
|
21
|
+
if validator.valid?
|
19
22
|
@issue_trackers[key] = klass
|
20
23
|
else
|
21
|
-
raise IncompatibilityError.new(
|
24
|
+
raise IncompatibilityError.new(validator.errors.join("; "))
|
22
25
|
end
|
23
26
|
end
|
24
27
|
|
data/lib/errbit_plugin.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "errbit_plugin/version"
|
2
4
|
require "errbit_plugin/registry"
|
3
5
|
require "errbit_plugin/issue_tracker"
|
4
|
-
require "errbit_plugin/
|
5
|
-
require "errbit_plugin/
|
6
|
+
require "errbit_plugin/issue_tracker_validator"
|
7
|
+
require "errbit_plugin/none_issue_tracker"
|
@@ -0,0 +1,517 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
RSpec.describe ErrbitPlugin::IssueTrackerValidator do
|
6
|
+
describe "#valid?" do
|
7
|
+
context "with a complete class" do
|
8
|
+
klass = Class.new(ErrbitPlugin::IssueTracker) do
|
9
|
+
def self.label
|
10
|
+
"foo"
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.note
|
14
|
+
"foo"
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.fields
|
18
|
+
["foo"]
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.icons
|
22
|
+
{}
|
23
|
+
end
|
24
|
+
|
25
|
+
def configured?
|
26
|
+
true
|
27
|
+
end
|
28
|
+
|
29
|
+
def errors
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
33
|
+
def create_issue
|
34
|
+
"http"
|
35
|
+
end
|
36
|
+
|
37
|
+
def close_issue
|
38
|
+
"http"
|
39
|
+
end
|
40
|
+
|
41
|
+
def url
|
42
|
+
"http"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it "valid" do
|
47
|
+
expect(ErrbitPlugin::IssueTrackerValidator.new(klass).valid?).to be true
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "with class not inherit from ErrbitPlugin::IssueTracker" do
|
52
|
+
klass = Class.new do
|
53
|
+
def self.label
|
54
|
+
"foo"
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.note
|
58
|
+
"foo"
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.fields
|
62
|
+
["foo"]
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.icons
|
66
|
+
{}
|
67
|
+
end
|
68
|
+
|
69
|
+
def initialize(params)
|
70
|
+
end
|
71
|
+
|
72
|
+
def configured?
|
73
|
+
true
|
74
|
+
end
|
75
|
+
|
76
|
+
def errors
|
77
|
+
true
|
78
|
+
end
|
79
|
+
|
80
|
+
def create_issue
|
81
|
+
"http"
|
82
|
+
end
|
83
|
+
|
84
|
+
def close_issue
|
85
|
+
"http"
|
86
|
+
end
|
87
|
+
|
88
|
+
def url
|
89
|
+
"http"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
it "not valid" do
|
94
|
+
expect(ErrbitPlugin::IssueTrackerValidator.new(klass).valid?).to be false
|
95
|
+
end
|
96
|
+
|
97
|
+
it "says :not_inherited" do
|
98
|
+
is = ErrbitPlugin::IssueTrackerValidator.new(klass)
|
99
|
+
is.valid?
|
100
|
+
expect(is.errors).to eql [[:not_inherited]]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
context "with no label method" do
|
105
|
+
klass = Class.new(ErrbitPlugin::IssueTracker) do
|
106
|
+
def self.note
|
107
|
+
"foo"
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.fields
|
111
|
+
["foo"]
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.icons
|
115
|
+
{}
|
116
|
+
end
|
117
|
+
|
118
|
+
def configured?
|
119
|
+
true
|
120
|
+
end
|
121
|
+
|
122
|
+
def errors
|
123
|
+
true
|
124
|
+
end
|
125
|
+
|
126
|
+
def create_issue
|
127
|
+
"http"
|
128
|
+
end
|
129
|
+
|
130
|
+
def close_issue
|
131
|
+
"http"
|
132
|
+
end
|
133
|
+
|
134
|
+
def url
|
135
|
+
"http"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
it "not valid" do
|
140
|
+
expect(ErrbitPlugin::IssueTrackerValidator.new(klass).valid?).to be false
|
141
|
+
end
|
142
|
+
|
143
|
+
it "say not implement label method" do
|
144
|
+
is = ErrbitPlugin::IssueTrackerValidator.new(klass)
|
145
|
+
is.valid?
|
146
|
+
expect(is.errors).to eql [[:class_method_missing, :label]]
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
context "with no icons method" do
|
151
|
+
klass = Class.new(ErrbitPlugin::IssueTracker) do
|
152
|
+
def self.note
|
153
|
+
"foo"
|
154
|
+
end
|
155
|
+
|
156
|
+
def self.fields
|
157
|
+
["foo"]
|
158
|
+
end
|
159
|
+
|
160
|
+
def self.label
|
161
|
+
"alabel"
|
162
|
+
end
|
163
|
+
|
164
|
+
def configured?
|
165
|
+
true
|
166
|
+
end
|
167
|
+
|
168
|
+
def errors
|
169
|
+
true
|
170
|
+
end
|
171
|
+
|
172
|
+
def create_issue
|
173
|
+
"http"
|
174
|
+
end
|
175
|
+
|
176
|
+
def close_issue
|
177
|
+
"http"
|
178
|
+
end
|
179
|
+
|
180
|
+
def url
|
181
|
+
"http"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
it "not valid" do
|
186
|
+
expect(ErrbitPlugin::IssueTrackerValidator.new(klass).valid?).to be false
|
187
|
+
end
|
188
|
+
|
189
|
+
it "say not implement icons method" do
|
190
|
+
is = ErrbitPlugin::IssueTrackerValidator.new(klass)
|
191
|
+
is.valid?
|
192
|
+
expect(is.errors).to eql [[:class_method_missing, :icons]]
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
context "without fields method" do
|
197
|
+
klass = Class.new(ErrbitPlugin::IssueTracker) do
|
198
|
+
def self.label
|
199
|
+
"foo"
|
200
|
+
end
|
201
|
+
|
202
|
+
def self.note
|
203
|
+
"foo"
|
204
|
+
end
|
205
|
+
|
206
|
+
def self.icons
|
207
|
+
{}
|
208
|
+
end
|
209
|
+
|
210
|
+
def configured?
|
211
|
+
true
|
212
|
+
end
|
213
|
+
|
214
|
+
def errors
|
215
|
+
true
|
216
|
+
end
|
217
|
+
|
218
|
+
def create_issue
|
219
|
+
"http"
|
220
|
+
end
|
221
|
+
|
222
|
+
def close_issue
|
223
|
+
"http"
|
224
|
+
end
|
225
|
+
|
226
|
+
def url
|
227
|
+
"http"
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
it "not valid" do
|
232
|
+
expect(ErrbitPlugin::IssueTrackerValidator.new(klass).valid?).to be false
|
233
|
+
end
|
234
|
+
|
235
|
+
it "say not implement fields method" do
|
236
|
+
is = ErrbitPlugin::IssueTrackerValidator.new(klass)
|
237
|
+
is.valid?
|
238
|
+
expect(is.errors).to eql [[:class_method_missing, :fields]]
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
context "without configured? method" do
|
243
|
+
klass = Class.new(ErrbitPlugin::IssueTracker) do
|
244
|
+
def self.label
|
245
|
+
"foo"
|
246
|
+
end
|
247
|
+
|
248
|
+
def self.note
|
249
|
+
"foo"
|
250
|
+
end
|
251
|
+
|
252
|
+
def self.fields
|
253
|
+
["foo"]
|
254
|
+
end
|
255
|
+
|
256
|
+
def self.icons
|
257
|
+
{}
|
258
|
+
end
|
259
|
+
|
260
|
+
def errors
|
261
|
+
true
|
262
|
+
end
|
263
|
+
|
264
|
+
def create_issue
|
265
|
+
"http"
|
266
|
+
end
|
267
|
+
|
268
|
+
def close_issue
|
269
|
+
"http"
|
270
|
+
end
|
271
|
+
|
272
|
+
def url
|
273
|
+
"http"
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
it "not valid" do
|
278
|
+
expect(ErrbitPlugin::IssueTrackerValidator.new(klass).valid?).to be false
|
279
|
+
end
|
280
|
+
|
281
|
+
it "say not implement configured? method" do
|
282
|
+
is = ErrbitPlugin::IssueTrackerValidator.new(klass)
|
283
|
+
is.valid?
|
284
|
+
expect(is.errors).to eql [[:instance_method_missing, :configured?]]
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
context "without errors method" do
|
289
|
+
klass = Class.new(ErrbitPlugin::IssueTracker) do
|
290
|
+
def self.label
|
291
|
+
"foo"
|
292
|
+
end
|
293
|
+
|
294
|
+
def self.note
|
295
|
+
"foo"
|
296
|
+
end
|
297
|
+
|
298
|
+
def self.fields
|
299
|
+
["foo"]
|
300
|
+
end
|
301
|
+
|
302
|
+
def self.icons
|
303
|
+
{}
|
304
|
+
end
|
305
|
+
|
306
|
+
def configured?
|
307
|
+
true
|
308
|
+
end
|
309
|
+
|
310
|
+
def create_issue
|
311
|
+
"http"
|
312
|
+
end
|
313
|
+
|
314
|
+
def close_issue
|
315
|
+
"http"
|
316
|
+
end
|
317
|
+
|
318
|
+
def url
|
319
|
+
"http"
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
it "not valid" do
|
324
|
+
expect(ErrbitPlugin::IssueTrackerValidator.new(klass).valid?).to be false
|
325
|
+
end
|
326
|
+
|
327
|
+
it "say not implement errors method" do
|
328
|
+
is = ErrbitPlugin::IssueTrackerValidator.new(klass)
|
329
|
+
is.valid?
|
330
|
+
expect(is.errors).to eql [[:instance_method_missing, :errors]]
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
context "without create_issue method" do
|
335
|
+
klass = Class.new(ErrbitPlugin::IssueTracker) do
|
336
|
+
def self.label
|
337
|
+
"foo"
|
338
|
+
end
|
339
|
+
|
340
|
+
def self.note
|
341
|
+
"foo"
|
342
|
+
end
|
343
|
+
|
344
|
+
def self.fields
|
345
|
+
["foo"]
|
346
|
+
end
|
347
|
+
|
348
|
+
def self.icons
|
349
|
+
{}
|
350
|
+
end
|
351
|
+
|
352
|
+
def configured?
|
353
|
+
true
|
354
|
+
end
|
355
|
+
|
356
|
+
def errors
|
357
|
+
true
|
358
|
+
end
|
359
|
+
|
360
|
+
def close_issue
|
361
|
+
"http"
|
362
|
+
end
|
363
|
+
|
364
|
+
def url
|
365
|
+
"http"
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
it "not valid" do
|
370
|
+
expect(ErrbitPlugin::IssueTrackerValidator.new(klass).valid?).to be false
|
371
|
+
end
|
372
|
+
it "say not implement create_issue method" do
|
373
|
+
is = ErrbitPlugin::IssueTrackerValidator.new(klass)
|
374
|
+
is.valid?
|
375
|
+
expect(is.errors).to eql [[:instance_method_missing, :create_issue]]
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
context "without close_issue method" do
|
380
|
+
# this is an optional method
|
381
|
+
klass = Class.new(ErrbitPlugin::IssueTracker) do
|
382
|
+
def self.label
|
383
|
+
"foo"
|
384
|
+
end
|
385
|
+
|
386
|
+
def self.note
|
387
|
+
"foo"
|
388
|
+
end
|
389
|
+
|
390
|
+
def self.fields
|
391
|
+
["foo"]
|
392
|
+
end
|
393
|
+
|
394
|
+
def self.icons
|
395
|
+
{}
|
396
|
+
end
|
397
|
+
|
398
|
+
def configured?
|
399
|
+
true
|
400
|
+
end
|
401
|
+
|
402
|
+
def errors
|
403
|
+
true
|
404
|
+
end
|
405
|
+
|
406
|
+
def create_issue
|
407
|
+
"http"
|
408
|
+
end
|
409
|
+
|
410
|
+
def url
|
411
|
+
"http"
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
it "is valid" do
|
416
|
+
expect(ErrbitPlugin::IssueTrackerValidator.new(klass).valid?).to be true
|
417
|
+
end
|
418
|
+
it "not say not implement close_issue method" do
|
419
|
+
is = ErrbitPlugin::IssueTrackerValidator.new(klass)
|
420
|
+
is.valid?
|
421
|
+
expect(is.errors).not_to eql [[:instance_method_missing, :close_issue]]
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
context "without url method" do
|
426
|
+
klass = Class.new(ErrbitPlugin::IssueTracker) do
|
427
|
+
def self.label
|
428
|
+
"foo"
|
429
|
+
end
|
430
|
+
|
431
|
+
def self.note
|
432
|
+
"foo"
|
433
|
+
end
|
434
|
+
|
435
|
+
def self.fields
|
436
|
+
["foo"]
|
437
|
+
end
|
438
|
+
|
439
|
+
def self.icons
|
440
|
+
{}
|
441
|
+
end
|
442
|
+
|
443
|
+
def configured?
|
444
|
+
true
|
445
|
+
end
|
446
|
+
|
447
|
+
def errors
|
448
|
+
true
|
449
|
+
end
|
450
|
+
|
451
|
+
def create_issue
|
452
|
+
"http"
|
453
|
+
end
|
454
|
+
|
455
|
+
def close_issue
|
456
|
+
"http"
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
it "not valid" do
|
461
|
+
expect(ErrbitPlugin::IssueTrackerValidator.new(klass).valid?).to be false
|
462
|
+
end
|
463
|
+
|
464
|
+
it "say not implement url method" do
|
465
|
+
is = ErrbitPlugin::IssueTrackerValidator.new(klass)
|
466
|
+
is.valid?
|
467
|
+
expect(is.errors).to eql [[:instance_method_missing, :url]]
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
context "without note method" do
|
472
|
+
klass = Class.new(ErrbitPlugin::IssueTracker) do
|
473
|
+
def self.label
|
474
|
+
"foo"
|
475
|
+
end
|
476
|
+
|
477
|
+
def self.fields
|
478
|
+
["foo"]
|
479
|
+
end
|
480
|
+
|
481
|
+
def self.icons
|
482
|
+
{}
|
483
|
+
end
|
484
|
+
|
485
|
+
def configured?
|
486
|
+
true
|
487
|
+
end
|
488
|
+
|
489
|
+
def errors
|
490
|
+
true
|
491
|
+
end
|
492
|
+
|
493
|
+
def create_issue
|
494
|
+
"http"
|
495
|
+
end
|
496
|
+
|
497
|
+
def close_issue
|
498
|
+
"http"
|
499
|
+
end
|
500
|
+
|
501
|
+
def url
|
502
|
+
"foo"
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
it "not valid" do
|
507
|
+
expect(ErrbitPlugin::IssueTrackerValidator.new(klass).valid?).to be false
|
508
|
+
end
|
509
|
+
|
510
|
+
it "say not implement note method" do
|
511
|
+
is = ErrbitPlugin::IssueTrackerValidator.new(klass)
|
512
|
+
is.valid?
|
513
|
+
expect(is.errors).to eql [[:class_method_missing, :note]]
|
514
|
+
end
|
515
|
+
end
|
516
|
+
end
|
517
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
RSpec.describe ErrbitPlugin::NoneIssueTracker do
|
6
|
+
let(:options) { {} }
|
7
|
+
|
8
|
+
subject { described_class.new(options) }
|
9
|
+
|
10
|
+
it { expect(subject).to be_an(ErrbitPlugin::IssueTracker) }
|
11
|
+
|
12
|
+
it { expect(subject.configured?).to eq(false) }
|
13
|
+
|
14
|
+
it { expect(subject.errors).to eq({}) }
|
15
|
+
|
16
|
+
it { expect(subject.url).to eq("") }
|
17
|
+
|
18
|
+
it { expect(subject.create_issue).to eq(false) }
|
19
|
+
|
20
|
+
it { expect(subject.close_issue).to eq(false) }
|
21
|
+
|
22
|
+
it { expect(described_class.label).to eq("none") }
|
23
|
+
|
24
|
+
it { expect(described_class.note).to start_with("When no issue tracker") }
|
25
|
+
|
26
|
+
it { expect(described_class.fields).to eq({}) }
|
27
|
+
|
28
|
+
it { expect(described_class.icons).not_to be_empty }
|
29
|
+
|
30
|
+
# TODO: .read_static_file
|
31
|
+
end
|