Class: Udat::Node

Inherits:
Object show all
Includes:
MonitorMixin
Defined in:
lib/udat.rb

Overview

The abstract class to represent any UDAT data is a Udat::Node. Udat::Node’s basically consist of a tag and the content. The tag can be a String or be nil. The content is either a scalar value stored as a String, or an ordered/unordered collection of values (where each value can optionally have a key associated with it). Keys and values of collections are always Udat::Node’s too.

Udat::Node’s can most easily be constructed from other ruby objects by using the Object#to_udat method.

By calling the method Udat::Node#encode, the Node and its children are encoded in a both easily human readable and easily machine readable format. The output can be later parsed by String#parse_udat or IO#read_udat.

Direct Known Subclasses

Collection, Scalar

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tag) ⇒ Node

Creates a new Udat::Node with a given tag (which may be nil). This method is private, Udat::Node#initialize is only called through supercalls.



82
83
84
85
# File 'lib/udat.rb', line 82

def initialize(tag)
  super()
  self.tag = tag
end

Instance Attribute Details

#tagObject

Tag (i.e. type of the content).



88
89
90
# File 'lib/udat.rb', line 88

def tag
  @tag
end

Class Method Details

.parse(input) ⇒ Object

Parses a given UDAT document string and returns a structure of Udat::Node’s.

Note: When parsing UDAT data, no information is gained, whether collections are ordered or unordered. After parsing, all collections will be marked as unordered, unless changed later by the application.



391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
# File 'lib/udat.rb', line 391

def self.parse(input)
  input = input.to_s
  pos = 0
  begin
    collection = (
      parse_intern(:normal, nil) do
        char = input[pos, 1]
        pos += 1
        next char.empty? ? nil : char
      end
    )
    unless collection.collection? and not collection.empty?
      raise ParseError, "No valid UDAT object found."
    end
    return collection.first
  rescue EOFError
    raise ParseError, $!.message
  end
end

.parse_document(input) ⇒ Object

Alias for Udat::Node.parse, will be removed in future versions.



411
412
413
# File 'lib/udat.rb', line 411

def self.parse_document(input)
  parse(input)
end

.read_from_stream(io) ⇒ Object

Reads an encoded Udat::Node from a stream.



416
417
418
419
420
421
422
# File 'lib/udat.rb', line 416

def self.read_from_stream(io)
  return (
    parse_intern(:one_value, nil) do
      io.read(1)
    end
  )
end

Instance Method Details

#==(other) ⇒ Object

Same as Udat::Node#eql?, but behaves differently in Udat::Collection.



224
225
226
# File 'lib/udat.rb', line 224

def ==(other)
  self.eql? other
end

#collection?Boolean

Returns true, if the Udat::Node represents a collection.

Returns:

  • (Boolean)


102
103
104
# File 'lib/udat.rb', line 102

def collection?
  false
end

#encodeObject

Returns the data (including it’s tag) encoded in the UDAT format and enclosed in square brackets.

Note: The UDAT format doesn’t contain information, whether contained collections are ordered or unordered. This information is lost during the encoding process, and has to be restored in an application specific way, if neccessary.



179
180
181
# File 'lib/udat.rb', line 179

def encode
  "[#{encode_without_brackets}]"
end

#encode_documentObject

Alias for Udat::Node#encode, will be removed in future versions.



183
184
185
# File 'lib/udat.rb', line 183

def encode_document
  encode
end

#eql?(other) ⇒ Boolean

Returns true, if class, tag and content (including it’s order in case of a Udat::Collection) are matching another object.

Returns:

  • (Boolean)


218
219
220
221
222
# File 'lib/udat.rb', line 218

def eql?(other)
  self.class == other.class and
    self.tag == other.tag and
    self.to_s == other.to_s
end

#hashObject

Returns a hash key used by ruby’s Hash’es.



213
214
215
# File 'lib/udat.rb', line 213

def hash
  to_s.hash
end

#inspectObject

Does the same as Udat::Node#encode, but preceds the result with “udat”.



