defmastership 1.0.17 → 1.0.18

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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.gitlab-ci.yml +22 -10
  3. data/Gemfile +51 -1
  4. data/Rakefile +16 -61
  5. data/bin/defmastership +9 -6
  6. data/config/mutant.yml +23 -3
  7. data/defmastership.gemspec +0 -10
  8. data/features/definition_checksum.feature +31 -1
  9. data/features/export.feature +43 -1
  10. data/features/rename_included_files.feature +28 -0
  11. data/lib/defmastership/batch_modifier.rb +17 -12
  12. data/lib/defmastership/change_ref_modifier.rb +88 -6
  13. data/lib/defmastership/comment_filter.rb +1 -1
  14. data/lib/defmastership/constants.rb +5 -4
  15. data/lib/defmastership/csv_formatter.rb +16 -12
  16. data/lib/defmastership/csv_formatter_body.rb +18 -15
  17. data/lib/defmastership/csv_formatter_header.rb +1 -1
  18. data/lib/defmastership/definition.rb +58 -19
  19. data/lib/defmastership/document.rb +109 -74
  20. data/lib/defmastership/matching_line.rb +17 -0
  21. data/lib/defmastership/modifier.rb +42 -0
  22. data/lib/defmastership/modifier_factory.rb +12 -0
  23. data/lib/defmastership/parsing_state.rb +15 -9
  24. data/lib/defmastership/rename_included_files_modifier.rb +172 -5
  25. data/lib/defmastership/set_join_hack.rb +11 -0
  26. data/lib/defmastership/update_def_checksum_modifier.rb +8 -13
  27. data/lib/defmastership/update_def_modifier.rb +49 -0
  28. data/lib/defmastership/update_def_version_modifier.rb +56 -15
  29. data/lib/defmastership/version.rb +1 -1
  30. data/lib/defmastership.rb +1 -6
  31. data/spec/spec_helper.rb +3 -1
  32. data/spec/unit/def_mastership/batch_modifier_spec.rb +38 -36
  33. data/spec/unit/def_mastership/change_ref_modifier_spec.rb +196 -51
  34. data/spec/unit/def_mastership/csv_formatter_body_spec.rb +60 -31
  35. data/spec/unit/def_mastership/csv_formatter_header_spec.rb +1 -1
  36. data/spec/unit/def_mastership/csv_formatter_spec.rb +79 -87
  37. data/spec/unit/def_mastership/definition_parser_spec.rb +1 -1
  38. data/spec/unit/def_mastership/definition_spec.rb +16 -6
  39. data/spec/unit/def_mastership/document_spec.rb +81 -38
  40. data/spec/unit/def_mastership/matching_line_spec.rb +37 -0
  41. data/spec/unit/def_mastership/modifier_factory_spec.rb +37 -0
  42. data/spec/unit/def_mastership/modifier_spec.rb +83 -0
  43. data/spec/unit/def_mastership/parsing_state_spec.rb +1 -1
  44. data/spec/unit/def_mastership/rename_included_files_modifier_spec.rb +219 -47
  45. data/spec/unit/def_mastership/string_spec.rb +1 -1
  46. data/spec/unit/def_mastership/update_def_checksum_modifier_spec.rb +82 -50
  47. data/spec/unit/def_mastership/update_def_modifier_spec.rb +119 -0
  48. data/spec/unit/def_mastership/update_def_version_modifier_spec.rb +135 -56
  49. data/tasks/console.rake +8 -0
  50. data/tasks/package.task +9 -0
  51. data/tasks/smelling_code.rake +38 -0
  52. data/tasks/test.rake +45 -0
  53. metadata +16 -153
  54. data/lib/defmastership/change_ref_line_modifier.rb +0 -85
  55. data/lib/defmastership/line_modifier_base.rb +0 -29
  56. data/lib/defmastership/modifier_base.rb +0 -36
  57. data/lib/defmastership/rename_included_files_line_modifier.rb +0 -126
  58. data/lib/defmastership/update_def_checksum_line_modifier.rb +0 -38
  59. data/lib/defmastership/update_def_version_line_modifier.rb +0 -58
  60. data/spec/unit/def_mastership/change_ref_line_modifier_spec.rb +0 -250
  61. data/spec/unit/def_mastership/rename_included_files_line_modifier_spec.rb +0 -207
  62. data/spec/unit/def_mastership/update_def_checksum_line_modifier_spec.rb +0 -82
  63. data/spec/unit/def_mastership/update_def_version_line_modifier_spec.rb +0 -131
