meshchat 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +39 -11
- data/lib/meshchat.rb +48 -42
- data/lib/meshchat/configuration.rb +14 -0
- data/lib/meshchat/configuration/app_config.rb +63 -0
- data/lib/meshchat/configuration/database.rb +41 -0
- data/lib/meshchat/{config → configuration}/hash_file.rb +7 -6
- data/lib/meshchat/configuration/identity.rb +79 -0
- data/lib/meshchat/{config → configuration}/settings.rb +22 -26
- data/lib/meshchat/debug.rb +69 -0
- data/lib/meshchat/encryption.rb +7 -2
- data/lib/meshchat/encryption/aes_rsa.rb +2 -1
- data/lib/meshchat/encryption/passthrough.rb +5 -3
- data/lib/meshchat/locale/en.yml +14 -0
- data/lib/meshchat/models/node.rb +140 -0
- data/lib/meshchat/network.rb +19 -0
- data/lib/meshchat/network/dispatcher.rb +83 -0
- data/lib/meshchat/network/errors.rb +11 -0
- data/lib/meshchat/network/incoming.rb +13 -0
- data/lib/meshchat/network/incoming/message_decryptor.rb +51 -0
- data/lib/meshchat/network/incoming/message_processor.rb +75 -0
- data/lib/meshchat/network/incoming/request_processor.rb +30 -0
- data/lib/meshchat/network/local.rb +12 -0
- data/lib/meshchat/network/local/connection.rb +58 -0
- data/lib/meshchat/network/local/server.rb +69 -0
- data/lib/meshchat/network/message.rb +34 -0
- data/lib/meshchat/network/message/base.rb +139 -0
- data/lib/meshchat/network/message/chat.rb +9 -0
- data/lib/meshchat/network/message/disconnect.rb +21 -0
- data/lib/meshchat/network/message/emote.rb +9 -0
- data/lib/meshchat/network/message/factory.rb +80 -0
- data/lib/meshchat/network/message/node_list.rb +75 -0
- data/lib/meshchat/network/message/node_list_diff.rb +18 -0
- data/lib/meshchat/network/message/node_list_hash.rb +32 -0
- data/lib/meshchat/network/message/ping.rb +31 -0
- data/lib/meshchat/network/message/ping_reply.rb +12 -0
- data/lib/meshchat/network/message/whisper.rb +28 -0
- data/lib/meshchat/network/remote.rb +13 -0
- data/lib/meshchat/network/remote/connection.rb +28 -0
- data/lib/meshchat/network/remote/relay.rb +109 -0
- data/lib/meshchat/network/remote/relay_pool.rb +52 -0
- data/lib/meshchat/ui.rb +13 -0
- data/lib/meshchat/ui/cli.rb +48 -0
- data/lib/meshchat/ui/cli/base.rb +39 -0
- data/lib/meshchat/ui/cli/input_factory.rb +50 -0
- data/lib/meshchat/ui/cli/keyboard_line_input.rb +14 -0
- data/lib/meshchat/ui/command.rb +51 -0
- data/lib/meshchat/ui/command/base.rb +77 -0
- data/lib/meshchat/ui/command/bind.rb +47 -0
- data/lib/meshchat/ui/command/chat.rb +31 -0
- data/lib/meshchat/ui/command/config.rb +37 -0
- data/lib/meshchat/ui/command/emote.rb +23 -0
- data/lib/meshchat/ui/command/exit.rb +16 -0
- data/lib/meshchat/ui/command/help.rb +20 -0
- data/lib/meshchat/ui/command/identity.rb +16 -0
- data/lib/meshchat/ui/command/import.rb +42 -0
- data/lib/meshchat/ui/command/irb.rb +22 -0
- data/lib/meshchat/ui/command/offline.rb +23 -0
- data/lib/meshchat/ui/command/online.rb +18 -0
- data/lib/meshchat/ui/command/ping.rb +65 -0
- data/lib/meshchat/ui/command/ping_all.rb +19 -0
- data/lib/meshchat/ui/command/send_disconnect.rb +20 -0
- data/lib/meshchat/ui/command/server.rb +22 -0
- data/lib/meshchat/ui/command/share.rb +16 -0
- data/lib/meshchat/ui/command/whisper.rb +40 -0
- data/lib/meshchat/ui/display.rb +78 -0
- data/lib/meshchat/ui/display/base.rb +58 -0
- data/lib/meshchat/ui/display/manager.rb +59 -0
- data/lib/meshchat/ui/notifier.rb +9 -0
- data/lib/meshchat/ui/notifier/base.rb +33 -0
- data/lib/meshchat/version.rb +3 -2
- metadata +150 -80
- data/lib/meshchat/cli.rb +0 -188
- data/lib/meshchat/cli/base.rb +0 -13
- data/lib/meshchat/cli/input.rb +0 -37
- data/lib/meshchat/command/base.rb +0 -80
- data/lib/meshchat/command/bind.rb +0 -44
- data/lib/meshchat/command/chat.rb +0 -30
- data/lib/meshchat/command/config.rb +0 -34
- data/lib/meshchat/command/emote.rb +0 -20
- data/lib/meshchat/command/exit.rb +0 -13
- data/lib/meshchat/command/help.rb +0 -17
- data/lib/meshchat/command/identity.rb +0 -13
- data/lib/meshchat/command/import.rb +0 -41
- data/lib/meshchat/command/init.rb +0 -34
- data/lib/meshchat/command/irb.rb +0 -23
- data/lib/meshchat/command/listen.rb +0 -13
- data/lib/meshchat/command/offline.rb +0 -20
- data/lib/meshchat/command/online.rb +0 -15
- data/lib/meshchat/command/ping.rb +0 -65
- data/lib/meshchat/command/ping_all.rb +0 -15
- data/lib/meshchat/command/send_disconnect.rb +0 -15
- data/lib/meshchat/command/server.rb +0 -20
- data/lib/meshchat/command/share.rb +0 -13
- data/lib/meshchat/command/stop_listening.rb +0 -13
- data/lib/meshchat/command/whisper.rb +0 -38
- data/lib/meshchat/database.rb +0 -30
- data/lib/meshchat/display.rb +0 -33
- data/lib/meshchat/display/base.rb +0 -60
- data/lib/meshchat/display/manager.rb +0 -55
- data/lib/meshchat/instance.rb +0 -40
- data/lib/meshchat/message.rb +0 -41
- data/lib/meshchat/message/base.rb +0 -97
- data/lib/meshchat/message/chat.rb +0 -19
- data/lib/meshchat/message/disconnect.rb +0 -13
- data/lib/meshchat/message/emote.rb +0 -9
- data/lib/meshchat/message/node_list.rb +0 -63
- data/lib/meshchat/message/node_list_diff.rb +0 -15
- data/lib/meshchat/message/node_list_hash.rb +0 -33
- data/lib/meshchat/message/ping.rb +0 -32
- data/lib/meshchat/message/ping_reply.rb +0 -9
- data/lib/meshchat/message/relay.rb +0 -43
- data/lib/meshchat/message/whisper.rb +0 -36
- data/lib/meshchat/models/entry.rb +0 -104
- data/lib/meshchat/net/client.rb +0 -83
- data/lib/meshchat/net/listener/errors.rb +0 -11
- data/lib/meshchat/net/listener/request.rb +0 -48
- data/lib/meshchat/net/listener/request_processor.rb +0 -50
- data/lib/meshchat/net/listener/server.rb +0 -114
- data/lib/meshchat/net/request.rb +0 -29
- data/lib/meshchat/notifier/base.rb +0 -31
@@ -1,60 +0,0 @@
|
|
1
|
-
module MeshChat
|
2
|
-
module Display
|
3
|
-
class Base
|
4
|
-
|
5
|
-
# instantiate the UI, startup the CLI
|
6
|
-
def start
|
7
|
-
fail 'overload this method'
|
8
|
-
end
|
9
|
-
|
10
|
-
# output a generic line of text
|
11
|
-
def add_line(_line)
|
12
|
-
fail 'overload this method'
|
13
|
-
end
|
14
|
-
|
15
|
-
# formatter for a whisper message
|
16
|
-
def whisper(_line)
|
17
|
-
fail 'overload this method'
|
18
|
-
end
|
19
|
-
|
20
|
-
|
21
|
-
# an emote
|
22
|
-
def emote(_line)
|
23
|
-
fail 'overload this method'
|
24
|
-
end
|
25
|
-
|
26
|
-
|
27
|
-
# server info or other ignorable information
|
28
|
-
def info(_line)
|
29
|
-
fail 'overload this method'
|
30
|
-
end
|
31
|
-
|
32
|
-
# warning message that the user may or may not care about
|
33
|
-
def warning(_line)
|
34
|
-
fail 'overload this method'
|
35
|
-
end
|
36
|
-
|
37
|
-
# really try to get the user's attention
|
38
|
-
def alert(_line)
|
39
|
-
fail 'overload this method'
|
40
|
-
end
|
41
|
-
|
42
|
-
# a happy message to affirm the user something succeded
|
43
|
-
def success(_line)
|
44
|
-
fail 'overload this method'
|
45
|
-
end
|
46
|
-
|
47
|
-
# general chat message
|
48
|
-
def chat(_line)
|
49
|
-
fail 'overload this method'
|
50
|
-
end
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
# log a message
|
55
|
-
def log(_msg)
|
56
|
-
fail 'overload this method'
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
@@ -1,55 +0,0 @@
|
|
1
|
-
require 'meshchat/display/base'
|
2
|
-
|
3
|
-
module MeshChat
|
4
|
-
module Display
|
5
|
-
class Manager
|
6
|
-
|
7
|
-
attr_accessor :_ui
|
8
|
-
|
9
|
-
delegate :start, to: :_ui
|
10
|
-
delegate :add_line, to: :_ui
|
11
|
-
delegate :info, to: :_ui
|
12
|
-
delegate :emote, to: :_ui
|
13
|
-
delegate :warning, to: :_ui
|
14
|
-
delegate :alert, to: :_ui
|
15
|
-
delegate :success, to: :_ui
|
16
|
-
delegate :chat, to: :_ui
|
17
|
-
delegate :whisper, to: :_ui
|
18
|
-
|
19
|
-
attr_accessor :_logger
|
20
|
-
delegate :fatal, to: :_logger
|
21
|
-
delegate :debug, to: :_logger
|
22
|
-
delegate :error, to: :_logger
|
23
|
-
|
24
|
-
def initialize(ui_klass)
|
25
|
-
self._logger = Logger.new('debug.log')
|
26
|
-
self._ui = ui_klass.new
|
27
|
-
end
|
28
|
-
|
29
|
-
def present_message(message)
|
30
|
-
result = message.handle
|
31
|
-
return unless result
|
32
|
-
|
33
|
-
case message.class.name
|
34
|
-
when Message::Chat.name
|
35
|
-
chat result
|
36
|
-
Notify.show(summary: message.sender_name, body: message.message)
|
37
|
-
when Message::Whisper.name
|
38
|
-
whisper result
|
39
|
-
Notify.show(summary: message.sender_name, body: message.message)
|
40
|
-
when Message::Emote.name
|
41
|
-
emote result
|
42
|
-
Notify.show(summary: message.sender_name, body: message.message)
|
43
|
-
when Message::PingReply.name, Message::Ping.name
|
44
|
-
info result
|
45
|
-
when Message::NodeList.name,
|
46
|
-
Message::NodeListDiff.name,
|
47
|
-
Message::NodeListHash.name
|
48
|
-
# display nothing
|
49
|
-
else
|
50
|
-
add_line result
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
data/lib/meshchat/instance.rb
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
module MeshChat
|
2
|
-
class Instance
|
3
|
-
attr_accessor :client_name, :client_version, :display
|
4
|
-
|
5
|
-
class << self
|
6
|
-
delegate :client_name, :client_version, :display,
|
7
|
-
to: :instance
|
8
|
-
|
9
|
-
def start(options)
|
10
|
-
# calling instance to get things going
|
11
|
-
@instance = new(options)
|
12
|
-
@instance.start_input_device(options[:input])
|
13
|
-
@instance.start_ui(options[:display], options[:on_display_start])
|
14
|
-
end
|
15
|
-
|
16
|
-
def instance
|
17
|
-
@instance
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def initialize(options = {})
|
22
|
-
self.client_name = options[:client_name] || MeshChat::NAME
|
23
|
-
self.client_version = options[:client_version] || MeshChat::VERSION
|
24
|
-
end
|
25
|
-
|
26
|
-
# @param [Class] klass should be something that implements Display::Base
|
27
|
-
# @param [Proc] proc what to do when starting the UI
|
28
|
-
def start_ui(klass, on_display_start)
|
29
|
-
self.display = Display::Manager.new(klass)
|
30
|
-
display.start do
|
31
|
-
on_display_start.call if on_display_start
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def start_input_device(klass)
|
36
|
-
# The CLI is a singleton that takes a method of input
|
37
|
-
MeshChat::CLI.create(klass)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
data/lib/meshchat/message.rb
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
require 'meshchat/message/base'
|
2
|
-
require 'meshchat/message/chat'
|
3
|
-
require 'meshchat/message/emote'
|
4
|
-
require 'meshchat/message/ping'
|
5
|
-
require 'meshchat/message/ping_reply'
|
6
|
-
require 'meshchat/message/disconnect'
|
7
|
-
require 'meshchat/message/whisper'
|
8
|
-
require 'meshchat/message/relay'
|
9
|
-
require 'meshchat/message/node_list'
|
10
|
-
require 'meshchat/message/node_list_diff'
|
11
|
-
require 'meshchat/message/node_list_hash'
|
12
|
-
|
13
|
-
module MeshChat
|
14
|
-
module Message
|
15
|
-
CHAT = 'chat'
|
16
|
-
EMOTE = 'emote'
|
17
|
-
PING = 'ping'
|
18
|
-
PING_REPLY = 'pingreply'
|
19
|
-
WHISPER = 'whisper'
|
20
|
-
RELAY = 'relay'
|
21
|
-
DISCONNECT = 'disconnect'
|
22
|
-
|
23
|
-
NODE_LIST = 'nodelist'
|
24
|
-
NODE_LIST_HASH = 'nodelisthash'
|
25
|
-
NODE_LIST_DIFF = 'nodelistdiff'
|
26
|
-
|
27
|
-
TYPES = {
|
28
|
-
CHAT => Chat,
|
29
|
-
EMOTE => Emote,
|
30
|
-
WHISPER => Whisper,
|
31
|
-
DISCONNECT => Disconnect,
|
32
|
-
PING => Ping,
|
33
|
-
PING_REPLY => PingReply,
|
34
|
-
NODE_LIST => NodeList,
|
35
|
-
NODE_LIST_DIFF => NodeListDiff,
|
36
|
-
NODE_LIST_HASH => NodeListHash
|
37
|
-
}
|
38
|
-
|
39
|
-
|
40
|
-
end
|
41
|
-
end
|
@@ -1,97 +0,0 @@
|
|
1
|
-
module MeshChat
|
2
|
-
module Message
|
3
|
-
#
|
4
|
-
# NOTE:
|
5
|
-
# #display: shows the message
|
6
|
-
# should be used locally, before *sending* a message
|
7
|
-
# #handle: processing logic for the message
|
8
|
-
# should be used when receiving a message, and there
|
9
|
-
# needs to be a response right away
|
10
|
-
# #respond: where the actual logic for the response goes
|
11
|
-
class Base
|
12
|
-
attr_accessor :payload, :time_recieved,
|
13
|
-
:message, :sender_name, :sender_location, :sender_uid
|
14
|
-
|
15
|
-
# @param [String] message
|
16
|
-
# @param [String] name_of_sender
|
17
|
-
# @param [String] location the location of the sender
|
18
|
-
# @param [Hash] payload optionally overrides the default payload
|
19
|
-
def initialize(
|
20
|
-
message: '',
|
21
|
-
sender_name: '',
|
22
|
-
sender_location: '',
|
23
|
-
sender_uid: '',
|
24
|
-
time_recieved: nil,
|
25
|
-
payload: nil)
|
26
|
-
|
27
|
-
@payload = payload.presence
|
28
|
-
|
29
|
-
# TODO: find a more elegant way to represent this
|
30
|
-
self.message = message.presence || @payload.try(:[], 'message')
|
31
|
-
self.sender_name = sender_name.presence || @payload.try(:[], 'sender').try(:[], 'alias') || Settings['alias']
|
32
|
-
self.sender_location = sender_location.presence || @payload.try(:[], 'sender').try(:[], 'location') || Settings.location
|
33
|
-
self.sender_uid = sender_uid.presence || @payload.try(:[], 'sender').try(:[], 'uid') || Settings['uid']
|
34
|
-
self.time_recieved = time_recieved.presence || Time.now
|
35
|
-
|
36
|
-
end
|
37
|
-
|
38
|
-
def payload
|
39
|
-
@payload ||= {
|
40
|
-
'type' => type,
|
41
|
-
'message' => message,
|
42
|
-
'client' => client,
|
43
|
-
'client_version' => client_version,
|
44
|
-
'time_sent' => time_recieved || Time.now.to_s,
|
45
|
-
'sender' => {
|
46
|
-
'alias' => sender_name,
|
47
|
-
'location' => sender_location,
|
48
|
-
'uid' => sender_uid
|
49
|
-
}
|
50
|
-
}
|
51
|
-
end
|
52
|
-
|
53
|
-
def type
|
54
|
-
@type ||= TYPES.invert[self.class]
|
55
|
-
end
|
56
|
-
|
57
|
-
def client
|
58
|
-
MeshChat.name
|
59
|
-
end
|
60
|
-
|
61
|
-
def client_version
|
62
|
-
MeshChat.version
|
63
|
-
end
|
64
|
-
|
65
|
-
def sender
|
66
|
-
payload['sender']
|
67
|
-
end
|
68
|
-
|
69
|
-
# shows the message
|
70
|
-
# should be used locally, before *sending* a message
|
71
|
-
def display
|
72
|
-
message
|
73
|
-
end
|
74
|
-
|
75
|
-
# processing logic for the message
|
76
|
-
# should be used when receiving a message, and there
|
77
|
-
# needs to be a response right away.
|
78
|
-
# this may call display, if the response is always to be displayed
|
79
|
-
def handle
|
80
|
-
display
|
81
|
-
end
|
82
|
-
|
83
|
-
# Most message types aren't going to need to have an
|
84
|
-
# immediate response.
|
85
|
-
#
|
86
|
-
def respond
|
87
|
-
return
|
88
|
-
end
|
89
|
-
|
90
|
-
# this message should be called immediately
|
91
|
-
# before sending to the whomever
|
92
|
-
def render
|
93
|
-
payload.to_json
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
module MeshChat
|
2
|
-
module Message
|
3
|
-
class Chat < Base
|
4
|
-
|
5
|
-
def display
|
6
|
-
time_recieved = self.time_recieved.strftime('%e/%m/%y %H:%I:%M')
|
7
|
-
name = payload['sender']['alias']
|
8
|
-
message = payload['message']
|
9
|
-
|
10
|
-
format_display(time_recieved, name, message)
|
11
|
-
end
|
12
|
-
|
13
|
-
|
14
|
-
def format_display(time, name, message)
|
15
|
-
"#{time} #{name} > #{message}"
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,13 +0,0 @@
|
|
1
|
-
module MeshChat
|
2
|
-
module Message
|
3
|
-
class Disconnect < Base
|
4
|
-
def display
|
5
|
-
location = payload['sender']['location']
|
6
|
-
name = payload['sender']['alias']
|
7
|
-
node = Node.find_by_location(location)
|
8
|
-
node.update(online: false) if node
|
9
|
-
"#{name}@#{location} has disconnected"
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
@@ -1,63 +0,0 @@
|
|
1
|
-
module MeshChat
|
2
|
-
module Message
|
3
|
-
class NodeList < Base
|
4
|
-
def message
|
5
|
-
@message ||= Node.as_json
|
6
|
-
end
|
7
|
-
|
8
|
-
def handle
|
9
|
-
respond
|
10
|
-
return
|
11
|
-
end
|
12
|
-
|
13
|
-
# only need to respond if this server has node entries that the
|
14
|
-
# sender of this message doesn't have
|
15
|
-
def respond
|
16
|
-
received_list = message
|
17
|
-
we_only_have, they_only_have = Node.diff(received_list)
|
18
|
-
|
19
|
-
Display.debug('node_list#respond: me: ' + we_only_have.to_s)
|
20
|
-
Display.debug('node_list#respond: they: ' + they_only_have.to_s)
|
21
|
-
|
22
|
-
if they_only_have.present?
|
23
|
-
they_only_have.each do |n|
|
24
|
-
Node.from_json(n).save!
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
|
29
|
-
location = payload['sender']['location']
|
30
|
-
|
31
|
-
node = Node.find_by_location(location)
|
32
|
-
if we_only_have.present?
|
33
|
-
Display.debug 'we have nodes that they do not'
|
34
|
-
|
35
|
-
# give the sender our list
|
36
|
-
MeshChat::Net::Client.send(
|
37
|
-
node: node,
|
38
|
-
message: NodeListDiff.new(message: we_only_have)
|
39
|
-
)
|
40
|
-
|
41
|
-
# give people we know about
|
42
|
-
# (but the sender of the Node List may not know about)
|
43
|
-
# our node list diff
|
44
|
-
Node.online.each do |entry|
|
45
|
-
MeshChat::Net::Client.send(
|
46
|
-
node: entry,
|
47
|
-
message: NodeListDiff.new(message: they_only_have)
|
48
|
-
)
|
49
|
-
end
|
50
|
-
else
|
51
|
-
Display.debug 'node lists are in sync'
|
52
|
-
|
53
|
-
# lists are in sync, confirm with hash
|
54
|
-
MeshChat::Net::Client.send(
|
55
|
-
node: node,
|
56
|
-
message: NodeListHash.new
|
57
|
-
)
|
58
|
-
end
|
59
|
-
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
@@ -1,15 +0,0 @@
|
|
1
|
-
module MeshChat
|
2
|
-
module Message
|
3
|
-
class NodeListDiff < Base
|
4
|
-
def handle
|
5
|
-
entries_we_do_not_have = message
|
6
|
-
|
7
|
-
entries_we_do_not_have.each do |entry_as_json|
|
8
|
-
# this will silently fail if there is a duplicate
|
9
|
-
# or if this is an invalid entry
|
10
|
-
Node.from_json(entry_as_json).save
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
@@ -1,33 +0,0 @@
|
|
1
|
-
module MeshChat
|
2
|
-
module Message
|
3
|
-
class NodeListHash < Base
|
4
|
-
def message
|
5
|
-
@message ||= Node.as_sha512
|
6
|
-
end
|
7
|
-
|
8
|
-
# node list hash is received
|
9
|
-
# @return [NilClass] no output for this message type
|
10
|
-
def handle
|
11
|
-
respond
|
12
|
-
return
|
13
|
-
end
|
14
|
-
|
15
|
-
def respond
|
16
|
-
if message != Node.as_sha512
|
17
|
-
Display.debug 'node list hashes do not match'
|
18
|
-
location = payload['sender']['location']
|
19
|
-
|
20
|
-
Display.debug 'sending a node list to ' + location
|
21
|
-
node = Node.find_by_location(location)
|
22
|
-
|
23
|
-
MeshChat::Net::Client.send(
|
24
|
-
node: node,
|
25
|
-
message: NodeList.new(message: Node.as_json)
|
26
|
-
)
|
27
|
-
else
|
28
|
-
Display.debug 'node list hash matches'
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|