Class: Stupidedi::Reader::StreamReader

Inherits:
Object
  • Object
show all
Includes:
Inspect
Defined in:
lib/stupidedi/reader/stream_reader.rb

Overview

The StreamReader is intended to scan the input for a valid ISA segment, after which the TokenReader class can be used to tokenize the remaining input.

Because X12 specifications have no bearing on what happens outside the interchange envelope (from IEA to ISA), out-of-band data like blank lines, human readable text, etc can occur between interchanges. This reader is designed to deal with that problem.

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Inspect

#inspect

Constructor Details

#initialize(input) ⇒ StreamReader

Returns a new instance of StreamReader


23
24
25
# File 'lib/stupidedi/reader/stream_reader.rb', line 23

def initialize(input)
  @input = input
end

Instance Attribute Details

#input (readonly)

Returns the value of attribute input


21
22
23
# File 'lib/stupidedi/reader/stream_reader.rb', line 21

def input
  @input
end

Instance Method Details

#consume_characterEither<StreamReader>

Skip a single character

Returns:


51
52
53
54
55
56
57
# File 'lib/stupidedi/reader/stream_reader.rb', line 51

def consume_character
  unless @input.empty?
    success(advance(1))
  else
    failure("less than one character available")
  end
end

#empty?Boolean

True if there is no remaining input

Returns:

  • (Boolean)

33
34
35
# File 'lib/stupidedi/reader/stream_reader.rb', line 33

def empty?
  @input.empty?
end

#read_characterEither<Result<Character, StreamReader>>

Read a single character

Returns:


40
41
42
43
44
45
46
# File 'lib/stupidedi/reader/stream_reader.rb', line 40

def read_character
  unless @input.empty?
    result(@input.at(0), advance(1))
  else
    failure("less than one character available")
  end
end

#read_segmentEither<Result<SegmentTok TokenReader>>

This method is unaware of subtle differences between interchange versions, so it really only tokenizes 16 elements, plus the element terminator and segment separators. It does not presume to understand the meaning of the elements, like the repetition separator or the component separator, because these are interchange version-dependent.


66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/stupidedi/reader/stream_reader.rb', line 66

def read_segment
  consume_isa.flatmap do |rest|
    # The character after "ISA" is defined to be the element separator
    rest.read_character.flatmap do |char, aR|
      separators = Separators.new(nil, nil, char, nil)
      remaining  = success(TokenReader.new(aR.input, separators))
      elements   = []

      # Read 15 simple elements into an array. Consume/discard the element
      # separator that follows each one.
      15.times do
        remaining =
          remaining.flatmap(&:read_simple_element).flatmap do |e, eR|
            elements << e

            # Throw away the following element separator
            eR.consume_prefix(separators.element)
          end
      end

      # We have to assume the last (16th) element is fixed-length because
      # it is not terminated by an element separator. The {read_character}
      # method defined by TokenReader skips past control characters.
      remaining.flatmap do |w|
        w.read_character.flatmap do |isa16, cR|
          elements << SimpleElementTok.build(isa16, w.input, cR.input)

          # The character after the last element is defined to be the
          # segment terminator. The {read_character} method here, defined
          # by StreamReader, does not skip past control character, so the
          # separator could be a control character.
          cR.stream.read_character.flatmap do |char, dR|
            if char == separators.element
              failure("element separator and segment terminator must be distinct", dR.input)
            else
              separators.segment = char

              token = SegmentTok.build(:ISA, elements,
                rest.input.position, dR.input.position)

              result(token, TokenReader.new(dR.input, separators))
            end
          end
        end
      end
    end.or do |reason|
      # We read "ISA" but failed to tokenize the input that followed. This
      # was probably a random occurrence of the sequence "ISA", so we'll
      # skip past it and try again.
      #
      # @todo: We should log this as a warning, because we could otherwise
      # be silently discarding an entire interchange if the ISA segment
      # was bogus
      rest.read_segment
    end
  end
end

#stream?Boolean

Returns true

Returns:

  • (Boolean)

    true


28
29
30
# File 'lib/stupidedi/reader/stream_reader.rb', line 28

def stream?
  true
end