plain_text 0.7.1 → 0.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,10 +1,12 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
- require "plain_text/util"
3
+ require_relative "error"
4
+ require_relative "builtin_type"
5
+ require_relative "util"
4
6
 
5
7
  module PlainText
6
8
 
7
- # Class to represent a Chapter-like entity like an Array
9
+ # Class to represent a Chapter-like entity, which behaves like an Array
8
10
  #
9
11
  # An instance of this class contains always an even number of elements,
10
12
  # either another {Part} instance or {Paragraph}-type String-like instance,
@@ -17,65 +19,80 @@ module PlainText
17
19
  # An example instance looks like this:
18
20
  #
19
21
  # Part (
20
- # (0) Paragraph::Empty,
21
- # (1) Boundary::General,
22
+ # (0) Part::Paragraph::Empty,
23
+ # (1) Part::Boundary::General,
22
24
  # (2) Part::ArticleHeader(
23
- # (0) Paragraph::Title,
24
- # (1) Boundary::Empty
25
+ # (0) Part::Paragraph::Title,
26
+ # (1) Part::Boundary::Empty
25
27
  # ),
26
- # (3) Boundary::TitleMain,
28
+ # (3) Part::Boundary::TitleMain,
27
29
  # (4) Part::ArticleMain(
28
30
  # (0) Part::ArticleSection(
29
- # (0) Paragraph::Title,
30
- # (1) Boundary::General,
31
- # (2) Paragraph::General,
32
- # (3) Boundary::General,
31
+ # (0) Part::Paragraph::Title,
32
+ # (1) Part::Boundary::General,
33
+ # (2) Part::Paragraph::General,
34
+ # (3) Part::Boundary::General,
33
35
  # (4) Part::ArticleSubSection(...),
34
- # (5) Boundary::General,
35
- # (6) Paragraph::General,
36
- # (7) Boundary::Empty
36
+ # (5) Part::Boundary::General,
37
+ # (6) Part::Paragraph::General,
38
+ # (7) Part::Boundary::Empty
37
39
  # ),
38
- # (1) Boundary::General,
39
- # (2) Paragraph::General,
40
- # (3) Boundary::Empty
40
+ # (1) Part::Boundary::General,
41
+ # (2) Part::Paragraph::General,
42
+ # (3) Part::Boundary::Empty
41
43
  # ),
42
- # (5) Boundary::General
44
+ # (5) Part::Boundary::General
43
45
  # )
44
46
  #
45
47
  # A Section (Part) always has an even number of elements: pairs of a Para ({Part}|{Paragraph}) and {Boundary} in this order.
46
48
  #
47
- # Note some standard destructive Array operations, most notably +#delete+, +#delete_if+, +#reject!+,
49
+ # This class behaves like an Array and so Array methods that usually return
50
+ # an Array returns an instance of this class, such as +pt[0..-2]+.
51
+ # However, not all Array methods are accepted in the same way.
52
+ # For example, for the instance +pt+, +pt[0..-2]+ raises an Exception
53
+ # because it would violate the principle of the instance of this class
54
+ # having always an even number of elements. Use +to_a+ to obtain
55
+ # a standard Array; e.g., +pt.to_a[0..-2]+ returns an Array with
56
+ # an odd number of elements.
57
+ #
58
+ # Some destructive Array operations, most notably +#delete+, +#delete_if+, +#reject!+,
48
59
  # +#select!+, +#filter!+, +#keep_if+, +#flatten!+, +#uniq!+ may alter the content in a way
49
- # it breaks the self-inconsistency of the object.
60
+ # it breaks the self-consistency of the object.
50
61
  # Use it at your own risk, if you wish (or don't).
62
+ # Such operations may become prohibited in the future release.
63
+ # {#<<} and {#delete_at} are currently disabled.
51
64
  #
52
65
  # An instance of this class is always *non-equal* to that of the standard Array class.
53
- # To compare it at the Array level, convert a {Part} class instance into Array with #to_a first and compare them.
66
+ # To compare it at the Array level, convert a {Part} class instance into Array
67
+ # with {#to_a} first: +pt.to_a == my_array+
54
68
  #
55
69
  # For CRUD of elements (contents) of an instance, the following methods are most basic:
56
70
  #
57
71
  # * Create:
58
- # * Insert/Append: {#insert} to insert. If the specified index is #size}, it means "append". For primitive operations, specify +primitive: true+ to skip various checks performed to guarantee the self-consistency as an instance of this class.
59
- # * {#<<} is disabled.
72
+ # * Use +PlainText::Part.new+ (see {PlainText::Part.initialize})
60
73
  # * Read:
61
- # * Read: #to_a gives the standard Array, and then you can do whatever manipulation allowed for Array. For example, if you delete an element in the returned Array, that does not affect the original {Part} instance. However, it is a shallow copy, and hence if you alter an element of it destructively (such as String#replace), the original instance, too, is affected.
62
- # * The methods {#[]} (or its alias {#slice}) have some restrictions, such as, an odd number of elements cannot be retrieved, so as to conform the returned value is a valid instance of this class.
74
+ # * All non-destructive Array operations are permitted and returns an instance of this class if the corresponding Array method returns an Array, providing it does not break the self-consistency.
75
+ # * {#to_a} gives or exposes the internal Array object, and then you can do whatever manipulation allowed for Array with it, if you insist. Note that destructive modification of the object carries a risk of breaking self-consistency of the instance and so is highly discouraged. You must know what you are doing if you do.
63
76
  # * Update:
77
+ # * Insert/Append: {#insert} to insert. If the specified index is #size}, it means "append". For primitive operations, specify +primitive: true+ to skip various checks performed to guarantee the self-consistency as an instance of this class.
64
78
  # * Replace: {#[]=} has some restrictions, such as, if multiple elements are replaced, they have to be pairs of Paragraph and Boundary. To skip all the checks, do {#insert} with +primitive: true+
65
79
  # * Delete:
66
80
  # * Delete: {#slice!} to delete. For primitive operations, specify +primitive: true+ to skip various checks performed to guarantee the self-consistency as an instance of this class.
67
- # * +#delete_at+ is disabled. +#delete+, +#delete_if+, +#reject!+, +#select!+, +#filter!+, +#keep_if+ (and +#drop_while+ and +#take_whie+ in recent Ruby) remain enabled, but if you use them, make sure to use them carefully at your own risk, as no self-consistency checks would be performed automatically.
81
+ # * +#delete_at+ is disabled. +#delete+, +#delete_if+, +#reject!+, +#select!+, +#filter!+, +#keep_if+ (and +#drop_while+ and +#take_whie+ in recent Ruby) remain enabled, but if you use them, do so at your own risk, as no self-consistency checks would be performed automatically. {#normalize} would return the self-consistent instance, or its destructive version, {#normalize!}.
68
82
  #
69
83
  # @author Masa Sakano (Wise Babel Ltd)
70
84
  #
71
85
  # @todo methods
72
86
  # * flatten
73
- # * SAFE level for command-line tools?
74
87
  #
75
- class Part < Array
88
+ class Part
76
89
 
