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:



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

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,}'


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

def self.dump(obj)
  if obj.kind_of?(Integer)
    int_str = obj.to_s
    "#{int_str.length}:#{int_str}#"
  elsif obj.kind_of?(Float)
    float_str = obj.to_s
    "#{float_str.length}:#{float_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:



175
176
177
178
179
180
181
# File 'lib/tnetstring.rb', line 175

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:



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

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,}'


124
125
126
127
# File 'lib/tnetstring.rb', line 124

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
41
42
# 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.to_f
  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:



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

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:



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

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:



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

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:



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

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:



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

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