@@ -1,85 +0,0 @@
1
- # Copyright (c) 2020 Jerome Arbez-Gindre
2
- # frozen_string_literal: true
3
-
4
- require 'defmastership/constants'
5
- require 'defmastership/line_modifier_base'
6
-
7
- module DefMastership
8
- # Change references from temporary to definitive with multiple RefChangers
9
- class ChangeRefLineModifier < LineModifierBase
10
- def self.from_config(hash)
11
- new.from_config(hash)
12
- end
13
-
14
- REGEXP_FROM = {
15
- definition: {
16
- before: DMRegexp::DEF_BEFORE_REF,
17
- after: DMRegexp::DEF_AFTER_REF
18
- },
19
- iref: {
20
- before: DMRegexp::IREF_DEF_BEF,
21
- after: DMRegexp::IREF_DEF_AFT
22
- }
23
- }.freeze
24
-
25
- private_constant :REGEXP_FROM
26
-
27
- def initialize
28
- super
29
- @config = {
30
- from_regexp: '',
31
- to_template: '',
32
- next_ref: 0
33
- }
34
- @parsing_state = ParsingState.new
35
- end
36
-
37
- def replace(method, line)
38
- public_send("replace_#{method}".to_sym, line)
39
- end
40
-
41
- def replace_refdef(line)
42
- if @parsing_state.enabled?(line)
43
- do_replace_refdef(line)
44
- else
45
- line
46
- end
47
- end
48
-
49
- def replace_irefs(line)
50
- changes.reduce(line) do |res_line, (from, to)|
51
- res_line.gsub(regexp_from(:iref, from)) do
52
- text_with(Regexp.last_match, to)
53
- end
54
- end
55
- end
56
-
57
- private
58
-
59
- def do_replace_refdef(line)
60
- line.gsub(regexp_from(:definition, from_regexp)) do
61
- replacement = to_template % hmerge(Regexp.last_match)
62
- changes << [Regexp.last_match[:from], replacement]
63
- @config[:next_ref] += 1
64
- text_with(Regexp.last_match, replacement)
65
- end
66
- end
67
-
68
- def regexp_from(const, from)
69
- regexp_str =
70
- "(?<before>#{REGEXP_FROM[const][:before]})" \
71
- "(?<from>#{from})" \
72
- "#{DMRegexp::DEF_VERSION_AND_CHECKSUM}" \
73
- "(?<after>#{REGEXP_FROM[const][:after]})"
74
- Regexp.new(regexp_str, Regexp::EXTENDED)
75
- end
76
-
77
- def text_with(match, replacement)
78
- match[:before] + replacement + (match[:version_and_checksum] || '') + match[:after]
79
- end
80
-
81
- def hmerge(match)
82
- @config.merge(match.names.map(&:to_sym).zip(match.captures).to_h)
83
- end
84
- end
85
- end
@@ -1,29 +0,0 @@
1
- # Copyright (c) 2020 Jerome Arbez-Gindre
2
- # frozen_string_literal: true
3
-
4
- module DefMastership
5
- # Change references from temporary to definitive with multiple RefChangers
6
- class LineModifierBase
7
- attr_reader :changes, :config
8
-
9
- def initialize
10
- @config = {}
11
- @changes = []
12
- end
13
-
14
- def method_missing(method_name, *args, &block)
15
- return @config[method_name] if @config[method_name]
16
-
17
- super
18
- end
19
-
20
- def respond_to_missing?(method_name, *args)
21
- @config[method_name] || super
22
- end
23
-
24
- def from_config(config)
25
- @config.merge!(config)
26
- self
27
- end
28
- end
29
- end
@@ -1,36 +0,0 @@
1
- # Copyright (c) 2020 Jerome Arbez-Gindre
2
- # frozen_string_literal: true
3
-
4
- module DefMastership
5
- # Change references from temporary to definitive with multiple RefChangers
6
- class ModifierBase
7
- attr_reader :config, :changes
8
-
9
- def initialize(config)
10
- @config = config
11
- @changes = []
12
- end
13
-
14
- def do_modifications(adoc_texts)
15
- line_modifier = new_line_modifier(@config, adoc_texts)
16
-
17
- adoc_texts =
18
- replacements.reduce(adoc_texts) do |texts, method|
19
- transform_values(texts, line_modifier, method)
20
- end
21
-
22
- @config = line_modifier.config
23
- @changes = line_modifier.changes
24
- adoc_texts
25
- end
26
-
27
- private
28
-
29
- def transform_values(texts, line_modifier, method)
30
- texts.transform_values do |text|
31
- text.lines.map { |line| line_modifier.public_send(method, line) }
32
- .join
33
- end
34
- end
35
- end
36
- end
@@ -1,126 +0,0 @@
1
- # Copyright (c) 2020 Jerome Arbez-Gindre
2
- # frozen_string_literal: true
3
-
4
- module DefMastership
5
- # Change included filenames on one line at a time
6
- class RenameIncludedFilesLineModifier < LineModifierBase
7
- LOCAL_FILTERS = [
8
- Filter.new(DMRegexp::VARIABLE_DEF, :new_variable_def, false),
9
- Filter.new(DMRegexp::DEFINITION, :new_definition, true),
10
- Filter.new(DMRegexp::BLOCK, :block_delimiter, true),
11
- Filter.new(DMRegexp::EMPTY_LINE, :empty_line, false),
12
- Filter.new(DMRegexp::WHATEVER, :new_line, true)
13
- ].freeze
14
- private_constant :LOCAL_FILTERS
15
-
16
- def self.from_config(config)
17
- new.from_config(config)
18
- end
19
-
20
- def initialize
21
- super
22
- @config = {
23
- from_regexp: '',
24
- to_template: ''
25
- }
26
- @variables = {}
27
- @definition_parser = DefinitionParser.new(self)
28
- end
29
-
30
- def replace(line)
31
- match = matched?(line)
32
-
33
- return line unless match
34
-
35
- new_line =
36
- line.gsub(complete_regexp_from(from_regexp)) do
37
- text_with(match, to_template % hmerge(match))
38
- end
39
-
40
- rename_file(line, new_line)
41
-
42
- new_line
43
- end
44
-
45
- def add_new_definition(match, _line)
46
- @config[:reference] = match[:reference]
47
- end
48
-
49
- def add_line(_match, _line) end
50
-
51
- def new_variable_def(match, line)
52
- @variables[match[:varname].to_sym] = match[:value]
53
- line
54
- end
55
-
56
- private
57
-
58
- def matched?(line)
59
- return if line.commented?
60
-
61
- parse(line)
62
-
63
- return if @definition_parser.idle?
64
- return unless line =~ complete_regexp_from(from_regexp)
65
-
66
- match = Regexp.last_match
67
-
68
- return if @config[:cancel_if_match] && match[:filename] =~ Regexp.new(cancel_if_match)
69
-
70
- match
71
- end
72
-
73
- def parse(line)
74
- LOCAL_FILTERS.each do |filter|
75
- next unless line.match(filter.regexp)
76
-
77
- line = generate_event(filter.event, Regexp.last_match, line)
78
-
79
- break if filter.consumed_line
80
- end
81
- end
82
-
83
- def rename_file(from, to)
84
- @changes << [extract_filename(from), extract_filename(to)]
85
- File.rename(extract_filename(from), extract_filename(to))
86
- end
87
-
88
- def extract_filename(include_statement)
89
- filename = include_statement
90
- .gsub(Regexp.new(DMRegexp::INCLUDE_KEYWORD), '')
91
- .gsub(Regexp.new(DMRegexp::INCLUDE_OPTIONS), '')
92
-
93
- filename.dup.scan(DMRegexp::VARIABLE_USE) do |_|
94
- varname = Regexp.last_match[:varname]
95
- next if @variables[varname.to_sym].nil?
96
-
97
- filename.gsub!("{#{varname}}", @variables[varname.to_sym])
98
- end
99
- filename.chomp
100
- end
101
-
102
- def complete_regexp_from(from)
103
- Regexp.new(
104
- DMRegexp::INCLUDE_KEYWORD + DMRegexp::INCLUDE_PATH +
105
- "(?<filename>#{from})" + DMRegexp::INCLUDE_OPTIONS
106
- )
107
- end
108
-
109
- def text_with(match, to)
110
- "include::#{match[:path] || ''}#{to}[#{match[:options]}]"
111
- end
112
-
113
- def hmerge(match)
114
- @config.merge(match.names.map(&:to_sym).zip(match.captures).to_h)
115
- end
116
-
117
- def generate_event(event, match, line)
118
- if respond_to?(event)
119
- public_send(event, match, line)
120
- else
121
- @definition_parser.public_send(event, match, line)
122
- end
123
- line
124
- end
125
- end
126
- end
@@ -1,38 +0,0 @@
1
- # Copyright (c) 2020 Jerome Arbez-Gindre
2
- # frozen_string_literal: true
3
-
4
- module DefMastership
5
- # modify one line after another
6
- class UpdateDefChecksumLineModifier < LineModifierBase
7
- attr_accessor :document
8
-
9
- def self.from_config(hash)
10
- new.from_config(hash)
11
- end
12
-
13
- def initialize
14
- super
15
- @config = {
16
- def_type: ''
17
- }
18
- end
19
-
20
- def replace(line)
21
- match = matched?(line)
22
-
23
- return line unless match
24
- return line unless match[:type] == def_type
25
-
26
- line.gsub(Regexp.new("#{match[:reference]}#{DMRegexp::DEF_VERSION_AND_CHECKSUM}")) do
27
- "#{match[:reference]}(#{match[:explicit_version]}#{@document.ref_to_def(match[:reference]).sha256})"
28
- end
29
- end
30
-
31
- def matched?(line)
32
- return if line.commented?
33
- return unless line =~ DMRegexp::DEFINITION
34
-
35
- Regexp.last_match
36
- end
37
- end
38
- end
@@ -1,58 +0,0 @@
1
- # Copyright (c) 2020 Jerome Arbez-Gindre
2
- # frozen_string_literal: true
3
-
4
- module DefMastership
5
- # modify one line after another
6
- class UpdateDefVersionLineModifier < LineModifierBase
7
- attr_accessor :document, :ref_document
8
-
9
- def self.from_config(hash)
10
- new.from_config(hash)
11
- end
12
-
13
- def initialize
14
- super
15
- @config = {
16
- def_type: '',
17
- ref_document: '',
18
- first_version: ''
19
- }
20
- end
21
-
22
- def replace(line)
23
- match = matched?(line)
24
-
25
- return line unless match
26
- return line unless match[:type] == def_type
27
-
28
- version_and_checksum = ''
29
- if match[:explicit_checksum] || version_string(match)
30
- version_and_checksum = "(#{version_string(match)}#{match[:explicit_checksum]})"
31
- end
32
-
33
- line.gsub(Regexp.new("#{match[:reference]}#{DMRegexp::DEF_VERSION_AND_CHECKSUM}")) do
34
- "#{match[:reference]}#{version_and_checksum}"
35
- end
36
- end
37
-
38
- private
39
-
40
- def matched?(line)
41
- return if line.commented?
42
- return unless line =~ DMRegexp::DEFINITION
43
-
44
- Regexp.last_match
45
- end
46
-
47
- def version_string(match)
48
- ref_definition = @ref_document.ref_to_def(match[:reference])
49
- definition = @document.ref_to_def(match[:reference])
50
-
51
- return if ref_definition.nil?
52
- return ref_definition.explicit_version if definition.sha256 == ref_definition.sha256
53
-
54
- ref_version = @ref_document.ref_to_def(match[:reference]).explicit_version
55
- ref_version.nil? ? @config[:first_version] : ref_version.next
56
- end
57
- end
58
- end
@@ -1,250 +0,0 @@
1
- # Copyright (c) 2020 Jerome Arbez-Gindre
2
- # frozen_string_literal: true
3
-
4
- require('defmastership')
5
-
6
- RSpec.describe(DefMastership::ChangeRefLineModifier) do
7
- subject(:refchanger) { described_class.new }
8
-
9
- describe '.new' do
10
- it { is_expected.not_to(be_nil) }
11
- it { is_expected.to(have_attributes(from_regexp: '')) }
12
- it { is_expected.to(have_attributes(to_template: '')) }
13
- it { is_expected.to(have_attributes(next_ref: 0)) }
14
- it { is_expected.to(have_attributes(changes: [])) }
15
-
16
- it do
17
- expect { refchanger.user_defined_attribute }
18
- .to(raise_error(NoMethodError))
19
- end
20
- end
21
-
22
- describe '.from_config' do
23
- subject(:refchanger) do
24
- described_class.from_config(
25
- from_regexp: 'Whatever.+',
26
- to_template: 'Whatever',
27
- next_ref: 17
28
- )
29
- end
30
-
31
- it { is_expected.not_to(be_nil) }
32
- it { is_expected.to(have_attributes(from_regexp: 'Whatever.+')) }
33
- it { is_expected.to(have_attributes(to_template: 'Whatever')) }
34
- it { is_expected.to(have_attributes(next_ref: 17)) }
35
- end
36
-
37
- describe '#from_config' do
38
- before do
39
- refchanger.from_config(
40
- from_regexp: 'Whatever.+',
41
- to_template: 'Whatever',
42
- next_ref: 17,
43
- user_defined_attribute: 'Tadaam'
44
- )
45
- end
46
-
47
- it { is_expected.to(have_attributes(from_regexp: 'Whatever.+')) }
48
- it { is_expected.to(have_attributes(to_template: 'Whatever')) }
49
- it { is_expected.to(have_attributes(next_ref: 17)) }
50
- it { is_expected.to(have_attributes(user_defined_attribute: 'Tadaam')) }
51
- end
52
-
53
- describe '#config' do
54
- context 'when not initalized' do
55
- it do
56
- expect(refchanger.config).to(
57
- include(from_regexp: '', to_template: '', next_ref: 0)
58
- )
59
- end
60
- end
61
-
62
- context 'when initalized with additionnal keys' do
63
- before do
64
- refchanger.from_config(
65
- next_ref: 1256,
66
- user_defined_attribute: 'Tadaam'
67
- )
68
- end
69
-
70
- it { expect(refchanger.config).to(include(from_regexp: '')) }
71
- it { expect(refchanger.config).to(include(to_template: '')) }
72
- it { expect(refchanger.config).to(include(next_ref: 1256)) }
73
- it { expect(refchanger.config).to(include(user_defined_attribute: 'Tadaam')) }
74
- end
75
- end
76
-
77
- describe '#replace_def' do
78
- context 'when really simple rule' do
79
- before do
80
- refchanger.from_config(
81
- from_regexp: 'TEMP',
82
- to_template: 'TUTU'
83
- )
84
- end
85
-
86
- context 'when valid definition' do
87
- it do
88
- expect(refchanger.replace_refdef('[define, whatever, TEMP]'))
89
- .to(eq('[define, whatever, TUTU]'))
90
- end
91
- end
92
-
93
- context 'when no valid definition' do
94
- it do
95
- expect(refchanger.replace_refdef('[pouet, whatever, TEMP]'))
96
- .to(eq('[pouet, whatever, TEMP]'))
97
- end
98
- end
99
- end
100
-
101
- context 'when template is variable' do
102
- before do
103
- refchanger.from_config(
104
- from_regexp: 'TEMP',
105
- to_template: 'TOTO-%<next_ref>04d',
106
- next_ref: 124
107
- )
108
- end
109
-
110
- it do
111
- expect(refchanger.replace_refdef('[define, whatever, TEMP]'))
112
- .to(eq('[define, whatever, TOTO-0124]'))
113
- end
114
-
115
- it do
116
- refchanger.replace_refdef('[define, whatever, TEMP]')
117
- expect(refchanger).to(have_attributes(next_ref: 125))
118
- end
119
- end
120
-
121
- context 'when capture group in regexp' do
122
- before do
123
- refchanger.from_config(
124
- from_regexp: '(?<cg>TOTO|TITI)-TEMP[\d]*',
125
- to_template: '%<cg>s-%<next_ref>04d',
126
- next_ref: 132
127
- )
128
- end
129
-
130
- it do
131
- expect(refchanger.replace_refdef('[define, whatever, TOTO-TEMP]'))
132
- .to(eq('[define, whatever, TOTO-0132]'))
133
- end
134
-
135
- it do
136
- refchanger.replace_refdef('[define, whatever, TOTO-TEMP]')
137
- expect(refchanger.replace_refdef('[define, whatever, TITI-TEMP]'))
138
- .to(eq('[define, whatever, TITI-0133]'))
139
- end
140
-
141
- it do
142
- refchanger.replace_refdef('[define, whatever, TOTO-TEMP1]')
143
- refchanger.replace_refdef('[define, whatever, TITI-TEMP2]')
144
- expect(refchanger).to(have_attributes(changes: [%w[TOTO-TEMP1 TOTO-0132], %w[TITI-TEMP2 TITI-0133]]))
145
- end
146
- end
147
-
148
- context 'when definition is in literal block' do
149
- before do
150
- refchanger.from_config(
151
- from_regexp: 'TEMP',
152
- to_template: 'TUTU'
153
- )
154
- refchanger.replace_refdef('....')
155
- end
156
-
157
- it do
158
- expect(refchanger.replace_refdef('[define, whatever, TEMP]'))
159
- .to(eq('[define, whatever, TEMP]'))
160
- end
161
- end
162
-
163
- context 'when defintion is after literal block' do
164
- before do
165
- refchanger.from_config(
166
- from_regexp: 'TEMP',
167
- to_template: 'TUTU'
168
- )
169
- refchanger.replace_refdef("....\n")
170
- refchanger.replace_refdef('[define, whatever, TEMP]')
171
- refchanger.replace_refdef("....\n")
172
- end
173
-
174
- it do
175
- expect(refchanger.replace_refdef('[define, whatever, TEMP]'))
176
- .to(eq('[define, whatever, TUTU]'))
177
- end
178
- end
179
- end
180
-
181
- describe '#replace_irefs' do
182
- before do
183
- refchanger.from_config(
184
- from_regexp: '(?<cg>TOTO|TITI)-TEMP[\d]*',
185
- to_template: '%<cg>s-%<next_ref>04d',
186
- next_ref: 132
187
- )
188
- refchanger.replace_refdef('[define, whatever, TOTO-TEMP123]')
189
- refchanger.replace_refdef('[define, whatever, TITI-TEMP421]')
190
- end
191
-
192
- it do
193
- expect(refchanger.replace_irefs('defs:iref[TOTO-TEMP1234]'))
194
- .to(eq('defs:iref[TOTO-TEMP1234]'))
195
- end
196
-
197
- it do
198
- expect(refchanger.replace_irefs('defs:iref[TOTO-TEMP123]'))
199
- .to(eq('defs:iref[TOTO-0132]'))
200
- end
201
-
202
- it do
203
- expect(
204
- refchanger.replace_irefs(
205
- 'defs:iref[TOTO-TEMP123] defs:iref[TITI-TEMP421] bla'
206
- )
207
- ).to(eq('defs:iref[TOTO-0132] defs:iref[TITI-0133] bla'))
208
- end
209
- end
210
-
211
- describe '#replace' do
212
- before do
213
- refchanger.from_config(
214
- from_regexp: 'TEMP',
215
- to_template: 'TUTU'
216
- )
217
- end
218
-
219
- it do
220
- expect(refchanger.replace(:refdef, '[define, whatever, TEMP]'))
221
- .to(eq('[define, whatever, TUTU]'))
222
- end
223
-
224
- it do
225
- expect(refchanger.replace(:refdef, '[define, whatever, TEMP(a~12345678)]'))
226
- .to(eq('[define, whatever, TUTU(a~12345678)]'))
227
- end
228
-
229
- it do
230
- expect(refchanger.replace(:refdef, '[define, whatever, TEMP(~12345678)]'))
231
- .to(eq('[define, whatever, TUTU(~12345678)]'))
232
- end
233
-
234
- it do
235
- expect(refchanger.replace(:refdef, '[define, whatever, TEMP(a)]'))
236
- .to(eq('[define, whatever, TUTU(a)]'))
237
- end
238
-
239
- it do
240
- refchanger.replace(:refdef, '[define, whatever, TEMP]')
241
- expect(refchanger.replace(:irefs, 'defs:iref[TEMP]'))
242
- .to(eq('defs:iref[TUTU]'))
243
- end
244
-
245
- it do
246
- expect { refchanger.replace(:pouet, 'whatever') }
247
- .to(raise_error(NoMethodError))
248
- end
249
- end
250
- end