90
+ include PlainText::BuiltinType
77
91
  include PlainText::Util
78
92
 
93
+ # Array methods that are disabled for this class.
94
+ DISABLED_ARRAY_METHODS = %i(<< delete_at)
95
+
79
96
  # Error messages
80
97
  ERR_MSGS = {
81
98
  even_num: 'even number of elements must be specified.',
@@ -83,25 +100,44 @@ module PlainText
83
100
  }
84
101
  private_constant :ERR_MSGS
85
102
 
86
- # @param arin [Array] of [Paragraph1, Boundary1, Para2, Bd2, ...] or just Paragraphs if boundaries is given as the second arguments
103
+ # Constructor
104
+ #
105
+ # New String-type objects are always created, i.e.,
106
+ # regardless of whether the input is a pure String or {Paragraph} etc,
107
+ # a new {Paragraph} is always created from {Paragraph#to_s}. Therefore,
108
+ # even if one of the String given to the argument is destructively
109
+ # modified, it does not affect the generated {Part} object. This also means
110
+ # that if the input {Paragraph} has special singloton methods or
111
+ # instance variables, the information will be lost.
112
+ #
113
+ # @param arin [Array] of [Paragraph1, Boundary1, Para2, Bd2, ...] or just of Paragraphs if boundaries is given as the second arguments
87
114
  # @param boundaries [Array] of Boundary
88
115
  # @option recursive: [Boolean] if true (Default), normalize recursively.
89
116
  # @option compact: [Boolean] if true (Default), pairs of nil paragraph and boundary are removed. Otherwise, nil is converted to an empty string.
90
117
  # @option compacter: [Boolean] if true (Default), pairs of nil or empty paragraph and boundary are removed.
91
118
  # @return [self]
92
119
  def initialize(arin, boundaries=nil, recursive: true, compact: true, compacter: true)
120
+ raise ArgumentError, "Arguments must be an Array(s) or equivalent: "+arin.inspect+(boundaries ? ", "+boundaries.inspect : "") if (!arin.respond_to?(:compact) && !arin.respond_to?(:paras)) || boundaries && !boundaries.respond_to?(:compact) # arin must be an Array or Part
93
121
  if !boundaries
94
- super(arin)
122
+ @array = arin.clone
123
+ #super(arin)
95
124
  else
125
+ raise ArgumentError, "Two main Arrays must have the same size." if arin.size != boundaries.size
96
126
 
97
127
  armain = []
98
128
  arin.each_with_index do |ea_e, i|
99
129
  armain << ea_e
100
130
  armain << (boundaries[i] || Boundary.new(''))
101
131
  end
102
- super armain
132
+ @array = armain
133
+ #super armain
134
+ end
135
+
136
+ begin
137
+ normalize!(recursive: recursive, compact: compact, compacter: compacter)
138
+ rescue PlainText::PartNormalizeError => err
139
+ raise TypeError, err.message
103
140
  end
104
- normalize!(recursive: recursive, compact: compact, compacter: compacter)
105
141
  end
106
142
 
107
143
  # Parses a given string (or {Part}) and returns this class of instance.
@@ -114,6 +150,33 @@ module PlainText
114
150
  self.new(arin)
115
151
  end
116
152
 
153
+ # Returns true if it is {PlainText::Part}
154
+ def self.part?(other)
155
+ _part_paragraph_boundary?(other, __method__)
156
+ end
157
+
158
+ # Returns true if it is {PlainText::Part::Boundary}
159
+ def self.boundary?(other)
160
+ _part_paragraph_boundary?(other, __method__)
161
+ end
162
+
163
+ # Returns true if it is {PlainText::Part::Paragraph}
164
+ def self.paragraph?(other)
165
+ _part_paragraph_boundary?(other, __method__)
166
+ end
167
+
168
+ # Returns true if it is built-in String or similar
169
+ def self.builtin_string?(other)
170
+ other.respond_to?(:to_str) && !boundary? && !paragraph?
171
+ end
172
+
173
+ # Core routines for {Part.part?} etc.
174
+ def self._part_paragraph_boundary?(other, metho)
175
+ %i(boundary? paragraph? part?).all?{|i| other.respond_to? i} && other.send(metho)
176
+ end
177
+ private_class_method :_part_paragraph_boundary?
178
+
179
+
117
180
  ####################################################
118
181
  # Instance methods
119
182
  ####################################################
@@ -122,12 +185,24 @@ module PlainText
122
185
  # Unique instance methods (not existing in Array)
123
186
  ##########
124
187
 
188
+ def boundary?
189
+ false
190
+ end
191
+
192
+ def paragraph?
193
+ false
194
+ end
195
+
196
+ def part?
197
+ true
198
+ end
199
+
125
200
  # Returns an array of boundaries (odd-number-index elements), consisting of Boundaries
126
201
  #
127
202
  # @return [Array<Boundary>]
128
203
  # @see #paras
129
204
  def boundaries
130
- select.with_index { |_, i| i.odd? } rescue select.each_with_index { |_, i| i.odd? } # Rescue for Ruby 2.1 or earlier
205
+ @array.select.with_index { |_, i| i.odd? } rescue @array.select.each_with_index { |_, i| i.odd? } # Rescue for Ruby 2.1 or earlier
131
206
  end
132
207
 
133
208
  # returns all the Boundaries immediately before the index and at it as an Array
@@ -139,16 +214,19 @@ module PlainText
139
214
  def boundary_extended_at(index)
140
215
  (i_pos = get_valid_ipos_for_boundary(index)) || return
141
216
  arret = []
142
- prt = self[i_pos-1]
143
- arret = prt.public_send(__method__, -1) if prt.class.method_defined? __method__
144
- arret << self[index]
217
+ prt = @array[i_pos-1]
218
+ arret = prt.public_send(__method__, -1) if prt.respond_to? __method__
219
+ arret << @array[index]
145
220
  end
146
221
 
147
222
  # Returns a dup-ped instance with all the Arrays and Strings dup-ped.
148
223
  #
149
224
  # @return [Part]
150
225
  def deepcopy
151
- dup.map!{ |i| i.class.method_defined?(:deepcopy) ? i.deepcopy : i.dup }
226
+ _return_this_or_other{
227
+ @array.dup.map!{ |i| i.respond_to?(:deepcopy) ? i.deepcopy : (i.dup rescue i)}
228
+ # the "rescue" deals with cases where i is immutable (which should never happen and in fact it would not raise an Exception in Ruby 3 seemingly).
229
+ }
152
230
  end
153
231
 
154
232
  # each method for boundaries only, providing also the index (always an odd number) to the block.
@@ -164,7 +242,7 @@ module PlainText
164
242
  # @param (see #map_boundary_with_index)
165
243
  # @return as self
166
244
  def each_boundary_with_index(**kwd, &bl)
167
- map_boundary_core(map: false, with_index: true, **kwd, &bl)
245
+ map_boundary_core(do_map: false, with_index: true, **kwd, &bl)
168
246
  end
169
247
 
170
248
  # each method for Paras only, providing also the index (always an even number) to the block.
