Class: PlainText::Part
- Inherits:
-
Object
- Object
- PlainText::Part
- Includes:
- BuiltinType, Util
- Defined in:
- lib/plain_text/part.rb,
lib/plain_text/part/boundary.rb,
lib/plain_text/part/paragraph.rb,
lib/plain_text/part/string_type.rb
Overview
methods
-
flatten
Class to represent a Chapter-like entity, which behaves like an Array
An instance of this class contains always an even number of elements, either another Part instance or Paragraph-type String-like instance, followed by a Boundary-type String-like instance. The first element is always a former and the last element is always a latter.
Essentially, the instance of this class holds the order information between sub-Part-s (< Array) and/or Paragraph-s (< String) and Boundary-s (< String).
An example instance looks like this:
Part (
(0) Part::Paragraph::Empty,
(1) Part::Boundary::General,
(2) Part::ArticleHeader(
(0) Part::Paragraph::Title,
(1) Part::Boundary::Empty
),
(3) Part::Boundary::TitleMain,
(4) Part::ArticleMain(
(0) Part::ArticleSection(
(0) Part::Paragraph::Title,
(1) Part::Boundary::General,
(2) Part::Paragraph::General,
(3) Part::Boundary::General,
(4) Part::ArticleSubSection(...),
(5) Part::Boundary::General,
(6) Part::Paragraph::General,
(7) Part::Boundary::Empty
),
(1) Part::Boundary::General,
(2) Part::Paragraph::General,
(3) Part::Boundary::Empty
),
(5) Part::Boundary::General
)
A Section (Part) always has an even number of elements: pairs of a Para (Part|Paragraph) and Boundary in this order.
This class behaves like an Array and so Array methods that usually return an Array returns an instance of this class, such as pt[0..-2]
. However, not all Array methods are accepted in the same way. For example, for the instance pt
, pt[0..-2]
raises an Exception because it would violate the principle of the instance of this class having always an even number of elements. Use to_a
to obtain a standard Array; e.g., pt.to_a[0..-2]
returns an Array with an odd number of elements.
Some destructive Array operations, most notably #delete
, #delete_if
, #reject!
, #select!
, #filter!
, #keep_if
, #flatten!
, #uniq!
may alter the content in a way it breaks the self-consistency of the object. Use it at your own risk, if you wish (or don’t). Such operations may become prohibited in the future release. #<< and #delete_at are currently disabled.
An instance of this class is always non-equal to that of the standard Array class. To compare it at the Array level, convert a Part class instance into Array with #to_a first: pt.to_a == my_array
For CRUD of elements (contents) of an instance, the following methods are most basic:
-
Create:
-
Use
PlainText::Part.new
(see initialize)
-
-
Read:
-
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.
-
#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.
-
-
Update:
-
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.
-
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
-
-
Delete:
-
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.
-
#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!.
-
-
Defined Under Namespace
Modules: StringType Classes: Boundary, Paragraph
Constant Summary collapse
- DISABLED_ARRAY_METHODS =
Array methods that are disabled for this class.
%i(<< delete_at)
Class Method Summary collapse
-
.boundary?(other) ⇒ Boolean
Returns true if it is Boundary.
-
.builtin_string?(other) ⇒ Boolean
Returns true if it is built-in String or similar.
-
.paragraph?(other) ⇒ Boolean
Returns true if it is Paragraph.
-
.parse(inprm, rule: PlainText::ParseRule::RuleConsecutiveLbs) ⇒ PlainText::Part
Parses a given string (or Part) and returns this class of instance.
-
.part?(other) ⇒ Boolean
Returns true if it is Part.
Instance Method Summary collapse
-
#+(other) ⇒ Object
Plus operator.
-
#-(other) ⇒ Object
Minus operator.
-
#==(other) ⇒ Object
Equal operator.
-
#[](arg1, *rest) ⇒ Object
(also: #slice)
Returns a partial Part-Array (or Object, if a single Integer is specified).
-
#[]=(arg1, *rest) ⇒ Object
Replaces some of the Array content.
-
#boundaries ⇒ Array<Boundary>
Returns an array of boundaries (odd-number-index elements), consisting of Boundaries.
-
#boundary? ⇒ Boolean
Unique instance methods (not existing in Array).
-
#boundary_extended_at(index) ⇒ Array?
returns all the Boundaries immediately before the index and at it as an Array.
-
#clone ⇒ PlainText::Part
Work around because Object#clone does not clone the instance variable @array.
-
#compact! ⇒ self, NilClass
Array#compact!.
-
#concat(*rest) ⇒ self
Array#concat.
-
#deepcopy ⇒ Part
Returns a dup-ped instance with all the Arrays and Strings dup-ped.
-
#dup ⇒ PlainText::Part
Work around because Object#dup does not dup the instance variable @array.
-
#each_boundary_with_index(**kwd, &bl) ⇒ Object
each method for boundaries only, providing also the index (always an odd number) to the block.
-
#each_para_with_index(**kwd, &bl) ⇒ Object
each method for Paras only, providing also the index (always an even number) to the block.
-
#first_significant_element ⇒ Integer?
The first significant (=non-empty) element.
-
#first_significant_index ⇒ Integer?
Index of the first significant (=non-empty) element.
-
#index_para?(i, accept_negative: true) ⇒ Boolean
True if the index should be semantically for Paragraph?.
-
#initialize(arin, boundaries = nil, recursive: true, compact: true, compacter: true) ⇒ self
constructor
Constructor.
-
#insert(ind, *rest, primitive: false) ⇒ self
Array#insert.
- #inspect ⇒ String
-
#last_significant_element ⇒ Integer?
The last significant (=non-empty) element.
-
#last_significant_index ⇒ Integer?
Index of the last significant (=non-empty) element.
-
#map_boundary(**kwd, &bl) ⇒ Object
map method for boundaries only, returning a copied self.
-
#map_boundary_with_index(**kwd, &bl) ⇒ Object
map method for boundaries only, providing also the index (always an odd number) to the block, returning a copied self.
-
#map_para(**kwd, &bl) ⇒ Object
map method for Paras only, returning a copied self.
-
#map_para_with_index(**kwd, &bl) ⇒ Object
map method for paras only, providing also the index (always an even number) to the block, returning a copied self.
-
#merge_para!(*rest, use_para_index: false) ⇒ self?
merge multiple paragraphs.
-
#merge_para_if {|ary, b1, b2, i| ... } ⇒ self, false
merge Paras if they satisfy the conditions.
-
#method_missing(method_name, *args, **kwds) ⇒ Object
Basically delegates everything to Array.
-
#normalize(recursive: true, ignore_array_boundary: true, compact: true, compacter: true) ⇒ Object
Non-destructive version of #normalize!.
-
#normalize!(recursive: true, ignore_array_boundary: true, compact: true, compacter: true) ⇒ self
Normalize the content, making sure it has an even number of elements.
- #paragraph? ⇒ Boolean
-
#paras ⇒ Array<Part, Paragraph>
Returns an array of Paras (even-number-index elements), consisting of Part and/or Paragraph.
- #part? ⇒ Boolean
-
#push(*rest) ⇒ self
(also: #append)
Array#push.
-
#reparse(**kwd) ⇒ PlainText::Part
Non-destructive version of #reparse!.
-
#reparse!(rule: PlainText::ParseRule::RuleConsecutiveLbs, name: nil, range: (0..-1)) ⇒ self
Reparses self or a part of it.
-
#respond_to_missing?(method_name, *rest) ⇒ Boolean
Redefines the behaviour of
respond_to?
(essential when definingmethod_missing
). -
#slice!(arg1, *rest, primitive: false) ⇒ Object
Delete elements and return the deleted content or nil if nothing is deleted.
-
#squash_boundaries! ⇒ self
Wrapper of #squash_boundary_at! to loop over the whole Part.
-
#squash_boundary_at!(index) ⇒ Boundary?
Emptifies all the Boundaries immediately before the Boundary at the index and squashes it to the one at it.
- #to_a ⇒ Array (also: #to_ary, #instance)
Methods included from BuiltinType
Constructor Details
#initialize(arin, boundaries = nil, recursive: true, compact: true, compacter: true) ⇒ self
Constructor
New String-type objects are always created, i.e., regardless of whether the input is a pure String or Paragraph etc, a new Paragraph is always created from PlainText::Part::StringType#to_s. Therefore, even if one of the String given to the argument is destructively modified, it does not affect the generated PlainText::Part object. This also means that if the input Paragraph has special singloton methods or instance variables, the information will be lost.
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/plain_text/part.rb', line 119 def initialize(arin, boundaries=nil, recursive: true, compact: true, compacter: true) 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 if !boundaries @array = arin.clone #super(arin) else raise ArgumentError, "Two main Arrays must have the same size." if arin.size != boundaries.size armain = [] arin.each_with_index do |ea_e, i| armain << ea_e armain << (boundaries[i] || Boundary.new('')) end @array = armain #super armain end begin normalize!(recursive: recursive, compact: compact, compacter: compacter) rescue PlainText::PartNormalizeError => err raise TypeError, err. end end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method_name, *args, **kwds) ⇒ Object
Basically delegates everything to Array
Array#<< and Array#delete_at are undefined because the instances of this class must take always an even number of elements.
671 672 673 674 675 676 677 678 |
# File 'lib/plain_text/part.rb', line 671 def method_missing(method_name, *args, **kwds) if DISABLED_ARRAY_METHODS.include? method_name raise NoMethodError, "no method "+method_name.to_s end _return_this_or_other{ @array.public_send(method_name, *args, **kwds) } end |
Class Method Details
.boundary?(other) ⇒ Boolean
Returns true if it is Boundary
159 160 161 |
# File 'lib/plain_text/part.rb', line 159 def self.boundary?(other) _part_paragraph_boundary?(other, __method__) end |
.builtin_string?(other) ⇒ Boolean
Returns true if it is built-in String or similar
169 170 171 |
# File 'lib/plain_text/part.rb', line 169 def self.builtin_string?(other) other.respond_to?(:to_str) && !boundary? && !paragraph? end |
.paragraph?(other) ⇒ Boolean
Returns true if it is Paragraph
164 165 166 |
# File 'lib/plain_text/part.rb', line 164 def self.paragraph?(other) _part_paragraph_boundary?(other, __method__) end |
.parse(inprm, rule: PlainText::ParseRule::RuleConsecutiveLbs) ⇒ PlainText::Part
Parses a given string (or PlainText::Part) and returns this class of instance.
148 149 150 151 |
# File 'lib/plain_text/part.rb', line 148 def self.parse(inprm, rule: PlainText::ParseRule::RuleConsecutiveLbs) arin = rule.apply(inprm) self.new(arin) end |
.part?(other) ⇒ Boolean
Returns true if it is PlainText::Part
154 155 156 |
# File 'lib/plain_text/part.rb', line 154 def self.part?(other) _part_paragraph_boundary?(other, __method__) end |
Instance Method Details
#+(other) ⇒ Object
Plus operator
633 634 635 636 637 638 639 |
# File 'lib/plain_text/part.rb', line 633 def +(other) # # eg., if self is PlainText::Part::Section, the returned object is the same. # ret = self.class.new(self.paras+other_even_odd[0], self.boundaries+other_even_odd[1]) raise(TypeError, "cannot operate with no #{self.class.name} instance (#{other.class.name})") if (!other.respond_to?(:to_ary) && !other.respond_to?(:normalize!)) ret = self.class.new(@array+other.to_ary) ret.normalize! end |
#-(other) ⇒ Object
Minus operator
646 647 648 649 650 |
# File 'lib/plain_text/part.rb', line 646 def -(other) raise ArgumentError, "cannot operate with no {self.class.name} instance" if !other.respond_to?(:to_ary) || !other.respond_to?(:normalize!) ret = self.class.new(@array+other.to_ary) ret.normalize! end |
#==(other) ⇒ Object
Equal operator
Unless both are kind of Part instances, false is returned. If you want to make comparison in the Array level, do
p1.to_a == a1.to_a
620 621 622 623 624 625 626 |
# File 'lib/plain_text/part.rb', line 620 def ==(other) return false if !other.respond_to?(:to_ary) || !other.respond_to?(:normalize!) %i(paras boundaries).each do |ea_m| # %i(...) defined in Ruby 2.0 and later 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 end @array == other.to_a # or you may just return true? end |
#[](arg1, *rest) ⇒ Object Also known as: slice
Returns a partial Part-Array (or Object, if a single Integer is specified)
Because the returned object is this class of instance (when a pair of Integer or Range is specified), only an even number of elements, starting from an even number of index, is allowed.
694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 |
# File 'lib/plain_text/part.rb', line 694 def [](arg1, *rest) arg2 = rest[0] return @array[arg1] if !arg2 && !arg1.respond_to?(:exclude_end?) check_bracket_args_type_error(arg1, arg2) # Args are now either (Int, Int) or (Range) if arg2 size2ret = size2extract(arg1, arg2, ignore_error: true) # maybe nil (if the index is too small). raise ArgumentError, ERR_MSGS[:even_num]+" "+ERR_MSGS[:use_to_a] if size2ret.odd? begin 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? rescue TypeError, IndexError # handled by super end return _return_this_or_other{ @array[arg1, arg2] } end begin rang = normalize_index_range(arg1) rescue IndexError #=> err return nil # raise RangeError, err.message end raise RangeError if rang.begin < 0 || rang.end < 0 # The end is smaller than the begin in the positive index. Empty instance of this class is returned. if rang.end < rang.begin return _return_this_or_other{ @array[arg1, *rest] } end 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? size2ret = size2extract(rang, skip_renormalize: true) raise ArgumentError, ERR_MSGS[:even_num]+" "+ERR_MSGS[:use_to_a] if size2ret.odd? _return_this_or_other{ @array[arg1, *rest] } end |
#[]=(arg1, *rest) ⇒ Object
Replaces some of the Array content.
743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 |
# File 'lib/plain_text/part.rb', line 743 def []=(arg1, *rest) if rest.size == 1 arg2, val = [nil, rest[-1]] else arg2, val = rest end # Simple substitution to a single element if !arg2 && !arg1.respond_to?(:exclude_end?) return _return_this_or_other{ @array[arg1] = val } end check_bracket_args_type_error(arg1, arg2) # Args are now either (Int, Int) or (Range) # raise TypeError, "object to replace must be Array type with an even number of elements." if !val.respond_to?(:to_ary) || val.size.odd? vals = (val.to_ary rescue [val]) if arg2 size2delete = size2extract(arg1, arg2, ignore_error: true) # maybe nil (if the index is too small). 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)) return _return_this_or_other{ @array[arg1] = val } end begin rang = normalize_index_range(arg1) rescue IndexError => err raise RangeError, err. end raise RangeError if rang.begin < 0 || rang.end < 0 # The end is smaller than the begin in the positive index. It is the same as insert (default in Ruby), except it returns the replaced Object (which may not be an Array). if rang.end < rang.begin insert(arg1, *vals) return val end size2delete = size2extract(rang, skip_renormalize: true) 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)) @array[arg1] = val # The result may not be in an even number anymore. Correct it. Boundary.insert(@array.size, "") if @array.size.odd? ############## is it correct??? # Original method may fill some elements of the array with String or even nil. normalize! end |
#boundaries ⇒ Array<Boundary>
Returns an array of boundaries (odd-number-index elements), consisting of Boundaries
204 205 206 |
# File 'lib/plain_text/part.rb', line 204 def boundaries @array.select.with_index { |_, i| i.odd? } rescue @array.select.each_with_index { |_, i| i.odd? } # Rescue for Ruby 2.1 or earlier end |
#boundary? ⇒ Boolean
Unique instance methods (not existing in Array)
188 189 190 |
# File 'lib/plain_text/part.rb', line 188 def boundary? false end |
#boundary_extended_at(index) ⇒ Array?
returns all the Boundaries immediately before the index and at it as an Array
See #squash_boundary_at! to squash them.
214 215 216 217 218 219 220 |
# File 'lib/plain_text/part.rb', line 214 def boundary_extended_at(index) (i_pos = get_valid_ipos_for_boundary(index)) || return arret = [] prt = @array[i_pos-1] arret = prt.public_send(__method__, -1) if prt.respond_to? __method__ arret << @array[index] end |
#clone ⇒ PlainText::Part
Work around because Object#clone does not clone the instance variable @array
609 610 611 |
# File 'lib/plain_text/part.rb', line 609 def clone dup_or_clone(super, __method__, '@array') # defined in builtin_type.rb end |
#compact! ⇒ self, NilClass
Array#compact!
If changed, re-#normalize! it.
887 888 889 890 |
# File 'lib/plain_text/part.rb', line 887 def compact! ret = @array.send(__method__) ret ? normalize!(recursive: false) : ret end |
#concat(*rest) ⇒ self
Array#concat
898 899 900 |
# File 'lib/plain_text/part.rb', line 898 def concat(*rest) insert(@array.size, *(rest.sum([]))) end |
#deepcopy ⇒ Part
Returns a dup-ped instance with all the Arrays and Strings dup-ped.
225 226 227 228 229 230 |
# File 'lib/plain_text/part.rb', line 225 def deepcopy _return_this_or_other{ @array.dup.map!{ |i| i.respond_to?(:deepcopy) ? i.deepcopy : (i.dup rescue i)} # 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). } end |
#dup ⇒ PlainText::Part
Work around because Object#dup does not dup the instance variable @array
602 603 604 |
# File 'lib/plain_text/part.rb', line 602 def dup dup_or_clone(super, __method__, '@array') # defined in builtin_type.rb end |
#each_boundary_with_index(**kwd, &bl) ⇒ Object
each method for boundaries only, providing also the index (always an odd number) to the block.
For just looping over the elements of #boundaries, do simply
boundaries.each do |ec|
end
The indices provided in this method are for the main Array, and hence different from #boundaries.each_with_index
244 245 246 |
# File 'lib/plain_text/part.rb', line 244 def each_boundary_with_index(**kwd, &bl) map_boundary_core(do_map: false, with_index: true, **kwd, &bl) end |
#each_para_with_index(**kwd, &bl) ⇒ Object
260 261 262 |
# File 'lib/plain_text/part.rb', line 260 def each_para_with_index(**kwd, &bl) map_para_core(do_map: false, with_index: false, **kwd, &bl) end |
#first_significant_element ⇒ Integer?
The first significant (=non-empty) element.
If the returned value is non-nil and destructively altered, self changes.
269 270 271 272 |
# File 'lib/plain_text/part.rb', line 269 def first_significant_element (i = first_significant_index) || return @array[i] end |
#first_significant_index ⇒ Integer?
Index of the first significant (=non-empty) element.
If every element is empty, the last index is returned.
279 280 281 282 283 284 |
# File 'lib/plain_text/part.rb', line 279 def first_significant_index return nil if @array.empty? @array.find_index do |val| val && !val.empty? end || (@array.size-1) end |
#index_para?(i, accept_negative: true) ⇒ Boolean
True if the index should be semantically for Paragraph?
291 292 293 |
# File 'lib/plain_text/part.rb', line 291 def index_para?(i, accept_negative: true) accept_negative ? positive_array_index_checked(i, @array).even? : i.even? end |
#insert(ind, *rest, primitive: false) ⇒ self
Array#insert
The most basic method to add/insert elements to self. Called from #[]= and #push, for example.
The maximum permitted position index is the size of self,s unlike Array#insert, which allows an index larger than the size, in which case nil
is inserted in between.
The number of the inserted elements must be always even.
809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 |
# File 'lib/plain_text/part.rb', line 809 def insert(ind, *rest, primitive: false) #return insert_original_b4_part(ind, *rest) if primitive return _return_this_or_other(@array.insert(ind, *rest)) if primitive ipos = positive_array_index_checked(ind, @array) # IndexError may be raised. ipos += 1 if ind < 0 # If ipos is negative, it should be inserted AFTER the index according to Array#index, # whereas if ipos is non-negative, inserted BEFORE. if ipos > @array.size raise IndexError, sprintf("index (%s) too large for array: maximum: %d.", ipos, @array.size) elsif rest.size.odd? raise ArgumentError, sprintf("number of arguments (%d) must be even.", rest.size) end return self if rest.empty? begin _return_this_or_other{ @array.insert(ipos, *rest) && normalize! && self } rescue PlainText::PartNormalizeError => err raise TypeError, err. end end |
#inspect ⇒ String
588 589 590 |
# File 'lib/plain_text/part.rb', line 588 def inspect self.class.name + @array.inspect end |
#last_significant_element ⇒ Integer?
The last significant (=non-empty) element.
If the returned value is non-nil and destructively altered, self changes.
300 301 302 303 |
# File 'lib/plain_text/part.rb', line 300 def last_significant_element (i = last_significant_index) || return @array[i] end |
#last_significant_index ⇒ Integer?
Index of the last significant (=non-empty) element.
If every element is empty, 0 is returned.
310 311 312 313 314 315 316 |
# File 'lib/plain_text/part.rb', line 310 def last_significant_index return nil if empty? (0..(size-1)).to_a.reverse.each do |i| return i if @array[i] && !@array[i].empty? # self for sanity end return 0 end |
#map_boundary(**kwd, &bl) ⇒ Object
map method for boundaries only, returning a copied self.
If recursive is true (Default), any Boundaries in the descendant Parts are also handled.
If a Boundary is set nil or empty, along with the preceding Paragraph, the pair is removed from the returned instance in Default (:compact and :compacter options
-
see #initialize for detail)
329 330 331 |
# File 'lib/plain_text/part.rb', line 329 def map_boundary(**kwd, &bl) map_boundary_core(with_index: false, **kwd, &bl) end |
#map_boundary_with_index(**kwd, &bl) ⇒ Object
map method for boundaries only, providing also the index (always an odd number) to the block, returning a copied self.
337 338 339 |
# File 'lib/plain_text/part.rb', line 337 def map_boundary_with_index(**kwd, &bl) map_boundary_core(with_index: true, **kwd, &bl) end |
#map_para(**kwd, &bl) ⇒ Object
map method for Paras only, returning a copied self.
If recursive is true (Default), any Paras in the descendant Parts are also handled.
If a Paragraph is set nil or empty, along with the following Boundary, the pair is removed from the returned instance in Default (:compact and :compacter options
-
see #initialize for detail)
352 353 354 |
# File 'lib/plain_text/part.rb', line 352 def map_para(**kwd, &bl) map_para_core(with_index: false, **kwd, &bl) end |
#map_para_with_index(**kwd, &bl) ⇒ Object
map method for paras only, providing also the index (always an even number) to the block, returning a copied self.
360 361 362 |
# File 'lib/plain_text/part.rb', line 360 def map_para_with_index(**kwd, &bl) map_para_core(with_index: false, **kwd, &bl) end |
#set(index1, index2, *rest) ⇒ self? #set(range) ⇒ self?
merge multiple paragraphs
The boundaries between them are simply joined as String as they are.
410 411 412 413 414 415 416 |
# File 'lib/plain_text/part.rb', line 410 def merge_para!(*rest, use_para_index: false) $myd = true (ranchk = build_index_range_for_merge_para!(*rest, use_para_index: use_para_index)) || (return self) # Do nothing. # ranchk is guaranteed to have a size of 2 or greater. @array[ranchk] = [Paragraph.new(@array[ranchk][0..-2].join), @array[ranchk.end]] # Array[Range] replaced with 2-elements (Para, Boundary) self end |
#merge_para_if {|ary, b1, b2, i| ... } ⇒ self, false
merge Paras if they satisfy the conditions.
A group of two Paras and the Boundaries in between and before and after is passed to the block consecutively.
376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 |
# File 'lib/plain_text/part.rb', line 376 def merge_para_if() arind2del = [] # Indices to delete (both paras and boundaries) @array.each_index do |ei| break if ei >= @array.size - 3 # 2nd last paragraph or later. next if !index_para?(ei, accept_negative: false) ar1st = @array[ei..ei+2] ar2nd = ((ei==0) ? nil : @array[ei-1]) do_merge = yield(ar1st, ar2nd, @array[ei+3], ei) return false if do_merge == :abort arind2del.push ei, ei+1, ei+2 if do_merge end return false if arind2del.empty? arind2del.uniq! (arind2ranges arind2del).reverse.each do |er| merge_para!(er) end return self end |
#normalize(recursive: true, ignore_array_boundary: true, compact: true, compacter: true) ⇒ Object
Non-destructive version of #normalize!
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 |
# File 'lib/plain_text/part.rb', line 501 def normalize(recursive: true, ignore_array_boundary: true, compact: true, compacter: true) # Trims pairs of consecutive Paragraph and Boundary of nil arall = @array size_parity = (@array.size.even? ? 0 : 1) if (@array.compact || compacter) && (@array.size > 0+size_parity) ((@array.size-2-size_parity)..0).each do |i| # Loop over every Paragraph next if i.odd? arall.slice! i, 2 if compact && !@array[i] && !@array[i+1] arall.slice! i, 2 if compacter && (!@array[i] || @array[i].empty?) && (!@array[i+1] || @array[i+1].empty?) end end i = -1 self.class.new( arall.map{ |ea| i += 1 normalize_core(ea, i, recursive: recursive) } + (arall.size.odd? ? [Boundary.new('')] : []) ) end |
#normalize!(recursive: true, ignore_array_boundary: true, compact: true, compacter: true) ⇒ self
Normalize the content, making sure it has an even number of elements
The even and odd number elements are, if bare Strings or Array, converted into Paeagraph and Boundary, or Part, respectively. If not, Exception is raised. Note nil is conveted into either an empty Paragraph or Boundary.
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 |
# File 'lib/plain_text/part.rb', line 472 def normalize!(recursive: true, ignore_array_boundary: true, compact: true, compacter: true) # Trim pairs of consecutive Paragraph and Boundary of nil size_parity = (@array.size.even? ? 0 : 1) if (@array.compact || compacter) && (@array.size > 0+size_parity) ((@array.size-2-size_parity)..0).each do |i| # Loop over every Paragraph next if i.odd? @array.slice! i, 2 if @array.compact && !@array[i] && !@array[i+1] @array.slice! i, 2 if compacter && (!@array[i] || @array[i].empty?) && (!@array[i+1] || @array[i+1].empty?) end end @array.map!.with_index{ |ea, ind| ax = normalize_core(ea, ind, recursive: recursive) ax #normalize_core(ea, ind, recursive: recursive) } @array.insert(@array.size, Boundary.new('')) if @array.size.odd? self end |
#paragraph? ⇒ Boolean
192 193 194 |
# File 'lib/plain_text/part.rb', line 192 def paragraph? false end |
#paras ⇒ Array<Part, Paragraph>
Returns an array of Paras (even-number-index elements), consisting of Part and/or Paragraph
528 529 530 531 |
# File 'lib/plain_text/part.rb', line 528 def paras @array.select.with_index { |_, i| i.even? } rescue @array.select.each_with_index { |_, i| i.even? } # Rescue for Ruby 2.1 or earlier # ret.freeze end |
#part? ⇒ Boolean
196 197 198 |
# File 'lib/plain_text/part.rb', line 196 def part? true end |
#push(*rest) ⇒ self Also known as: append
Array#push
908 909 910 |
# File 'lib/plain_text/part.rb', line 908 def push(*rest) concat(rest) end |
#reparse(**kwd) ⇒ PlainText::Part
Non-destructive version of #reparse!
548 549 550 551 552 |
# File 'lib/plain_text/part.rb', line 548 def reparse(**kwd) ret = @array.dup ret.reparse!(**kwd) ret end |
#reparse!(rule: PlainText::ParseRule::RuleConsecutiveLbs, name: nil, range: (0..-1)) ⇒ self
Reparses self or a part of it.
539 540 541 542 |
# File 'lib/plain_text/part.rb', line 539 def reparse!(rule: PlainText::ParseRule::RuleConsecutiveLbs, name: nil, range: (0..-1)) insert range.begin, self.class.parse((range ? @array[range] : self), rule: rule, name: name) self end |
#respond_to_missing?(method_name, *rest) ⇒ Boolean
Redefines the behaviour of respond_to?
(essential when defining method_missing
)
681 682 683 |
# File 'lib/plain_text/part.rb', line 681 def respond_to_missing?(method_name, *rest) # include_all=false !DISABLED_ARRAY_METHODS.include?(method_name) && @array.respond_to?(method_name, *rest) || super end |
#slice!(arg1, *rest, primitive: false) ⇒ Object
Delete elements and return the deleted content or nil if nothing is deleted.
The number of elements to be deleted must be even.
844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 |
# File 'lib/plain_text/part.rb', line 844 def slice!(arg1, *rest, primitive: false) #return slice_original_b4_part!(arg1, *rest) if primitive return _return_this_or_other(@array.slice(ind, *rest)) if primitive arg2 = rest[0] # Simple substitution to a single element raise ArgumentError, ERR_MSGS[:even_num] if !arg2 && !arg1.respond_to?(:exclude_end?) check_bracket_args_type_error(arg1, arg2) # Args are now either (Int, Int) or (Range) if arg2 size2delete = size2extract(arg1, arg2, ignore_error: true) # maybe nil (if the index is too small). raise ArgumentError, ERR_MSGS[:even_num] if size2delete && size2delete.odd? 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. return _return_this_or_other(@array.slice!(arg1, *rest)) end begin rang = normalize_index_range(arg1) rescue IndexError => err raise RangeError, err. end raise RangeError if rang.begin < 0 || rang.end < 0 return _return_this_or_other(@array.slice!(arg1, *rest)) if (rang.begin > rang.end) # nil or [] is returned size2delete = size2extract(rang, skip_renormalize: true) raise ArgumentError, ERR_MSGS[:even_num] if size2delete && size2delete.odd? 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. _return_this_or_other(@array.slice!(arg1, *rest)) end |
#squash_boundaries! ⇒ self
Wrapper of #squash_boundary_at! to loop over the whole PlainText::Part
573 574 575 576 577 578 |
# File 'lib/plain_text/part.rb', line 573 def squash_boundaries! each_boundary_with_index do |ec, i| squash_boundary_at!(i) end self end |
#squash_boundary_at!(index) ⇒ Boundary?
Emptifies all the Boundaries immediately before the Boundary at the index and squashes it to the one at it.
See #boundary_extended_at to view them.
561 562 563 564 565 566 567 |
# File 'lib/plain_text/part.rb', line 561 def squash_boundary_at!(index) (i_pos = get_valid_ipos_for_boundary(index)) || return prt = @array[i_pos-1] m = :emptify_last_boundary! @array[i_pos] << prt.public_send(m) if prt.respond_to? m @array[i_pos] end |
#to_a ⇒ Array Also known as: to_ary, instance
593 594 595 |
# File 'lib/plain_text/part.rb', line 593 def to_a @array end |