193
194
195
# File 'lib/udat.rb', line 193

def inspect
  "udat#{self.encode}"
end

#require_collection(tag = AnyTag) ⇒ Object

Returns self, if the Udat::Node represents a collection, otherwise an Udat::UdatTypeMismatch exception is raised. If an optional tag is given as an argument, it is checked by calling Udat::Node#require_tag.



139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/udat.rb', line 139

def require_collection(tag = AnyTag)
  if collection?
    if tag == AnyTag
      return self
    else
      return require_tag(tag)
    end
  elsif scalar?
    raise UdatTypeMismatch,
      "UDAT scalar found where a collection was expected."
  else
    raise "Internal error in UDAT library."
  end
end

#require_scalar(tag = AnyTag) ⇒ Object

Returns self, if the Udat::Node represents a scalar, otherwise an Udat::UdatTypeMismatch exception is raised. If an optional tag is given as an argument, it is checked by calling Udat::Node#require_tag.



121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/udat.rb', line 121

def require_scalar(tag = AnyTag)
  if scalar?
    if tag == AnyTag
      return self
    else
      return require_tag(tag)
    end
  elsif collection?
    raise UdatTypeMismatch,
      "UDAT collection found where a scalar was expected."
  else
    raise "Internal error in UDAT library."
  end
end

#require_tag(tag) ⇒ Object

Returns self, if the tag is matching the argument, otherwise an Udat::UdatTagMismatch exception is raised.



108
109
110
111
112
113
114
115
116
# File 'lib/udat.rb', line 108

def require_tag(tag)
  synchronize do
    if self.tag == tag.to_s
      return self
    else
      raise UdatTagMismatch, "UDAT tag mismatch."
    end
  end
end

#rpc(domain, port = nil) ⇒ Object

Sends the object in encoded form through a TCP connection to a given host and port, or if no port is supplied to the host and port specified in a DNS SRV record “_udat-rpc._tcp.<domain>”. Returns the UDAT object received as a reply. As this method can block for an unlimited amount of time, it might be useful to enclose the call in a Timeout.timeout call.



437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
# File 'lib/udat.rb', line 437

def rpc(domain, port = nil)
  domain = domain.to_str
  port = port.to_int if port
  socket_args = nil
  result = nil
  if port.nil?
    Resolv::DNS.open do |dns|
      srv_rr = dns.getresource(
        "_udat-rpc._tcp.#{domain}",
        Resolv::DNS::Resource::IN::SRV
      )
      socket_args = [srv_rr.target.to_s, srv_rr.port.to_i]
    end
  else
    socket_args = [domain, port]
  end
  socket = nil
  begin
    socket = TCPSocket.new(*socket_args)
    socket.write_udat(self)
    socket.close_write
    result = socket.read_udat
  ensure
    socket.close if socket
  end
  unless result
    raise EOFError, "End of file before reading UDAT RPC result."
  end
  return result
end

#scalar?Boolean

Returns true, if the Udat::Node represents a scalar value.

Returns:

  • (Boolean)


98
99
100
# File 'lib/udat.rb', line 98

def scalar?
  false
end

#to_sObject

Here the method does the same as Udat::Node#encode, but this method is overwritten in Udat::Scalar!



188
189
190
# File 'lib/udat.rb', line 188

def to_s
  encode
end

#to_udat(tag = AnyTag) ⇒ Object

Returns self, or a duplicate of self with a different tag set, if an argument is passed.



199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/udat.rb', line 199

def to_udat(tag = AnyTag)
  if tag == AnyTag
    return self
  else
    obj = nil
    synchronize do
      obj = self.dup
    end
    obj.tag = tag
    return obj
  end
end

#write_to_stream(io) ⇒ Object

Encodes an object in the UDAT format by calling Object#to_udat followed by Udat::Node#encode, and writes it to a stream. Returns self.



426
427
428
429
# File 'lib/udat.rb', line 426

def write_to_stream(io)
  io << self.to_udat.encode
  return self
end