Module: TNetstring

Defined in:
lib/tnetstring/version.rb,
lib/tnetstring.rb,
lib/tnetstring/errors.rb

Overview

:nodoc:

Defined Under Namespace

Modules: Version Classes: ProcessError

Class Method Summary collapse

Class Method Details

.assert(truthy, message) ⇒ Object

:nodoc:

Raises:



178
179
180
# File 'lib/tnetstring.rb', line 178

def self.assert(truthy, message) # :nodoc:
  raise ProcessError.new(message) unless truthy
end

.dump(obj) ⇒ Object

Constructs a tnetstring out of the given object. Valid Ruby object types include strings, integers, boolean values, nil, arrays, and hashes. Arrays and hashes may contain any of the previous valid Ruby object types, but hash keys must be strings.

Example

int = 12345
TNetstring.dump(int)

#=> '5:12345#'

hash = {'hello' => 'world'}
TNetstring.dump(hash)

#=> '16:5:hello,5:world,}'


144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/tnetstring.rb', line 144

def self.dump(obj)
  if obj.kind_of?(Integer)
    int_str = obj.to_s
    "#{int_str.length}:#{int_str}#"
  elsif obj.kind_of?(String) || obj.kind_of?(Symbol)
    "#{obj.length}:#{obj},"
  elsif obj.is_a?(TrueClass)
    "4:true!"
  elsif obj.is_a?(FalseClass)
    "5:false!"
  elsif obj == nil
    "0:~"
  elsif obj.kind_of?(Array)
    dump_list(obj)
  elsif obj.kind_of?(Hash)
    dump_dictionary(obj)
  else
    assert false, "Object must be of a primitive type: #{obj.inspect}"
  end
end

.dump_dictionary(dict) ⇒ Object

:nodoc:



170
171
172
173
174
175
176
# File 'lib/tnetstring.rb', line 170

def self.dump_dictionary(dict) # :nodoc:
  contents = dict.map do |key, value|
    assert key.kind_of?(String) || key.kind_of?(Symbol), "Dictionary keys must be Strings or Symbols"
    "#{dump(key)}#{dump(value)}"
  end.join
  "#{contents.length}:#{contents}}"
end

.dump_list(list) ⇒ Object

:nodoc:



165
166
167
168
# File 'lib/tnetstring.rb', line 165

def self.dump_list(list) # :nodoc:
  contents = list.map {|item| dump(item)}.join
  "#{contents.length}:#{contents}]"
end

.encode(obj) ⇒ Object

DEPRECATED: Please use dump instead.

Constructs a tnetstring out of the given object. Valid Ruby object types include strings, integers, boolean values, nil, arrays, and hashes. Arrays and hashes may contain any of the previous valid Ruby object types, but hash keys must be strings.

Example

int = 12345
TNetstring.dump(int)

#=> '5:12345#'

hash = {'hello' => 'world'}
TNetstring.dump(hash)

#=> '16:5:hello,5:world,}'


122
123
124
125
# File 'lib/tnetstring.rb', line 122

def self.encode(obj)
  warn "[DEPRECATION] `encode` is deprecated.  Please use `dump` instead."
  dump obj
end

.parse(tnetstring) ⇒ Object

Converts a tnetstring into the encoded data structure.

It expects a string argument prefixed with a valid tnetstring and returns a tuple of the parsed object and any remaining string input.

Example

str = '5:12345#'
TNetstring.parse(str)

#=> [12345, '']

str = '11:hello world,abc123'

#=> ['hello world', 'abc123']


20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/tnetstring.rb', line 20

def self.parse(tnetstring)
  payload, payload_type, remain = parse_payload(tnetstring)
  value = case payload_type
  when '#'
    payload.to_i
  when ','
    payload
  when ']'
    parse_list(payload)
  when '}'
    parse_dictionary(payload)
  when '~'
    assert payload.length == 0, "Payload must be 0 length for null"
    nil
  when '!'
    parse_boolean(payload)
  else
    assert false, "Invalid payload type: #{payload_type}"
  end
  [value, remain]
end

.parse_boolean(data) ⇒ Object

:nodoc:



92
93
94
95
96
97
98
99
100
101
# File 'lib/tnetstring.rb', line 92

def self.parse_boolean(data) # :nodoc:
  case data
  when "false"
    false
  when "true"
    true
  else
    assert false, "Boolean wasn't 'true' or 'false'"
  end
end

.parse_dictionary(data) ⇒ Object

:nodoc:



70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/tnetstring.rb', line 70

def self.parse_dictionary(data) # :nodoc:
  return {} if data.length == 0

  key, value, extra = parse_pair(data)
  result = {key => value}

  while extra.length > 0
      key, value, extra = parse_pair(extra)
      result[key] = value
  end
  result
end

.parse_list(data) ⇒ Object

:nodoc:



57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/tnetstring.rb', line 57

def self.parse_list(data) # :nodoc:
  return [] if data.length == 0
  list = []
  value, remain = parse(data)
  list << value

  while remain.length > 0
    value, remain = parse(remain)
    list << value
  end
  list
end

.parse_pair(data) ⇒ Object

:nodoc:



83
84
85
86
87
88
89
90
# File 'lib/tnetstring.rb', line 83

def self.parse_pair(data) # :nodoc:
  key, extra = parse(data)
  assert key.kind_of?(String) || key.kind_of?(Symbol), "Dictionary keys must be Strings or Symbols"
  assert extra, "Unbalanced dictionary store"
  value, extra = parse(extra)

  [key, value, extra]
end

.parse_payload(data) ⇒ Object

:nodoc:



42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/tnetstring.rb', line 42

def self.parse_payload(data) # :nodoc:
  assert data, "Invalid data to parse; it's empty"
  length, extra = data.split(':', 2)
  length = length.to_i
  assert length <= 999_999_999, "Data is longer than the specification allows"
  assert length >= 0, "Data length cannot be negative"

  payload, extra = extra[0, length], extra[length..-1]
  assert extra, "No payload type: #{payload}, #{extra}"
  payload_type, remain = extra[0,1], extra[1..-1]

  assert payload.length == length, "Data is wrong length: #{length} expected but was #{payload.length}"
  [payload, payload_type, remain]
end