gamefic 1.6.0 → 2.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.rubocop.yml +16 -0
- data/.solargraph.yml +5 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile +7 -0
- data/LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +10 -0
- data/gamefic.gemspec +27 -0
- data/lib/gamefic.rb +11 -8
- data/lib/gamefic/action.rb +68 -58
- data/lib/gamefic/active.rb +331 -0
- data/lib/gamefic/actor.rb +8 -0
- data/lib/gamefic/command.rb +9 -7
- data/lib/gamefic/core_ext/array.rb +27 -49
- data/lib/gamefic/core_ext/string.rb +25 -16
- data/lib/gamefic/describable.rb +37 -22
- data/lib/gamefic/element.rb +47 -0
- data/lib/gamefic/entity.rb +24 -48
- data/lib/gamefic/{matchable.rb → keywords.rb} +52 -50
- data/lib/gamefic/messaging.rb +43 -45
- data/lib/gamefic/node.rb +14 -5
- data/lib/gamefic/plot.rb +73 -85
- data/lib/gamefic/plot/darkroom.rb +80 -0
- data/lib/gamefic/plot/host.rb +42 -46
- data/lib/gamefic/plot/snapshot.rb +14 -214
- data/lib/gamefic/query.rb +15 -17
- data/lib/gamefic/query/base.rb +51 -42
- data/lib/gamefic/query/children.rb +0 -0
- data/lib/gamefic/query/descendants.rb +2 -2
- data/lib/gamefic/query/external.rb +18 -0
- data/lib/gamefic/query/family.rb +3 -7
- data/lib/gamefic/query/matches.rb +75 -67
- data/lib/gamefic/query/parent.rb +0 -0
- data/lib/gamefic/query/siblings.rb +0 -0
- data/lib/gamefic/query/text.rb +12 -12
- data/lib/gamefic/query/tree.rb +17 -0
- data/lib/gamefic/scene.rb +1 -5
- data/lib/gamefic/scene/{active.rb → activity.rb} +4 -6
- data/lib/gamefic/scene/base.rb +77 -13
- data/lib/gamefic/scene/conclusion.rb +0 -2
- data/lib/gamefic/scene/custom.rb +0 -2
- data/lib/gamefic/scene/multiple_choice.rb +18 -16
- data/lib/gamefic/scene/multiple_scene.rb +29 -20
- data/lib/gamefic/scene/pause.rb +7 -2
- data/lib/gamefic/scene/yes_or_no.rb +21 -9
- data/lib/gamefic/scriptable.rb +88 -0
- data/lib/gamefic/serialize.rb +223 -0
- data/lib/gamefic/subplot.rb +47 -51
- data/lib/gamefic/syntax.rb +15 -13
- data/lib/gamefic/version.rb +3 -3
- data/lib/gamefic/world.rb +18 -0
- data/lib/gamefic/world/callbacks.rb +135 -0
- data/lib/gamefic/world/commands.rb +184 -0
- data/lib/gamefic/world/entities.rb +98 -0
- data/lib/gamefic/{plot → world}/playbook.rb +245 -236
- data/lib/gamefic/world/players.rb +37 -0
- data/lib/gamefic/world/scenes.rb +226 -0
- metadata +40 -108
- data/bin/gamefic +0 -9
- data/lib/gamefic/character.rb +0 -232
- data/lib/gamefic/character/state.rb +0 -12
- data/lib/gamefic/engine.rb +0 -7
- data/lib/gamefic/engine/base.rb +0 -66
- data/lib/gamefic/engine/tty.rb +0 -24
- data/lib/gamefic/grammar.rb +0 -13
- data/lib/gamefic/grammar/conjugator.rb +0 -20
- data/lib/gamefic/grammar/gender.rb +0 -11
- data/lib/gamefic/grammar/person.rb +0 -10
- data/lib/gamefic/grammar/plural.rb +0 -13
- data/lib/gamefic/grammar/pronouns.rb +0 -105
- data/lib/gamefic/grammar/tense.rb +0 -6
- data/lib/gamefic/grammar/verb_set.rb +0 -43
- data/lib/gamefic/grammar/verbs.rb +0 -26
- data/lib/gamefic/grammar/word_adapter.rb +0 -49
- data/lib/gamefic/plot/articles.rb +0 -22
- data/lib/gamefic/plot/callbacks.rb +0 -127
- data/lib/gamefic/plot/commands.rb +0 -121
- data/lib/gamefic/plot/entities.rb +0 -88
- data/lib/gamefic/plot/players.rb +0 -15
- data/lib/gamefic/plot/scenes.rb +0 -149
- data/lib/gamefic/plot/theater.rb +0 -73
- data/lib/gamefic/plot/you_mount.rb +0 -22
- data/lib/gamefic/script.rb +0 -13
- data/lib/gamefic/script/base.rb +0 -42
- data/lib/gamefic/script/file.rb +0 -14
- data/lib/gamefic/script/text.rb +0 -14
- data/lib/gamefic/shell.rb +0 -76
- data/lib/gamefic/source.rb +0 -14
- data/lib/gamefic/source/base.rb +0 -12
- data/lib/gamefic/source/file.rb +0 -23
- data/lib/gamefic/source/text.rb +0 -16
- data/lib/gamefic/tester.rb +0 -19
- data/lib/gamefic/text.rb +0 -8
- data/lib/gamefic/text/ansi.rb +0 -53
- data/lib/gamefic/text/html.rb +0 -68
- data/lib/gamefic/text/html/conversions.rb +0 -250
- data/lib/gamefic/text/html/entities.rb +0 -9
- data/lib/gamefic/tty.rb +0 -10
- data/lib/gamefic/user.rb +0 -8
- data/lib/gamefic/user/base.rb +0 -15
- data/lib/gamefic/user/buffer.rb +0 -32
- data/lib/gamefic/user/tty.rb +0 -54
data/lib/gamefic/scene/custom.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
module Gamefic
|
2
|
-
|
3
2
|
# Provide a list of options and process the selection in the scene's finish
|
4
3
|
# block. After the scene is finished, the :active scene will be cued unless
|
5
4
|
# some other scene has already been prepared or cued.
|
@@ -8,9 +7,21 @@ module Gamefic
|
|
8
7
|
# instead of a String.
|
9
8
|
#
|
10
9
|
class Scene::MultipleChoice < Scene::Custom
|
10
|
+
# The zero-based index of the selected option.
|
11
|
+
#
|
12
|
+
# @return [Integer]
|
11
13
|
attr_reader :index
|
14
|
+
|
15
|
+
# The one-based index of the selected option.
|
16
|
+
#
|
17
|
+
# @return [Integer]
|
12
18
|
attr_reader :number
|
19
|
+
|
20
|
+
# The full text of the selected option.
|
21
|
+
#
|
22
|
+
# @return [String]
|
13
23
|
attr_reader :selection
|
24
|
+
|
14
25
|
attr_writer :invalid_message
|
15
26
|
|
16
27
|
def post_initialize
|
@@ -18,20 +29,7 @@ module Gamefic
|
|
18
29
|
self.prompt = 'Enter a choice:'
|
19
30
|
end
|
20
31
|
|
21
|
-
#def start actor
|
22
|
-
# data = start_data_for(actor)
|
23
|
-
# data.clear
|
24
|
-
# do_start_block actor, data
|
25
|
-
# tell_options
|
26
|
-
#end
|
27
|
-
|
28
|
-
def start
|
29
|
-
super
|
30
|
-
raise "MultipleChoice scene has zero options" if options.empty?
|
31
|
-
end
|
32
|
-
|
33
32
|
def finish
|
34
|
-
#data = finish_data_for(actor, input)
|
35
33
|
get_choice
|
36
34
|
if selection.nil?
|
37
35
|
actor.tell invalid_message
|
@@ -41,10 +39,16 @@ module Gamefic
|
|
41
39
|
end
|
42
40
|
end
|
43
41
|
|
42
|
+
# The array of available options.
|
43
|
+
#
|
44
|
+
# @return [Array<String>]
|
44
45
|
def options
|
45
46
|
@options ||= []
|
46
47
|
end
|
47
48
|
|
49
|
+
# The text to display when an invalid selection is received.
|
50
|
+
#
|
51
|
+
# @return [String]
|
48
52
|
def invalid_message
|
49
53
|
@invalid_message ||= 'That is not a valid choice.'
|
50
54
|
end
|
@@ -82,7 +86,5 @@ module Gamefic
|
|
82
86
|
list += "</ol>"
|
83
87
|
actor.tell list
|
84
88
|
end
|
85
|
-
|
86
89
|
end
|
87
|
-
|
88
90
|
end
|
@@ -1,20 +1,29 @@
|
|
1
|
-
module Gamefic
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
1
|
+
module Gamefic
|
2
|
+
class Scene::MultipleScene < Scene::MultipleChoice
|
3
|
+
def option_map
|
4
|
+
@option_map ||= {}
|
5
|
+
end
|
6
|
+
|
7
|
+
# @param option [String]
|
8
|
+
# @param scene [Class<Gamefic::Scene::Base>]
|
9
|
+
def map option, scene
|
10
|
+
options.push option
|
11
|
+
option_map[option] = scene
|
12
|
+
end
|
13
|
+
|
14
|
+
def finish
|
15
|
+
get_choice
|
16
|
+
unless selection.nil?
|
17
|
+
actor.prepare option_map[selection]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def state
|
22
|
+
entered = {}
|
23
|
+
option_map.each_pair do |k, v|
|
24
|
+
entered[k] = actor.entered?(v)
|
25
|
+
end
|
26
|
+
super.merge entered: entered
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/gamefic/scene/pause.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
module Gamefic
|
2
|
-
|
3
2
|
# Pause for user input.
|
4
3
|
#
|
5
4
|
class Scene::Pause < Scene::Custom
|
@@ -7,6 +6,12 @@ module Gamefic
|
|
7
6
|
self.type = 'Pause'
|
8
7
|
self.prompt = 'Press enter to continue...'
|
9
8
|
end
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def tracked?
|
12
|
+
@tracked = true if @tracked.nil?
|
13
|
+
@tracked
|
14
|
+
end
|
15
|
+
end
|
10
16
|
end
|
11
|
-
|
12
17
|
end
|
@@ -1,32 +1,41 @@
|
|
1
1
|
module Gamefic
|
2
|
-
|
3
2
|
# Prompt the user to answer "yes" or "no". The scene will accept variations
|
4
3
|
# like "YES" or "n" and normalize the answer to "yes" or "no" in the finish
|
5
4
|
# block. After the scene is finished, the :active scene will be cued if no
|
6
5
|
# other scene has been prepared or cued.
|
7
6
|
#
|
8
7
|
class Scene::YesOrNo < Scene::Custom
|
8
|
+
attr_writer :invalid_message
|
9
|
+
|
9
10
|
def post_initialize
|
10
11
|
self.type = 'YesOrNo'
|
11
|
-
self.prompt = 'Yes or No
|
12
|
+
self.prompt = 'Yes or No:'
|
12
13
|
end
|
13
14
|
|
15
|
+
# True if the actor's answer is Yes.
|
16
|
+
# Any answer beginning with letter Y is considered Yes.
|
17
|
+
#
|
18
|
+
# @return [Boolean]
|
14
19
|
def yes?
|
15
|
-
input.to_s[0,1].downcase == 'y'
|
20
|
+
input.to_s[0,1].downcase == 'y' or input.to_i == 1
|
16
21
|
end
|
17
22
|
|
23
|
+
# True if the actor's answer is No.
|
24
|
+
# Any answer beginning with letter N is considered No.
|
25
|
+
#
|
26
|
+
# @return [Boolean]
|
18
27
|
def no?
|
19
|
-
input.to_s[0,1].downcase == 'n'
|
28
|
+
input.to_s[0,1].downcase == 'n' or input.to_i == 2
|
20
29
|
end
|
21
30
|
|
31
|
+
# The message sent to the user for an invalid answer, i.e., the input
|
32
|
+
# could not be resolved to either Yes or No.
|
33
|
+
#
|
34
|
+
# @return [String]
|
22
35
|
def invalid_message
|
23
36
|
@invalid_message ||= 'Please enter Yes or No.'
|
24
37
|
end
|
25
38
|
|
26
|
-
def prompt
|
27
|
-
@prompt ||= 'Yes or No?'
|
28
|
-
end
|
29
|
-
|
30
39
|
def finish
|
31
40
|
if yes? or no?
|
32
41
|
super
|
@@ -34,6 +43,9 @@ module Gamefic
|
|
34
43
|
actor.tell invalid_message
|
35
44
|
end
|
36
45
|
end
|
37
|
-
end
|
38
46
|
|
47
|
+
def state
|
48
|
+
super.merge options: ['Yes', 'No']
|
49
|
+
end
|
50
|
+
end
|
39
51
|
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Gamefic
|
2
|
+
# The Scriptable module provides a clean room (aka "theater") for scripts.
|
3
|
+
#
|
4
|
+
# @!method stage(*args, &block)
|
5
|
+
# Execute a block of code in a subset of the owner's scope.
|
6
|
+
#
|
7
|
+
# The provided code is evaluated inside a clean room object that has its
|
8
|
+
# own instance variables and access to the owner's public methods. The proc
|
9
|
+
# can accept the method call's arguments.
|
10
|
+
#
|
11
|
+
# @example Execute a block of code
|
12
|
+
# stage {
|
13
|
+
# puts 'Hello'
|
14
|
+
# }
|
15
|
+
#
|
16
|
+
# @example Execute a block of code with arguments
|
17
|
+
# stage 'hello' { |message|
|
18
|
+
# puts message # <- prints 'hello'
|
19
|
+
# }
|
20
|
+
#
|
21
|
+
# @example Use an instance variable
|
22
|
+
# stage { @message = 'hello'" }
|
23
|
+
# stage { puts @message } # <- prints 'hello'
|
24
|
+
#
|
25
|
+
# @yieldpublic [Gamefic::Plot]
|
26
|
+
# @return [Object] The value returned by the executed code
|
27
|
+
#
|
28
|
+
# @!method theater
|
29
|
+
# The object that acts as an isolated namespace for staged code.
|
30
|
+
# @return [Object]
|
31
|
+
#
|
32
|
+
# @!parse alias cleanroom theater
|
33
|
+
module Scriptable
|
34
|
+
module ClassMethods
|
35
|
+
# An array of blocks that were added by the `script` class method.
|
36
|
+
#
|
37
|
+
# @return [Array<Proc>]
|
38
|
+
def blocks
|
39
|
+
@blocks ||= []
|
40
|
+
end
|
41
|
+
|
42
|
+
# Add a block to be executed by the instance's `stage` method.
|
43
|
+
#
|
44
|
+
# Note that `script` does not execute the block instantly, but stores
|
45
|
+
# it in the `blocks` array to be executed later.
|
46
|
+
#
|
47
|
+
# @yieldpublic [Gamefic::Plot]
|
48
|
+
def script &block
|
49
|
+
blocks.push block
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.included klass
|
54
|
+
klass.extend ClassMethods
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
# Execute all the scripts that were added by the `script` class method.
|
60
|
+
#
|
61
|
+
def run_scripts
|
62
|
+
self.class.blocks.each { |blk| stage &blk }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# @note #stage and #theater are implemented this way so the clean room object
|
68
|
+
# defines its classes and modules in the root namespace.
|
69
|
+
Gamefic::Scriptable.module_exec do
|
70
|
+
define_method :stage do |*args, &block|
|
71
|
+
theater.instance_exec *args, &block
|
72
|
+
end
|
73
|
+
|
74
|
+
define_method :theater do
|
75
|
+
@theater ||= begin
|
76
|
+
instance = self
|
77
|
+
theater ||= Object.new
|
78
|
+
theater.instance_exec do
|
79
|
+
define_singleton_method :method_missing do |symbol, *args, &block|
|
80
|
+
instance.public_send :public_send, symbol, *args, &block
|
81
|
+
end
|
82
|
+
end
|
83
|
+
theater.extend Gamefic::Serialize
|
84
|
+
theater
|
85
|
+
end
|
86
|
+
end
|
87
|
+
alias cleanroom theater
|
88
|
+
end
|
@@ -0,0 +1,223 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Gamefic
|
4
|
+
module Serialize
|
5
|
+
def to_serial(index = [])
|
6
|
+
if index.include?(self)
|
7
|
+
{
|
8
|
+
'instance' => "#<ELE_#{index.index(self)}>",
|
9
|
+
'ivars' => {}
|
10
|
+
}
|
11
|
+
else
|
12
|
+
if self.class == Class && self.name
|
13
|
+
{
|
14
|
+
'class' => 'Class',
|
15
|
+
'name' => name
|
16
|
+
}
|
17
|
+
else
|
18
|
+
index.push self if self.is_a?(Gamefic::Serialize)
|
19
|
+
{
|
20
|
+
'class' => serialized_class(index),
|
21
|
+
'ivars' => serialize_instance_variables(index)
|
22
|
+
}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def serialized_class index
|
28
|
+
if index.include?(self.class)
|
29
|
+
"#<ELE_#{index.index(self.class)}>"
|
30
|
+
else
|
31
|
+
self.class.to_s
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.instances
|
36
|
+
GC.start
|
37
|
+
result = []
|
38
|
+
ObjectSpace.each_object(Gamefic::Serialize) { |obj| result.push obj }
|
39
|
+
result
|
40
|
+
end
|
41
|
+
|
42
|
+
# @param string [String]
|
43
|
+
# @return [Object]
|
44
|
+
def self.string_to_constant string
|
45
|
+
space = Object
|
46
|
+
string.split('::').each do |part|
|
47
|
+
space = space.const_get(part)
|
48
|
+
end
|
49
|
+
space
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class Object
|
55
|
+
class << self
|
56
|
+
def exclude_from_serial ary
|
57
|
+
@excluded_from_serial = ary
|
58
|
+
end
|
59
|
+
|
60
|
+
def excluded_from_serial
|
61
|
+
@excluded_from_serial ||= []
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_serial(_index)
|
66
|
+
return self if [true, false, nil].include?(self)
|
67
|
+
# @todo This warning is a little too spammy. Set up a logger so it can be
|
68
|
+
# limited to an info or debug level.
|
69
|
+
# STDERR.puts "Unable to convert #{self} to element"
|
70
|
+
"#<UNKNOWN>"
|
71
|
+
end
|
72
|
+
|
73
|
+
def from_serial(index = [])
|
74
|
+
if self.is_a?(Hash) && (self['class'] || self['instance'])
|
75
|
+
if self['instance']
|
76
|
+
elematch = self['instance'].match(/^#<ELE_([\d]+)>$/)
|
77
|
+
object = index[elematch[1].to_i]
|
78
|
+
raise "Unable to load indexed element ##{elematch[1]} #{self}" if object.nil?
|
79
|
+
elsif self['class']
|
80
|
+
if self['class'] == 'Hash'
|
81
|
+
object = {}
|
82
|
+
self['data'].each do |arr|
|
83
|
+
object[arr[0].from_serial(index)] = arr[1].from_serial(index)
|
84
|
+
end
|
85
|
+
return object
|
86
|
+
elsif self['class'] == 'Class'
|
87
|
+
return Gamefic::Serialize.string_to_constant(self['name'])
|
88
|
+
elsif self['class'] == 'Set'
|
89
|
+
return Set.new(self['data'].map { |el| el.from_serial(index) })
|
90
|
+
else
|
91
|
+
elematch = self['class'].match(/^#<ELE_([\d]+)>$/)
|
92
|
+
if elematch
|
93
|
+
klass = index[elematch[1].to_i]
|
94
|
+
else
|
95
|
+
klass = Gamefic::Serialize.string_to_constant(self['class'])
|
96
|
+
end
|
97
|
+
raise "Unable to find class #{self['class']} #{self}" if klass.nil?
|
98
|
+
object = klass.allocate
|
99
|
+
index.push object if object.is_a?(Gamefic::Serialize)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
self['ivars'].each_pair do |k, v|
|
103
|
+
object.instance_variable_set(k, v.from_serial(index))
|
104
|
+
end
|
105
|
+
object
|
106
|
+
elsif self.is_a?(Numeric)
|
107
|
+
self
|
108
|
+
elsif self.is_a?(String)
|
109
|
+
match = self.match(/#<ELE_([0-9]+)>/)
|
110
|
+
return index.index(match[1].to_i) if match
|
111
|
+
match = self.match(/#<SYM:([a-z0-9_\?\!]+)>/i)
|
112
|
+
return match[1].to_sym if match
|
113
|
+
# return nil if self == '#<UNKNOWN>'
|
114
|
+
self
|
115
|
+
elsif self.is_a?(Hash)
|
116
|
+
result = {}
|
117
|
+
unknown = false
|
118
|
+
self.each_pair do |k, v|
|
119
|
+
k2 = k.from_serial(index)
|
120
|
+
v2 = v.from_serial(index)
|
121
|
+
if k2 == "#<UNKNOWN>" || v2 == "#<UNKNOWN>"
|
122
|
+
unknown = true
|
123
|
+
break
|
124
|
+
end
|
125
|
+
result[k2] = v2
|
126
|
+
end
|
127
|
+
result = "#<UNKNOWN>" if unknown
|
128
|
+
result
|
129
|
+
elsif self && self != true
|
130
|
+
STDERR.puts "Unable to unserialize #{self.class}"
|
131
|
+
nil
|
132
|
+
else
|
133
|
+
# true, false, or nil
|
134
|
+
self
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def serialize_instance_variables(index)
|
139
|
+
result = {}
|
140
|
+
instance_variables.each do |k|
|
141
|
+
next if self.class.excluded_from_serial.include?(k)
|
142
|
+
val = instance_variable_get(k)
|
143
|
+
if index.include?(val)
|
144
|
+
result[k.to_s] = {
|
145
|
+
'instance' => "#<ELE_#{index.index(val)}>",
|
146
|
+
'ivars' => {}
|
147
|
+
}
|
148
|
+
else
|
149
|
+
result[k.to_s] = val.to_serial(index)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
result
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
class Class
|
157
|
+
def to_serial(index = [])
|
158
|
+
if name.nil?
|
159
|
+
super
|
160
|
+
else
|
161
|
+
{
|
162
|
+
'class' => 'Class',
|
163
|
+
'name' => name
|
164
|
+
}
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
class Symbol
|
170
|
+
def to_serial(_index = [])
|
171
|
+
"#<SYM:#{self}>"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
class String
|
176
|
+
def to_serial(_index = [])
|
177
|
+
self
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
class Numeric
|
182
|
+
def to_serial(_index = [])
|
183
|
+
self
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
class Array
|
188
|
+
def to_serial(index = [])
|
189
|
+
map do |e|
|
190
|
+
s = e.to_serial(index)
|
191
|
+
return "#<UNKNOWN>" if s == "#<UNKNOWN>"
|
192
|
+
s
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def from_serial(index = [])
|
197
|
+
result = map { |e| e.from_serial(index) }
|
198
|
+
result = "#<UNKNOWN>" if result.any? { |e| e == "#<UNKNOWN>" }
|
199
|
+
result
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
class Hash
|
204
|
+
def to_serial(index = [])
|
205
|
+
result = {'class' => 'Hash', 'data' => []}
|
206
|
+
each_pair do |key, value|
|
207
|
+
k2 = key.to_serial(index)
|
208
|
+
v2 = value.to_serial(index)
|
209
|
+
return "#<UNKNOWN>" if k2 == "#<UNKNOWN>" || v2 == "#<UNKNOWN>"
|
210
|
+
result['data'].push [k2, v2]
|
211
|
+
end
|
212
|
+
result
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
class Set
|
217
|
+
def to_serial(index = [])
|
218
|
+
{
|
219
|
+
'class' => 'Set',
|
220
|
+
'data' => to_a.map { |el| el.to_serial(index) }
|
221
|
+
}
|
222
|
+
end
|
223
|
+
end
|