@@ -180,7 +258,7 @@ module PlainText
180
258
  # @param (see #map_para_with_index)
181
259
  # @return as self
182
260
  def each_para_with_index(**kwd, &bl)
183
- map_para_core(map: false, with_index: false, **kwd, &bl)
261
+ map_para_core(do_map: false, with_index: false, **kwd, &bl)
184
262
  end
185
263
 
186
264
  # The first significant (=non-empty) element.
@@ -190,7 +268,7 @@ module PlainText
190
268
  # @return [Integer, nil] if self.empty? nil is returned.
191
269
  def first_significant_element
192
270
  (i = first_significant_index) || return
193
- self[i]
271
+ @array[i]
194
272
  end
195
273
 
196
274
  # Index of the first significant (=non-empty) element.
@@ -199,20 +277,19 @@ module PlainText
199
277
  #
200
278
  # @return [Integer, nil] if self.empty? nil is returned.
201
279
  def first_significant_index
202
- return nil if empty?
203
- each_index do |i|
204
- return i if self[i] && !self[i].empty? # self for sanity
205
- end
206
- return size-1
280
+ return nil if @array.empty?
281
+ @array.find_index do |val|
282
+ val && !val.empty?
283
+ end || (@array.size-1)
207
284
  end
208
285
 
209
286
  # True if the index should be semantically for Paragraph?
210
287
  #
211
288
  # @param i [Integer] index for the array of self
212
- # @option skip_check: [Boolean] if true (Default: false), skip conversion of the negative index to positive.
289
+ # @option accept_negative: [Boolean] if false (Default: true), skip conversion of the negative index to positive.
213
290
  # @see #paras
214
- def index_para?(i, skip_check: false)
215
- skip_check ? i.even? : positive_array_index_checked(i, self).even?
291
+ def index_para?(i, accept_negative: true)
292
+ accept_negative ? positive_array_index_checked(i, @array).even? : i.even?
216
293
  end
217
294
 
218
295
  # The last significant (=non-empty) element.
@@ -222,7 +299,7 @@ module PlainText
222
299
  # @return [Integer, nil] if self.empty? nil is returned.
223
300
  def last_significant_element
224
301
  (i = last_significant_index) || return
225
- self[i]
302
+ @array[i]
226
303
  end
227
304
 
228
305
  # Index of the last significant (=non-empty) element.
@@ -233,7 +310,7 @@ module PlainText
233
310
  def last_significant_index
234
311
  return nil if empty?
235
312
  (0..(size-1)).to_a.reverse.each do |i|
236
- return i if self[i] && !self[i].empty? # self for sanity
313
+ return i if @array[i] && !@array[i].empty? # self for sanity
237
314
  end
238
315
  return 0
239
316
  end
@@ -298,12 +375,12 @@ module PlainText
298
375
  # @return [self, false] false if no pairs of Paras are merged, else self.
299
376
  def merge_para_if()
300
377
  arind2del = [] # Indices to delete (both paras and boundaries)
301
- each_index do |ei|
302
- break if ei >= size - 3 # 2nd last paragraph or later.
303
- next if !index_para?(ei, skip_check: true)
304
- ar1st = self.to_a[ei..ei+2]
305
- ar2nd = ((ei==0) ? nil : self[ei-1])
306
- do_merge = yield(ar1st, ar2nd, self[ei+3], ei)
378
+ @array.each_index do |ei|
379
+ break if ei >= @array.size - 3 # 2nd last paragraph or later.
380
+ next if !index_para?(ei, accept_negative: false)
381
+ ar1st = @array[ei..ei+2]
382
+ ar2nd = ((ei==0) ? nil : @array[ei-1])
383
+ do_merge = yield(ar1st, ar2nd, @array[ei+3], ei)
307
384
  return false if do_merge == :abort
308
385
  arind2del.push ei, ei+1, ei+2 if do_merge
309
386
  end
@@ -332,11 +409,9 @@ module PlainText
332
409
  # @return [self, nil] nil if nothing is merged (because of wrong indices).
333
410
  def merge_para!(*rest, use_para_index: false)
334
411
  $myd = true
335
- #warn "DEBUG:m00: #{rest}\n"
336
412
  (ranchk = build_index_range_for_merge_para!(*rest, use_para_index: use_para_index)) || (return self) # Do nothing.
337
413
  # ranchk is guaranteed to have a size of 2 or greater.
338
- #warn "DEBUG:m0: #{ranchk}\n"
339
- self[ranchk] = [self[ranchk].to_a[0..-2].join, self[ranchk.end]] # 2-elements (Para, Boundary)
414
+ @array[ranchk] = [Paragraph.new(@array[ranchk][0..-2].join), @array[ranchk.end]] # Array[Range] replaced with 2-elements (Para, Boundary)
340
415
  self
341
416
  end
342
417
 
@@ -351,13 +426,10 @@ $myd = true
351
426
  # @param use_para_index [Boolean] false
352
427
  # @return [Range, nil] nil if no range is selected.
353
428
  def build_index_range_for_merge_para!(*rest, use_para_index: false)
354
- #warn "DEBUG:b0: #{rest.inspect} to_a=#{to_a}\n"
355
429
  inary = rest.flatten
356
430
  return nil if inary.empty?
357
431
  # inary = inary[0] if like_range?(inary[0])
358
- #warn "DEBUG:b1: #{inary.inspect}\n"
359
- (ary = to_ary_positive_index(inary, to_a)) || return # Guaranteed to be an array of positive indices (sorted and uniq-ed).
360
- #warn "DEBUG:b3: #{ary}\n"
432
+ (ary = to_ary_positive_index(inary, @array)) || return # Guaranteed to be an array of positive indices (sorted and uniq-ed).
361
433
  return nil if ary.empty?
362
434
 
363
435
  # Normalize so the array contains both Paragraph and Boundaries in between.
@@ -367,7 +439,7 @@ $myd = true
367
439
  # of elements, ending with Boundary.
368
440
  if use_para_index
369
441
  ary = ary.map{|i| [i*2, i*2+1]}.flatten
370
- elsif index_para?(ary[-1], skip_check: true)
442
+ elsif index_para?(ary[-1], accept_negative: false)
371
443
  # The last index in the given Array or Range was for Paragraph (Likely case).
372
444
  ary.push(ary[-1]+1)
373
445
  end
@@ -377,7 +449,7 @@ $myd = true
377
449
 
378
450
  $myd = false
379
451
  # Exception if the first index is for Boundary and no Paragraph.
380
- raise ArgumentError, "The first index (#{ary[0]}) is not for Paragraph." if !index_para?(ary[0], skip_check: true)
452
+ raise ArgumentError, "The first index (#{ary[0]}) is not for Paragraph." if !index_para?(ary[0], accept_negative: false)
381
453
 
382
454
  i_end = [ary[-1], size-1].min
383
455
  return if i_end - ary[0] < 3 # No more than 1 para selected.
@@ -399,22 +471,22 @@ $myd = false
399
471
  # @return [self]
400
472
  def normalize!(recursive: true, ignore_array_boundary: true, compact: true, compacter: true)
