Class: CMF::Parser

Inherits:
Object
  • Object
show all
Defined in:
lib/cmf/parser.rb

Overview

Instances of the Parser class can parse CMF messages.

Basic usage:

p = CMF::Parser.new
p.parse("\x04\r")   # {0=>true, 1=>false}
p.parse_hex("040d") # {0=>true, 1=>false}

Using a dictionary:

p = CMF::Parser.new([:tag0, :tag1])
p.parse_hex("040d") # {:tag0=>true, :tag1=>false}

If a tag is found multiple times in the message, its values will be stored in an array in the parsed object.

CMF::Parser.new.parse_hex("040504") # {0=>[true, false, true]}

Using #next_pair:

p = CMF::Parser.new
p.message_hex = "040d"
p.next_pair # [0, true]
p.next_pair # [1, false]
p.next_pair # nil

Using #each:

p = CMF::Parser.new
p.messge_hex = "040d"
p.each { |k, v| puts "#{k}: #{v}" }

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(dictionary = nil) ⇒ Parser

Creates a new instance of CMF::Parser.



42
43
44
45
# File 'lib/cmf/parser.rb', line 42

def initialize(dictionary = nil)
  @inverted_dictionary = Dictionary.validate(dictionary).invert
  @io = StringIO.new
end

Instance Attribute Details

#inverted_dictionaryHash (readonly)



36
37
38
# File 'lib/cmf/parser.rb', line 36

def inverted_dictionary
  @inverted_dictionary
end

Instance Method Details

#each {|tag, value| ... } ⇒ Enumerator

Calls the given block once for each pair found in the message.

Yield Parameters:

  • tag (Integer, Object)

    The pair's tag. An integer, or the associated value found in the dictionary.

  • value (String, Integer, Boolean, Float)

    The pair's value.

See Also:



104
105
106
107
108
109
110
111
# File 'lib/cmf/parser.rb', line 104

def each
  return to_enum(:each) unless block_given?
  loop do
    pair = next_pair
    break if pair.nil?
    yield(pair[0], pair[1])
  end
end

#message=(message) ⇒ Object

Sets a new CMF message.



50
51
52
# File 'lib/cmf/parser.rb', line 50

def message=(message)
  @io = StringIO.new(message)
end

#message_hex=(message_hex) ⇒ Object

Sets a new CMF message from a hex string.



57
58
59
# File 'lib/cmf/parser.rb', line 57

def message_hex=(message_hex)
  self.message = [message_hex].pack('H*')
end

#next_pairArray?

Returns the next pair in the message, or nil if the whole message has been parsed.

Raises:



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/cmf/parser.rb', line 125

def next_pair
  return nil if @io.eof?

  byte = @io.getbyte
  type = byte & 0x07
  tag = byte >> 3
  if tag == 31
    tag = Varint.deserialize(@io)
  end

  if @inverted_dictionary[tag]
    tag = @inverted_dictionary[tag]
  end

  case type
  when Type::POSITIVE_NUMBER
    [tag, Varint.deserialize(@io)]
  when Type::NEGATIVE_NUMBER
    [tag, -Varint.deserialize(@io)]
  when Type::STRING, Type::BYTE_ARRAY
    length = Varint.deserialize(@io)
    s = @io.read(length)
    s.bytesize == length or raise MalformedMessageError, "Unexpected end of stream"
    s = s.force_encoding(Encoding::UTF_8) if type == Type::STRING
    [tag, s]
  when Type::BOOL_TRUE
    [tag, true]
  when Type::BOOL_FALSE
    [tag, false]
  when Type::DOUBLE
    s = @io.read(8)
    s.bytesize == 8 or raise MalformedMessageError, "Unexpected end of stream"
    [tag, s.unpack('E').first]
  else
    raise MalformedMessageError, "Unknown type"
  end

end

#parse(message = nil) ⇒ Hash

Parses a CMF message into an object.



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/cmf/parser.rb', line 71

def parse(message = nil)
  self.message = message if message

  obj = {}
  each do |key, value|
    if obj.has_key?(key)
      obj[key] = Array(obj[key])
      obj[key] << value
    else
      obj[key] = value
    end
  end

  obj
end

#parse_hex(message_hex) ⇒ Hash

Parses a hex-encoded CMF message into an object.



91
92
93
94
95
# File 'lib/cmf/parser.rb', line 91

def parse_hex(message_hex)
  self.message_hex = message_hex

  parse
end