401
473
  # Trim pairs of consecutive Paragraph and Boundary of nil
402
- size_parity = (size.even? ? 0 : 1)
403
- if (compact || compacter) && (size > 0+size_parity)
404
- ((size-2-size_parity)..0).each do |i|
474
+ size_parity = (@array.size.even? ? 0 : 1)
475
+ if (@array.compact || compacter) && (@array.size > 0+size_parity)
476
+ ((@array.size-2-size_parity)..0).each do |i|
405
477
  # Loop over every Paragraph
406
478
  next if i.odd?
407
- slice! i, 2 if compact && !self[i] && !self[i+1]
408
- slice! i, 2 if compacter && (!self[i] || self[i].empty?) && (!self[i+1] || self[i+1].empty?)
479
+ @array.slice! i, 2 if @array.compact && !@array[i] && !@array[i+1]
480
+ @array.slice! i, 2 if compacter && (!@array[i] || @array[i].empty?) && (!@array[i+1] || @array[i+1].empty?)
409
481
  end
410
482
  end
411
483
 
412
- i = -1
413
- map!{ |ea|
414
- i += 1
415
- normalize_core(ea, i, recursive: recursive)
484
+ @array.map!.with_index{ |ea, ind|
485
+ ax = normalize_core(ea, ind, recursive: recursive)
486
+ ax
487
+ #normalize_core(ea, ind, recursive: recursive)
416
488
  }
417
- insert_original_b4_part(size, Boundary.new('')) if size.odd?
489
+ @array.insert(@array.size, Boundary.new('')) if @array.size.odd?
418
490
  self
419
491
  end
420
492
 
@@ -428,14 +500,14 @@ $myd = false
428
500
  # @see #normalize!
429
501
  def normalize(recursive: true, ignore_array_boundary: true, compact: true, compacter: true)
430
502
  # Trims pairs of consecutive Paragraph and Boundary of nil
431
- arall = to_a
432
- size_parity = (size.even? ? 0 : 1)
433
- if (compact || compacter) && (size > 0+size_parity)
434
- ((size-2-size_parity)..0).each do |i|
503
+ arall = @array
504
+ size_parity = (@array.size.even? ? 0 : 1)
505
+ if (@array.compact || compacter) && (@array.size > 0+size_parity)
506
+ ((@array.size-2-size_parity)..0).each do |i|
435
507
  # Loop over every Paragraph
436
508
  next if i.odd?
437
- arall.slice! i, 2 if compact && !self[i] && !self[i+1]
438
- arall.slice! i, 2 if compacter && (!self[i] || self[i].empty?) && (!self[i+1] || self[i+1].empty?)
509
+ arall.slice! i, 2 if compact && !@array[i] && !@array[i+1]
510
+ arall.slice! i, 2 if compacter && (!@array[i] || @array[i].empty?) && (!@array[i+1] || @array[i+1].empty?)
439
511
  end
440
512
  end
441
513
 
@@ -454,7 +526,7 @@ $myd = false
454
526
  # @return [Array<Part, Paragraph>]
455
527
  # @see #boundaries
456
528
  def paras
457
- select.with_index { |_, i| i.even? } rescue select.each_with_index { |_, i| i.even? } # Rescue for Ruby 2.1 or earlier
529
+ @array.select.with_index { |_, i| i.even? } rescue @array.select.each_with_index { |_, i| i.even? } # Rescue for Ruby 2.1 or earlier
458
530
  # ret.freeze
459
531
  end
460
532
 
@@ -465,7 +537,7 @@ $myd = false
465
537
  # @option range [Range, nil] Range of indices of self to reparse. In Default, the entire self.
466
538
  # @return [self]
467
539
  def reparse!(rule: PlainText::ParseRule::RuleConsecutiveLbs, name: nil, range: (0..-1))
468
- insert range.begin, self.class.parse((range ? self[range] : self), rule: rule, name: name)
540
+ insert range.begin, self.class.parse((range ? @array[range] : self), rule: rule, name: name)
469
541
  self
470
542
  end
471
543
 
@@ -474,7 +546,7 @@ $myd = false
474
546
  # @param (see #reparse!)
475
547
  # @return [PlainText::Part]
476
548
  def reparse(**kwd)
477
- ret = self.dup
549
+ ret = @array.dup
478
550
  ret.reparse!(**kwd)
479
551
  ret
480
552
  end
@@ -488,17 +560,17 @@ $myd = false
488
560
  # @return [Boundary, nil] nil if a too large index is specified.
489
561
  def squash_boundary_at!(index)
490
562
  (i_pos = get_valid_ipos_for_boundary(index)) || return
491
- prt = self[i_pos-1]
563
+ prt = @array[i_pos-1]
492
564
  m = :emptify_last_boundary!
493
- self[i_pos] << prt.public_send(m) if prt.class.method_defined? m
494
- self[i_pos]
565
+ @array[i_pos] << prt.public_send(m) if prt.respond_to? m
566
+ @array[i_pos]
495
567
  end
496
568
 
497
569
 
498
570
  # Wrapper of {#squash_boundary_at!} to loop over the whole {Part}
499
571
  #
500
572
  # @return [self]
501
- def squash_boundaryies!
573
+ def squash_boundaries!
502
574
  each_boundary_with_index do |ec, i|
503
575
  squash_boundary_at!(i)
504
576
  end
@@ -506,70 +578,37 @@ $myd = false
506
578
  end
507
579
 
508
580
 
509
- # Boundary sub-class name only
510
- #
511
- # Make sure your class is a child class of Part
512
- # Otherwise this method would not be inherited, obviously.
513
- #
514
- # @example
515
- # class PlainText::Part
516
- # class Section < self
517
- # class Subsection < self; end # It must be a child class!
518
- # end
519
- # end
520
- # ss = PlainText::Part::Section::Subsection.new ["abc"]
521
- # ss.subclass_name # => "Section::Subsection"
522
- #
523
- # @return [String]
524
- # @see PlainText::Part#subclass_name
525
- def subclass_name
526
- printf "__method__=(%s)\n", __method__
527
- self.class.name.split(/\A#{Regexp.quote method(__method__).owner.name}::/)[1] || ''
528
- end
529
-
530
581
  ##########
531
582
  # Overwriting instance methods of the parent Object or Array class
532
583
  ##########
533
584
 
534
- # Original equal and plus operators of Array
535
- hsmethod = {
536
- :equal_original_b4_part => :==,
537
- :substitute_original_b4_part => :[]=,
538
- :insert_original_b4_part => :insert,
539
- :delete_at_original_b4_part => :delete_at,
540
- :slice_original_b4_part => :slice,
541
- :slice_original_b4_part! => :slice!,
542
- }
585
+ ########## Most basic methods (Object/BasicObject) ##########
543
586
 
544
- hsmethod.each_pair do |k, ea_orig|
545
- if self.method_defined?(k)
546
- # To Developer: If you see this message, switch the DEBUG flag on (-d option) and run it.
547
- warn sprintf("WARNING: Method %s#%s has been already defined, which should not be. Contact the code developer. Line %d in %s%s", self.name, k.to_s, __FILE__, __LINE__, ($DEBUG ? "\n"+caller_locations.join("\n").map{|i| " "+i} : ""))
548
- else
549
- alias_method k, ea_orig
550
- end
587
+ # @return [String]
588
+ def inspect
589
+ self.class.name + @array.inspect
551
590
  end
552
591
 
553
- alias_method :substit, :substitute_original_b4_part
554
-
555
- ########## Most basic methods (Object) ##########
592
+ # @return [Array]
593
+ def to_a
594
+ @array
595
+ end
596
+ alias_method :to_ary, :to_a
597
+ alias_method :instance, :to_a if ! self.method_defined?(:instance)
556
598
 
557
- # @return [String]
558
- def inspect
559
- self.class.name + super
599
+ # Work around because Object#dup does not dup the instance variable @array
600
+ #
601
+ # @return [PlainText::Part]
602
+ def dup
603
+ dup_or_clone(super, __method__, '@array') # defined in builtin_type.rb
560
604
  end
561
605
 
562
- # # clone
563
- # #
564
- # # Redefines Array#clone so the instance variables are also cloned.
565
- # #
566
- # # @return [self]
567
- # def clone
568
- # copied = super
569
- # val = (@sep.clone rescue @sep) # rescue in case of immutable.
570
- # copied.instance_eval{ @sep = val }
571
- # copied
572
- # end
606
+ # Work around because Object#clone does not clone the instance variable @array
607
+ #
608
+ # @return [PlainText::Part]
609
+ def clone
610
+ dup_or_clone(super, __method__, '@array') # defined in builtin_type.rb
611
+ end
573
612
 
574
613
  # Equal operator
575
614
  #
@@ -579,44 +618,23 @@ $myd = false
579
618
  #
580
619
  # @param other [Object]
581
620
  def ==(other)
582
- return false if !other.class.method_defined?(:to_ary)
621
+ return false if !other.respond_to?(:to_ary) || !other.respond_to?(:normalize!)
583
622
  %i(paras boundaries).each do |ea_m| # %i(...) defined in Ruby 2.0 and later
584
- return false if !other.class.method_defined?(ea_m) || (self.public_send(ea_m) != other.public_send(ea_m)) # public_send() defined in Ruby 2.0 (1.9?) and later
623
+ return false if !other.respond_to?(ea_m) || (self.public_send(ea_m) != other.public_send(ea_m)) # public_send() defined in Ruby 2.0 (1.9?) and later
585
624
  end
586
- super
625
+ @array == other.to_a # or you may just return true?
587
626
  end
588
627
 
589
628
 
590
- # # Multiplication operator
591
- # #
592
- # # @param other [Integer, String]
593
- # # @return as self
594
- # def *(other)
595
- # super
596
- # end
597
-
598
-
599
629
  # Plus operator
600
630
  #
601
631
  # @param other [Object]
602
632
  # @return as self
603
633
  def +(other)
604
- # ## The following is strict, but a bit redundant.
605
- # # is_para = true # Whether "other" is a Part class instance.
606
- # # %i(to_ary paras boundaries).each do |ea_m| # %i(...) defined in Ruby 2.0 and later
607
- # # is_para &&= other.class.method_defined?(ea_m)
608
- # # end
609
-
610
- # begin
611
- # other_even_odd =
612
- # ([other.paras, other.boundaries] rescue even_odd_arrays(self, size_even: true, filler: ""))
613
- # rescue NoMethodError
614
- # raise TypeError, sprintf("no implicit conversion of %s into %s", other.class.name, self.class.name)
615
- # end
616
-
617
634
  # # eg., if self is PlainText::Part::Section, the returned object is the same.
618
635
  # ret = self.class.new(self.paras+other_even_odd[0], self.boundaries+other_even_odd[1])
619
- ret = self.class.new super
636
+ raise(TypeError, "cannot operate with no #{self.class.name} instance (#{other.class.name})") if (!other.respond_to?(:to_ary) && !other.respond_to?(:normalize!))
637
+ ret = self.class.new(@array+other.to_ary)
620
638
  ret.normalize!
621
639
  end
622
640
 
@@ -626,17 +644,43 @@ $myd = false
626
644
  # @param other [Object]
627
645
  # @return as self
628
646
  def -(other)
629
- ret = self.class.new super
647
+ raise ArgumentError, "cannot operate with no {self.class.name} instance" if !other.respond_to?(:to_ary) || !other.respond_to?(:normalize!)
648
+ ret = self.class.new(@array+other.to_ary)
630
649
  ret.normalize!
631
650
  end
632
651
 
633
- # Array#<< is now undefined
634
- # (because the instances of this class must take always an even number of elements).
635
- undef_method(:<<) if method_defined?(:<<)
652
+ # if the Array method returns an Array, returns this class instance.
653
+ #
654
+ # Note that even the value is this-class instance, a new instance is created
655
+ # in default unless it is +eql?(self)+.
656
+ #
657
+ # @param obj [Object] the value to be evaluated.
658
+ # @return [Object]
659
+ # @yield [] the returned value is evaluated, if no argument is given
660
+ # @yieldreturn [Object] Either this-class instance or Object
661
+ def _return_this_or_other(obj=nil)
662
+ ret = (block_given? ? yield : obj)
663
+ (ret.respond_to?(:to_ary) && !ret.eql?(self)) ? self.class.new(ret) : ret
664
+ end
665
+ private :_return_this_or_other
636
666
 
637
- # Array#delete_at is now undefined
638
- # (because the instances of this class must have always an even number of elements).
639
- undef_method(:delete_at) if method_defined?(:delete_at)
667
+ # Basically delegates everything to Array
668
+ #
669
+ # Array#<< and Array#delete_at are undefined
670
+ # because the instances of this class must take always an even number of elements.
671
+ def method_missing(method_name, *args, **kwds)
672
+ if DISABLED_ARRAY_METHODS.include? method_name
673
+ raise NoMethodError, "no method "+method_name.to_s
674
+ end
675
+ _return_this_or_other{
676
+ @array.public_send(method_name, *args, **kwds)
677
+ }
678
+ end
679
+
680
+ # Redefines the behaviour of +respond_to?+ (essential when defining +method_missing+)
681
+ def respond_to_missing?(method_name, *rest) # include_all=false
682
+ !DISABLED_ARRAY_METHODS.include?(method_name) && @array.respond_to?(method_name, *rest) || super
683
+ end
640
684
 
641
685
  # Returns a partial Part-Array (or Object, if a single Integer is specified)
642
686
  #
@@ -649,7 +693,7 @@ $myd = false
649
693
  # @return [Object]
650
694
  def [](arg1, *rest)
651
695
  arg2 = rest[0]
652
- return super(arg1) if !arg2 && !arg1.class.method_defined?(:exclude_end?)
696
+ return @array[arg1] if !arg2 && !arg1.respond_to?(:exclude_end?)
653
697
 
654
698
  check_bracket_args_type_error(arg1, arg2) # Args are now either (Int, Int) or (Range)
655
699
 
@@ -657,11 +701,13 @@ $myd = false
657
701
  size2ret = size2extract(arg1, arg2, ignore_error: true) # maybe nil (if the index is too small).
658
702
  raise ArgumentError, ERR_MSGS[:even_num]+" "+ERR_MSGS[:use_to_a] if size2ret.odd?
659
703
  begin
660
- raise ArgumentError, "odd index is not allowed as the starting index for #{self.class.name}. It must be even. "+ERR_MSGS[:use_to_a] if positive_array_index_checked(arg1, self).odd?
704
+ raise ArgumentError, "odd index is not allowed as the starting index for #{self.class.name}. It must be even. "+ERR_MSGS[:use_to_a] if positive_array_index_checked(arg1, @array).odd?
661
705
  rescue TypeError, IndexError
662
706
  # handled by super
663
707
  end
664
- return super
708
+ return _return_this_or_other{
709
+ @array[arg1, arg2]
710
+ }
665
711
  end
666
712
 
667
713
  begin
@@ -675,13 +721,17 @@ $myd = false
675
721
 
676
722
  # The end is smaller than the begin in the positive index. Empty instance of this class is returned.
677
723
  if rang.end < rang.begin
678
- return super
724
+ return _return_this_or_other{
725
+ @array[arg1, *rest]
726
+ }
679
727
  end
680
728
 
681
729
  raise RangeError, "odd index is not allowed as the starting Range for #{sefl.class.name}. It must be even. "+ERR_MSGS[:use_to_a] if rang.begin.odd?
682
730
  size2ret = size2extract(rang, skip_renormalize: true)
683
731
  raise ArgumentError, ERR_MSGS[:even_num]+" "+ERR_MSGS[:use_to_a] if size2ret.odd?
684
- super
732
+ _return_this_or_other{
733
+ @array[arg1, *rest]
734
+ }
685
735
  end
686
736
 
687
737
 
@@ -698,17 +748,23 @@ $myd = false
698
748
  end
699
749
 
700
750
  # Simple substitution to a single element
701
- return super(arg1, val) if !arg2 && !arg1.class.method_defined?(:exclude_end?)
751
+ if !arg2 && !arg1.respond_to?(:exclude_end?)
752
+ return _return_this_or_other{
753
+ @array[arg1] = val
754
+ }
755
+ end
702
756
 
703
757
  check_bracket_args_type_error(arg1, arg2) # Args are now either (Int, Int) or (Range)
704
758
 
705
- # raise TypeError, "object to replace must be Array type with an even number of elements." if !val.class.method_defined?(:to_ary) || val.size.odd?
759
+ # raise TypeError, "object to replace must be Array type with an even number of elements." if !val.respond_to?(:to_ary) || val.size.odd?
706
760
 
707
761
  vals = (val.to_ary rescue [val])
708
762
  if arg2
709
763
  size2delete = size2extract(arg1, arg2, ignore_error: true) # maybe nil (if the index is too small).
710
764
  raise ArgumentError, "odd-even parity of size of array to replace must be identical to that to slice." if size2delete && ((size2delete % 2) != (vals.size % 2))
711
- return super
765
+ return _return_this_or_other{
766
+ @array[arg1] = val
767
+ }
712
768
  end
713
769
 
714
770
  begin
@@ -727,41 +783,53 @@ $myd = false
727
783
 
728
784
  size2delete = size2extract(rang, skip_renormalize: true)
729
785
  raise ArgumentError, "odd-even parity of size of array to replace must be identical to that to slice." if size2delete && ((size2delete % 2) != (vals.size % 2))
730
- ret = super
786
+ @array[arg1] = val
731
787
 
732
788
  # The result may not be in an even number anymore. Correct it.
733
- Boundary.insert_original_b4_part(size, "") if size.odd?
789
+ Boundary.insert(@array.size, "") if @array.size.odd? ############## is it correct???
734
790
 
735
791
  # Original method may fill some elements of the array with String or even nil.
736
792
  normalize!
737
- ret
738
793
  end
739
794
 
740
795
  # Array#insert
741
796
  #
742
797
  # The most basic method to add/insert elements to self. Called from {#[]=} and {#push}, for example.
743
798
  #
744
- # If ind is greater than size, a number of "", as opposed to nil, are inserted.
799
+ # The maximum permitted position index is the size of self,s
800
+ # unlike +Array#insert+, which allows an index larger than the size,
801
+ # in which case +nil+ is inserted in between.
802
+ #
803
+ # The number of the inserted elements must be always even.
745
804
  #
746
- # @param ind [Index]
747
- # @param rest [Array] This must have an even number of arguments, unless ind is larger than the array size and an odd number.
748
- # @option primitive: [String] if true (Def: false), the original {#insert_original_b4_part} is called.
805
+ # @param ind [Index] +rest+ is inserted *before* +ind+ if non-negative and *after* if negative.
806
+ # @param rest [Array] This must have an even number of arguments.
807
+ # @option primitive: [Boolean] if true (Def: false), no wrapper action is performed.
749
808
  # @return [self]
750
809
  def insert(ind, *rest, primitive: false)
751
- return insert_original_b4_part(ind, *rest) if primitive
752
- ipos = positive_array_index_checked(ind, self)
753
- if rest.size.even? && (ipos > size - 1) && ipos.even? # ipos.even? is equivalent to index_para?(ipos), i.e., "is the index for Paragraph?"
754
- raise ArgumentError, sprintf("number of arguments (%d) must be odd for index %s.", rest.size, ind)
755
- elsif rest.size.odd? && (ipos <= size - 1)
810
+ #return insert_original_b4_part(ind, *rest) if primitive
811
+ return _return_this_or_other(@array.insert(ind, *rest)) if primitive
812
+
813
+ ipos = positive_array_index_checked(ind, @array) # IndexError may be raised.
814
+ ipos += 1 if ind < 0
815
+ # If ipos is negative, it should be inserted AFTER the index according to Array#index,
816
+ # whereas if ipos is non-negative, inserted BEFORE.
817
+
818
+ if ipos > @array.size
819
+ raise IndexError, sprintf("index (%s) too large for array: maximum: %d.", ipos, @array.size)
820
+ elsif rest.size.odd?
756
821
  raise ArgumentError, sprintf("number of arguments (%d) must be even.", rest.size)
757
822
  end
758
823
 
759
- if ipos >= size
760
- rest = Array.new(ipos - size).map{|i| ""} + rest
761
- ipos = size
762
- end
824
+ return self if rest.empty?
763
825
 
764
- super(ipos, rest)
826
+ begin
827
+ _return_this_or_other{
828
+ @array.insert(ipos, *rest) && normalize! && self
829
+ }
830
+ rescue PlainText::PartNormalizeError => err
831
+ raise TypeError, err.message
832
+ end
765
833
  end
766
834
 
767
835
 
@@ -771,24 +839,24 @@ $myd = false
771
839
  #
772
840
  # @param arg1 [Integer, Range]
773
841
  # @option arg2 [Integer, NilClass]
774
- # @option primitive: [Boolean] if true (Def: false), the original {#insert_original_b4_part} is called.
842
+ # @option primitive: [Boolean] if true (Def: false), no wrapper action is performed.
775
843
  # @return as self or NilClass
776
844
  def slice!(arg1, *rest, primitive: false)
777
- return slice_original_b4_part!(arg1, *rest) if primitive
845
+ #return slice_original_b4_part!(arg1, *rest) if primitive
846
+ return _return_this_or_other(@array.slice(ind, *rest)) if primitive
778
847
 
779
848
  arg2 = rest[0]
780
849
 
781
850
  # Simple substitution to a single element
782
- raise ArgumentError, ERR_MSGS[:even_num] if !arg2 && !arg1.class.method_defined?(:exclude_end?)
851
+ raise ArgumentError, ERR_MSGS[:even_num] if !arg2 && !arg1.respond_to?(:exclude_end?)
783
852
 
784
853
  check_bracket_args_type_error(arg1, arg2) # Args are now either (Int, Int) or (Range)
785
854
 
786
855
  if arg2
787
856
  size2delete = size2extract(arg1, arg2, ignore_error: true) # maybe nil (if the index is too small).
788
- # raise ArgumentError, ERR_MSGS[:even_num] if arg2.to_int.odd?
789
857
  raise ArgumentError, ERR_MSGS[:even_num] if size2delete && size2delete.odd?
790
858
  raise ArgumentError, "odd index is not allowed as the starting Range for #{self.class.name}. It must be even." if arg1.odd? # Because the returned value is this class of instance.
791
- return super(arg1, *rest)
859
+ return _return_this_or_other(@array.slice!(arg1, *rest))
792
860
  end
793
861
 
794
862
  begin
@@ -799,12 +867,13 @@ $myd = false
799
867
 
800
868
  raise RangeError if rang.begin < 0 || rang.end < 0
801
869
 
802
- return super(arg1, *rest) if (rang.begin > rang.end) # nil or [] is returned
870
+ return _return_this_or_other(@array.slice!(arg1, *rest)) if (rang.begin > rang.end) # nil or [] is returned
871
+
803
872
 
804
873
  size2delete = size2extract(rang, skip_renormalize: true)
805
874
  raise ArgumentError, ERR_MSGS[:even_num] if size2delete && size2delete.odd?
806
875
  raise ArgumentError, "odd index is not allowed as the starting Range for #{self.class.name}. It must be even." if rang.begin.odd? # Because the returned value is this class of instance.
807
- super(arg1, *rest)
876
+ _return_this_or_other(@array.slice!(arg1, *rest))
808
877
  end
809
878
 
810
879
 
@@ -816,8 +885,8 @@ $myd = false
816
885
  #
817
886
  # @return [self, NilClass]
818
887
  def compact!
819
- ret = super
820
- ret ? ret.normalize!(recursive: false) : ret
888
+ ret = @array.send(__method__)
889
+ ret ? normalize!(recursive: false) : ret
821
890
  end
822
891
 
823
892
  # Array#concat
@@ -827,7 +896,7 @@ $myd = false
827
896
  # @param rest [Array<Array>]
828
897
  # @return [self]
829
898
  def concat(*rest)
830
- insert(size, *(rest.sum([])))
899
+ insert(@array.size, *(rest.sum([])))
831
900
  end
832
901
 
833
902
  # Array#push
@@ -863,9 +932,9 @@ $myd = false
863
932
  # @raise [TypeError] if not conforms.
864
933
  def check_bracket_args_type_error(arg1, arg2=nil)
865
934
  if arg2
866
- raise TypeError, sprintf("no implicit conversion of #{arg2.class} into Integer") if !arg2.class.method_defined?(:to_int)
935
+ raise TypeError, sprintf("no implicit conversion of #{arg2.class} into Integer") if !arg2.respond_to?(:to_int)
867
936
  else
868
- raise TypeError if !arg1.class.method_defined?(:exclude_end?)
937
+ raise TypeError if !arg1.respond_to?(:exclude_end?)
869
938
  end
870
939
  end
871
940
  private :check_bracket_args_type_error
@@ -875,11 +944,11 @@ $myd = false
875
944
  #
876
945
  # @return [Boundary] all the descendants' last Boundaries merged.
877
946
  def emptify_last_boundary!
878
- return Boundary::Empty.dup if size == 0
947
+ return Boundary::Empty.dup if @array.size == 0
879
948
  ret = ""
880
- ret << prt.public_send(__method__) if prt.class.method_defined? __method__
881
- ret << self[-1]
882
- self[-1] = Boundary::Empty.dup
949
+ ret << prt.public_send(__method__) if prt.respond_to? __method__
950
+ ret << @array[-1]
951
+ @array[-1] = Boundary::Empty.dup
883
952
  ret
884
953
  end
885
954
  private :emptify_last_boundary!
@@ -890,8 +959,8 @@ $myd = false
890
959
  # @param index [Integer]
891
960
  # @return [Integer, nil] nil if a too large index is specified.
892
961
  def get_valid_ipos_for_boundary(index)
893
- i_pos = positive_array_index_checked(index, self)
894
- raise ArgumentError, "Index #{index} specified was for Para, which should be for Boundary." if index_para?(i_pos, skip_check: true)
962
+ i_pos = positive_array_index_checked(index, @array)
963
+ raise ArgumentError, "Index #{index} specified was for Para, which should be for Boundary." if index_para?(i_pos, accept_negative: false)
895
964
  (i_pos > size - 1) ? nil : i_pos
896
965
  end
897
966
  private :get_valid_ipos_for_boundary
@@ -899,44 +968,44 @@ $myd = false
899
968
 
900
969
  # Core routine for {#map_boundary} and similar.
901
970
  #
902
- # @option map opts: [Boolean] if true (Default), map is performed. Else just each.
971
+ # @option do_map opts: [Boolean] if true (Default), map is performed. Else just each.
903
972
  # @option with_index: [Boolean] if true (Default: false), yield with also index
904
973
  # @option recursive: [Boolean] if true (Default), map is performed recursively.
905
974
  # @return as self if map: is true, else void
906
- def map_boundary_core(map: true, with_index: false, recursive: true, **kwd, &bl)
975
+ def map_boundary_core(do_map: true, with_index: false, recursive: true, **kwd, &bl)
907
976
  ind = -1
908
- arnew = map{ |ec|
977
+ arnew = @array.map{ |ec|
909
978
  ind += 1
910
- if recursive && index_para?(ind, skip_check: true) && ec.class.method_defined?(__method__)
979
+ if recursive && index_para?(ind, accept_negative: false) && ec.respond_to?(__method__)
911
980
  ec.public_send(__method__, recursive: true, **kwd, &bl)
912
- elsif !index_para?(ind, skip_check: true)
981
+ elsif !index_para?(ind, accept_negative: false)
913
982
  with_index ? yield(ec, ind) : yield(ec)
914
983
  else
915
984
  ec
916
985
  end
917
986
  }
918
- self.class.new arnew, recursive: recursive, **kwd if map
987
+ self.class.new arnew, recursive: recursive, **kwd if do_map
919
988
  end
920
989
  private :map_boundary_core
921
990
 
922
991
  # Core routine for {#map_para}
923
992
  #
924
- # @option map: [Boolean] if true (Default), map is performed. Else just each.
993
+ # @option do_map: [Boolean] if true (Default), map is performed. Else just each.
925
994
  # @option with_index: [Boolean] if true (Default: false), yield with also index
926
995
  # @option recursive: [Boolean] if true (Default), map is performed recursively.
927
996
  # @return as self
928
997
  # @see #initialize for the other options (:compact and :compacter)
929
- def map_para_core(map: true, with_index: false, recursive: true, **kwd, &bl)
998
+ def map_para_core(do_map: true, with_index: false, recursive: true, **kwd, &bl)
930
999
  ind = -1
931
1000
  new_paras = paras.map{ |ec|
932
1001
  ind += 1
933
- if recursive && ec.class.method_defined?(__method__)
1002
+ if recursive && ec.respond_to?(__method__)
934
1003
  ec.public_send(__method__, recursive: true, **kwd, &bl)
935
1004
  else
936
1005
  with_index ? yield(ec, ind) : yield(ec)
937
1006
  end
938
1007
  }
939
- self.class.new new_paras, boundaries, recursive: recursive, **kwd if map
1008
+ self.class.new new_paras, boundaries, recursive: recursive, **kwd if do_map
940
1009
  end
941
1010
  private :map_para_core
942
1011
 
@@ -948,18 +1017,25 @@ $myd = false
948
1017
  # @option ignore_array_boundary: [Boolean] if true (Default), even if a Boundary element (odd-numbered index) is an Array, ignore it.
949
1018
  # @return [Part, Paragraph, Boundary]
950
1019
  def normalize_core(ea, i, recursive: true, ignore_array_boundary: true)
951
- if ea.class.method_defined?(:to_ary)
952
- if index_para?(i, skip_check: true) || ignore_array_boundary
1020
+ if ea.respond_to?(:to_ary)
1021
+ if index_para?(i, accept_negative: false) || ignore_array_boundary
953
1022
  (/\APlainText::/ =~ ea.class.name && defined?(ea.normalize)) ? (recursive ? ea.normalize : ea) : self.class.new(ea, recursive: recursive)
954
1023
  else
955
- raise "Index ({#i}) is an Array or its child, but it should be Boundary or String."
1024
+ raise "Element at index ({#i}) is a Part or Array, but it should be Boundary or String."
1025
+ end
1026
+ elsif ea.respond_to?(:to_str)
1027
+ if i.even? && self.class.boundary?(ea)
1028
+ msg = "not be Boundary"
1029
+ elsif i.odd? && self.class.paragraph?(ea)
1030
+ msg = "be neither Part nor Paragraph"
956
1031
  end
957
- elsif ea.class.method_defined?(:to_str)
1032
+ raise PlainText::PartNormalizeError, sprintf("Element at index=(%d) must %s", i, msg) if msg
1033
+
958
1034
  if /\APlainText::/ =~ ea.class.name
959
1035
  # Paragraph or Boundary
960
- ea.unicode_normalize
1036
+ ea.unicode_normalize # Unicode-normalized new object
961
1037
  else
962
- if index_para?(i, skip_check: true)
1038
+ if index_para?(i, accept_negative: false)
963
1039
  Paragraph.new(ea.unicode_normalize || "")
964
1040
  else
965
1041
  Boundary.new( ea.unicode_normalize || "")
@@ -979,7 +1055,7 @@ $myd = false
979
1055
  # @raise [IndexError] if too negative index is specified.
980
1056
  def normalize_index_range(rng, **kwd)
981
1057
  # NOTE to developers: (0..-1).to_a returns [] (!)
982
- arpair = [rng.begin, rng.end].to_a.map{ |i| positive_array_index_checked(i, self, **kwd) }
1058
+ arpair = [rng.begin, rng.end].to_a.map{ |i| positive_array_index_checked(i, @array, **kwd) }
983
1059
  arpair[1] -= 1 if rng.exclude_end?
984
1060
  (arpair[0]..arpair[1])
985
1061
  end
@@ -1002,11 +1078,11 @@ $myd = false
1002
1078
  # @return [Integer, NilClass] nil if an Error is raised with ignore_error being true
1003
1079
  def size2extract(arg1, arg2=nil, ignore_error: false, skip_renormalize: false, **kwd)
1004
1080
  begin
1005
- if arg1.class.method_defined? :exclude_end?
1081
+ if arg1.respond_to? :exclude_end?
1006
1082
  rng = arg1
1007
1083
  rng = normalize_index_range(rng, **kwd) if !skip_renormalize
1008
1084
  else
1009
- ipos = positive_array_index_checked(arg1, self)
1085
+ ipos = positive_array_index_checked(arg1, @array)
1010
1086
  rng = (ipos..(ipos+arg2.to_int-1))
1011
1087
  end
1012
1088
  return 0 if rng.begin > size-1
@@ -1024,48 +1100,13 @@ $myd = false
1024
1100
  end # module PlainText
1025
1101
 
1026
1102
 
1027
- ####################################################
1028
- # Modifies Array
1029
- ####################################################
1030
-
1031
- class Array
1032
-
1033
- # Original equal and plus operators of Array
1034
- hsmethod = {
1035
- :equal_original_b4_part? => :== ,
1036
- # :plus_operator_original_b4_part => :+
1037
- }
1038
-
1039
- hsmethod.each_pair do |k, ea_orig|
1040
- if self.method_defined?(k)
1041
- # To Developer: If you see this message, switch the DEBUG flag on (-d option) and run it.
1042
- warn sprintf("WARNING: Method %s#%s has been already defined, which should not be. Contact the code developer. Line %d in %s%s", self.name, k.to_s, __FILE__, __LINE__, ($DEBUG ? "\n"+caller_locations.join("\n").map{|i| " "+i} : ""))
1043
- else
1044
- alias_method k, ea_orig
1045
- end
1046
- end
1047
-
1048
- # Equal operator modified to deal with {PlainText::Part}
1049
- #
1050
- # @param other [Object]
1051
- def ==(other)
1052
- return false if !other.class.method_defined?(:to_ary)
1053
- %i(paras boundaries).each do |ea_m| # %i(...) defined in Ruby 2.0 and later
1054
- return equal_original_b4_part?(other) if !other.class.method_defined?(ea_m)
1055
- return false if !self.class.method_defined?(ea_m) || (self.public_send(ea_m) != other.public_send(ea_m)) # public_send() defined in Ruby 2.0 (1.9?) and later
1056
- end
1057
- true
1058
- end
1059
-
1060
- end
1061
-
1062
1103
  ####################################################
1063
1104
  # require (after the module is defined)
1064
1105
  ####################################################
1065
1106
 
1066
- require 'plain_text/part/paragraph'
1067
- require 'plain_text/part/boundary'
1068
- require "plain_text/parse_rule"
1107
+ require_relative 'part/paragraph'
1108
+ require_relative 'part/boundary'
1109
+ require_relative "parse_rule"
1069
1110
 
1070
1111
  #######
1071
1112
  # idea