gamefic 0.2.0 → 0.6.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/lib/gamefic/action.rb +87 -56
- data/lib/gamefic/ansi.rb +55 -0
- data/lib/gamefic/character.rb +130 -76
- data/lib/gamefic/command.rb +19 -0
- data/lib/gamefic/core_ext/array.rb +51 -40
- data/lib/gamefic/core_ext/string.rb +4 -0
- data/lib/gamefic/describable.rb +108 -46
- data/lib/gamefic/direction.rb +46 -0
- data/lib/gamefic/director/delegate.rb +91 -0
- data/lib/gamefic/director/order.rb +10 -0
- data/lib/gamefic/director/parser.rb +119 -0
- data/lib/gamefic/director.rb +16 -197
- data/lib/gamefic/engine/cgi.rb +221 -0
- data/lib/gamefic/engine/tty.rb +237 -0
- data/lib/gamefic/engine.rb +88 -67
- data/lib/gamefic/entity.rb +96 -69
- data/lib/gamefic/grammar/conjugator.rb +20 -0
- data/lib/gamefic/grammar/gender.rb +11 -0
- data/lib/gamefic/grammar/person.rb +10 -0
- data/lib/gamefic/grammar/plural.rb +13 -0
- data/lib/gamefic/grammar/pronouns.rb +60 -0
- data/lib/gamefic/grammar/tense.rb +6 -0
- data/lib/gamefic/grammar/verb_set.rb +43 -0
- data/lib/gamefic/grammar/verbs.rb +25 -0
- data/lib/gamefic/grammar/word_adapter.rb +36 -0
- data/lib/gamefic/grammar.rb +13 -0
- data/lib/gamefic/html.rb +53 -0
- data/lib/gamefic/keywords.rb +51 -33
- data/lib/gamefic/node.rb +65 -58
- data/lib/gamefic/plot/article_mount.rb +22 -0
- data/lib/gamefic/plot/command_mount.rb +88 -0
- data/lib/gamefic/plot/entity_mount.rb +45 -0
- data/lib/gamefic/plot/query_mount.rb +9 -0
- data/lib/gamefic/plot/scene_mount.rb +181 -0
- data/lib/gamefic/plot/you_mount.rb +22 -0
- data/lib/gamefic/plot.rb +296 -247
- data/lib/gamefic/query/ambiguous_children.rb +5 -0
- data/lib/gamefic/query/base.rb +265 -0
- data/lib/gamefic/query/children.rb +10 -0
- data/lib/gamefic/query/expression.rb +47 -0
- data/lib/gamefic/query/family.rb +10 -0
- data/lib/gamefic/query/many_children.rb +7 -0
- data/lib/gamefic/query/matches.rb +11 -0
- data/lib/gamefic/query/parent.rb +10 -0
- data/lib/gamefic/query/plural_children.rb +14 -0
- data/lib/gamefic/query/self.rb +10 -0
- data/lib/gamefic/query/siblings.rb +10 -0
- data/lib/gamefic/query/text.rb +43 -0
- data/lib/gamefic/query.rb +19 -203
- data/lib/gamefic/rule.rb +18 -0
- data/lib/gamefic/scene/active.rb +25 -0
- data/lib/gamefic/scene/concluded.rb +22 -0
- data/lib/gamefic/scene/multiplechoice.rb +74 -0
- data/lib/gamefic/scene/paused.rb +26 -0
- data/lib/gamefic/scene/yesorno.rb +43 -0
- data/lib/gamefic/scene.rb +125 -0
- data/lib/gamefic/script/base.rb +33 -0
- data/lib/gamefic/script/file.rb +14 -0
- data/lib/gamefic/script/text.rb +14 -0
- data/lib/gamefic/script.rb +9 -0
- data/lib/gamefic/serialized.rb +24 -0
- data/lib/gamefic/shell.rb +9 -247
- data/lib/gamefic/snapshots.rb +134 -0
- data/lib/gamefic/source/base.rb +12 -0
- data/lib/gamefic/source/file.rb +23 -0
- data/lib/gamefic/source/text.rb +16 -0
- data/lib/gamefic/source.rb +9 -0
- data/lib/gamefic/stage.rb +75 -0
- data/lib/gamefic/syntax.rb +106 -124
- data/lib/gamefic/tester.rb +20 -0
- data/lib/gamefic/version.rb +3 -0
- data/lib/gamefic.rb +18 -12
- metadata +102 -70
- data/lib/gamefic/base.rb +0 -10
- data/lib/gamefic/before.rb +0 -12
- data/lib/gamefic/import/basics/actions/close.rb +0 -16
- data/lib/gamefic/import/basics/actions/commands.rb +0 -3
- data/lib/gamefic/import/basics/actions/drop-in.rb +0 -17
- data/lib/gamefic/import/basics/actions/drop-on.rb +0 -16
- data/lib/gamefic/import/basics/actions/drop.rb +0 -30
- data/lib/gamefic/import/basics/actions/enter.rb +0 -16
- data/lib/gamefic/import/basics/actions/go.rb +0 -35
- data/lib/gamefic/import/basics/actions/inventory.rb +0 -8
- data/lib/gamefic/import/basics/actions/leave.rb +0 -29
- data/lib/gamefic/import/basics/actions/look-in-at.rb +0 -27
- data/lib/gamefic/import/basics/actions/look-under.rb +0 -3
- data/lib/gamefic/import/basics/actions/look.rb +0 -71
- data/lib/gamefic/import/basics/actions/nil.rb +0 -25
- data/lib/gamefic/import/basics/actions/open.rb +0 -23
- data/lib/gamefic/import/basics/actions/quit.rb +0 -3
- data/lib/gamefic/import/basics/actions/take.rb +0 -107
- data/lib/gamefic/import/basics/entities/container.rb +0 -8
- data/lib/gamefic/import/basics/entities/entity.rb +0 -11
- data/lib/gamefic/import/basics/entities/fixture.rb +0 -5
- data/lib/gamefic/import/basics/entities/item.rb +0 -5
- data/lib/gamefic/import/basics/entities/portal.rb +0 -40
- data/lib/gamefic/import/basics/entities/room.rb +0 -30
- data/lib/gamefic/import/basics/entities/scenery.rb +0 -5
- data/lib/gamefic/import/basics/entities/supporter.rb +0 -6
- data/lib/gamefic/import/basics/entities/thing.rb +0 -16
- data/lib/gamefic/import/basics/queries/reachable.rb +0 -38
- data/lib/gamefic/import/basics/queries/room.rb +0 -8
- data/lib/gamefic/import/basics/queries/visible.rb +0 -32
- data/lib/gamefic/import/basics/rules/has-enough-light.rb +0 -14
- data/lib/gamefic/import/basics.old/actions/container.rb +0 -112
- data/lib/gamefic/import/basics.old/actions/inventory.rb +0 -50
- data/lib/gamefic/import/basics.old/actions/look.rb +0 -53
- data/lib/gamefic/import/basics.old/actions/meta.rb +0 -6
- data/lib/gamefic/import/basics.old/actions/traversal.rb +0 -35
- data/lib/gamefic/import/basics.old/actions.rb +0 -1
- data/lib/gamefic/import/basics.old/entities/container.rb +0 -3
- data/lib/gamefic/import/basics.old/entities/fixture.rb +0 -3
- data/lib/gamefic/import/basics.old/entities/item.rb +0 -3
- data/lib/gamefic/import/basics.old/entities/portal.rb +0 -43
- data/lib/gamefic/import/basics.old/entities/room.rb +0 -27
- data/lib/gamefic/import/basics.old/entities/scenery.rb +0 -3
- data/lib/gamefic/import/basics.old/entities/supporter.rb +0 -3
- data/lib/gamefic/import/basics.old/entities.rb +0 -1
- data/lib/gamefic/import/basics.old/room_modes.rb +0 -48
- data/lib/gamefic/import/basics.rb +0 -6
- data/lib/gamefic/import/room_modes.rb +0 -48
- data/lib/gamefic/import/standard.rb +0 -1
- data/lib/gamefic/meta.rb +0 -12
- data/lib/gamefic/optionset.rb +0 -114
- data/lib/gamefic/requirement.rb +0 -14
- data/lib/gamefic/story.rb +0 -14
- data/lib/gamefic/thing.rb +0 -7
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Gamefic
|
|
2
|
+
|
|
3
|
+
class ActiveSceneManager < SceneManager
|
|
4
|
+
def scene_class
|
|
5
|
+
ActiveScene
|
|
6
|
+
end
|
|
7
|
+
def state
|
|
8
|
+
@state ||= "Active"
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class ActiveScene < Scene
|
|
13
|
+
def finish actor, input
|
|
14
|
+
@data.input = input
|
|
15
|
+
if @finish.nil?
|
|
16
|
+
last_order = actor.perform data.input
|
|
17
|
+
# HACK Set the last_order here so inline performs don't set it
|
|
18
|
+
actor.send(:last_order=, last_order)
|
|
19
|
+
else
|
|
20
|
+
@finish.call actor, data
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Gamefic
|
|
2
|
+
|
|
3
|
+
class ConcludedSceneManager < SceneManager
|
|
4
|
+
def scene_class
|
|
5
|
+
ConcludedScene
|
|
6
|
+
end
|
|
7
|
+
def state
|
|
8
|
+
@state ||= "Concluded"
|
|
9
|
+
end
|
|
10
|
+
def prompt
|
|
11
|
+
@prompt ||= "GAME OVER"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class ConcludedScene < Scene
|
|
16
|
+
# TODO: This class might need some logic for closing the game. Then again,
|
|
17
|
+
# maybe not. The Concluded state might be enough for the plot to know what
|
|
18
|
+
# to do. In fact, if the plot detects the Concluded state, it might never
|
|
19
|
+
# get around to calling the scene's finish proc.
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
module Gamefic
|
|
2
|
+
|
|
3
|
+
class MultipleChoiceSceneManager < SceneManager
|
|
4
|
+
def data_class
|
|
5
|
+
MultipleChoiceSceneData
|
|
6
|
+
end
|
|
7
|
+
def scene_class
|
|
8
|
+
MultipleChoiceScene
|
|
9
|
+
end
|
|
10
|
+
def state
|
|
11
|
+
@state ||= "MultipleChoice"
|
|
12
|
+
end
|
|
13
|
+
def prompt
|
|
14
|
+
@prompt ||= "Enter a choice:"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class MultipleChoiceSceneData < SceneData
|
|
19
|
+
attr_accessor :index, :selection, :options
|
|
20
|
+
def options
|
|
21
|
+
@options ||= []
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class MultipleChoiceScene < Scene
|
|
26
|
+
def initialize manager, key
|
|
27
|
+
super
|
|
28
|
+
@prompt ||= "Enter a choice:"
|
|
29
|
+
end
|
|
30
|
+
def start actor
|
|
31
|
+
super
|
|
32
|
+
list = '<ol class="multiple_choice">'
|
|
33
|
+
@data.options.each { |o|
|
|
34
|
+
list += "<li>#{o}</li>"
|
|
35
|
+
}
|
|
36
|
+
list += "</ol>"
|
|
37
|
+
actor.tell list
|
|
38
|
+
@data.prompt ||= @prompt
|
|
39
|
+
end
|
|
40
|
+
def finish actor, input
|
|
41
|
+
@data.input = input
|
|
42
|
+
@data.index = nil
|
|
43
|
+
@data.selection = nil
|
|
44
|
+
if @data.input.strip =~ /[0-9]+/
|
|
45
|
+
if input.to_i > 0
|
|
46
|
+
@data.index = input.to_i - 1
|
|
47
|
+
@data.selection = @data.options[@data.index]
|
|
48
|
+
@data.index = nil if @data.selection.nil?
|
|
49
|
+
end
|
|
50
|
+
else
|
|
51
|
+
i = 0
|
|
52
|
+
@data.options.each { |o|
|
|
53
|
+
if o.casecmp(@data.input).zero?
|
|
54
|
+
@data.index = i
|
|
55
|
+
@data.selection = o
|
|
56
|
+
break
|
|
57
|
+
end
|
|
58
|
+
i += 1
|
|
59
|
+
}
|
|
60
|
+
end
|
|
61
|
+
if @data.selection.nil?
|
|
62
|
+
# TODO: Consider allowing for an error block to customize this
|
|
63
|
+
# response.
|
|
64
|
+
actor.tell "That's not a valid selection."
|
|
65
|
+
else
|
|
66
|
+
@data.next_cue ||= :active
|
|
67
|
+
if !@finish.nil?
|
|
68
|
+
@finish.call actor, @data
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Gamefic
|
|
2
|
+
|
|
3
|
+
class PausedSceneManager < SceneManager
|
|
4
|
+
def scene_class
|
|
5
|
+
PausedScene
|
|
6
|
+
end
|
|
7
|
+
def data_class
|
|
8
|
+
PausedSceneData
|
|
9
|
+
end
|
|
10
|
+
def state
|
|
11
|
+
@state ||= "Paused"
|
|
12
|
+
end
|
|
13
|
+
def prompt
|
|
14
|
+
@prompt ||= "Press enter to continue..."
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class PausedSceneData < SceneData
|
|
19
|
+
attr_accessor :next_cue
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class PausedScene < Scene
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Gamefic
|
|
2
|
+
|
|
3
|
+
class YesOrNoSceneManager < SceneManager
|
|
4
|
+
def scene_class
|
|
5
|
+
YesOrNoScene
|
|
6
|
+
end
|
|
7
|
+
def data_class
|
|
8
|
+
YesOrNoSceneData
|
|
9
|
+
end
|
|
10
|
+
def state
|
|
11
|
+
@state ||= "YesOrNo"
|
|
12
|
+
end
|
|
13
|
+
def prompt
|
|
14
|
+
@prompt ||= "Enter Yes Or No:"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class YesOrNoSceneData < SceneData
|
|
19
|
+
# @!attribute [rw] answer
|
|
20
|
+
# @return [String] The answer provided by the user, normalized to either "yes" or "no"
|
|
21
|
+
attr_accessor :answer
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class YesOrNoScene < Scene
|
|
25
|
+
def finish actor, input
|
|
26
|
+
@data.input = input
|
|
27
|
+
# The input in a YesOrNoScene gets normalized to "yes" or "no"
|
|
28
|
+
@data.answer = nil
|
|
29
|
+
if input.downcase[0, 1] == "y"
|
|
30
|
+
@data.answer = "yes"
|
|
31
|
+
elsif input.downcase[0, 1] == "n"
|
|
32
|
+
@data.answer = "no"
|
|
33
|
+
end
|
|
34
|
+
if @data.answer.nil?
|
|
35
|
+
actor.tell "Please enter Yes or No."
|
|
36
|
+
else
|
|
37
|
+
return if @finish.nil?
|
|
38
|
+
@finish.call actor, data
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
end
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
module Gamefic
|
|
2
|
+
|
|
3
|
+
# SceneManagers handle the creation and execution of player scenes.
|
|
4
|
+
#
|
|
5
|
+
# @example Create a scene that lets the player select a name.
|
|
6
|
+
# scene_managers[:get_name] = SceneManager.new do |manager|
|
|
7
|
+
# manager.state = "Active" # Tell the Engine that this scene accepts input
|
|
8
|
+
# manager.prompt = "Enter your name:"
|
|
9
|
+
# manager.start do |actor, data|
|
|
10
|
+
# actor.tell "Let's start with a formal introduction."
|
|
11
|
+
# end
|
|
12
|
+
# manager.finish do |actor, data|
|
|
13
|
+
# actor[:name] = data.input
|
|
14
|
+
# actor.tell "Howdy, #{actor[:name]}!"
|
|
15
|
+
# actor.next_cue = :active # Proceed to the default :active scene
|
|
16
|
+
# end
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
class SceneManager
|
|
20
|
+
attr_accessor :state
|
|
21
|
+
attr_writer :prompt
|
|
22
|
+
|
|
23
|
+
def initialize &block
|
|
24
|
+
yield self if block_given?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Get the SceneData class that provide data about the current event to a
|
|
28
|
+
# Scene instance.
|
|
29
|
+
#
|
|
30
|
+
def data_class
|
|
31
|
+
SceneData
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Get the name that describes this scene's state.
|
|
35
|
+
# Two common values for the state are Active and Passive. If a scene is
|
|
36
|
+
# Active, it is capable of accepting user input. If it is Passive, it
|
|
37
|
+
# is probably not interactive (e.g., a cutscene) and will usually cue
|
|
38
|
+
# an Active scene in order to continue gameplay.
|
|
39
|
+
#
|
|
40
|
+
# @return [String] The name of the state.
|
|
41
|
+
def state
|
|
42
|
+
@state ||= 'Passive'
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Get the Scene class that the SceneManager uses to prepare a scene for
|
|
46
|
+
# the plot.
|
|
47
|
+
#
|
|
48
|
+
def scene_class
|
|
49
|
+
Scene
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Define a Block to be executed when the scene starts. The Engine should
|
|
53
|
+
# execute this block before the player is queried for input.
|
|
54
|
+
#
|
|
55
|
+
# @yieldparam [Character]
|
|
56
|
+
# @yieldparam [SceneData]
|
|
57
|
+
def start &block
|
|
58
|
+
@start = block
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Define a Block to be executed when the scene finishes. The engine should
|
|
62
|
+
# process user input in this block.
|
|
63
|
+
#
|
|
64
|
+
# @yieldparam [Character]
|
|
65
|
+
# @yieldparam [SceneData]
|
|
66
|
+
def finish &block
|
|
67
|
+
@finish = block
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Prepare a new Scene for execution.
|
|
71
|
+
#
|
|
72
|
+
# @return [Scene]
|
|
73
|
+
def prepare key
|
|
74
|
+
scene_class.new(self, key)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Get the prompt to display to the user when requesting input.
|
|
78
|
+
#
|
|
79
|
+
# @return [String]
|
|
80
|
+
def prompt
|
|
81
|
+
@prompt ||= ">"
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
class SceneData
|
|
86
|
+
attr_accessor :input, :prompt, :next_cue
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
class Scene
|
|
90
|
+
attr_reader :data, :state, :key
|
|
91
|
+
|
|
92
|
+
def initialize(manager, key)
|
|
93
|
+
@manager = manager
|
|
94
|
+
@start = manager.instance_variable_get(:@start)
|
|
95
|
+
@finish = manager.instance_variable_get(:@finish)
|
|
96
|
+
@state = manager.state
|
|
97
|
+
@data = manager.data_class.new
|
|
98
|
+
@data.prompt = manager.prompt
|
|
99
|
+
@key = key
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Start the scene. This method is typically called by the Plot.
|
|
103
|
+
#
|
|
104
|
+
# @param actor [Character] The Scene's Character.
|
|
105
|
+
def start actor
|
|
106
|
+
return if @start.nil?
|
|
107
|
+
@data.input = nil
|
|
108
|
+
@start.call actor, @data
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Finish the scene. This method is typically called by the Plot.
|
|
112
|
+
# The Finish method is responsible for processing any input received
|
|
113
|
+
# from players.
|
|
114
|
+
#
|
|
115
|
+
# @param actor [Character] The Scene's Character.
|
|
116
|
+
# @param input [String] Input received from the Character (e.g., a player command).
|
|
117
|
+
def finish actor, input
|
|
118
|
+
@data.next_cue ||= :active
|
|
119
|
+
return if @finish.nil?
|
|
120
|
+
@data.input = input
|
|
121
|
+
@finish.call actor, @data
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Gamefic
|
|
2
|
+
|
|
3
|
+
class Script::Base
|
|
4
|
+
def initialize
|
|
5
|
+
raise "#initialize must be defined in subclasses"
|
|
6
|
+
end
|
|
7
|
+
# Get the script's text.
|
|
8
|
+
# The text must be source code suitable for evaluation via Plot#stage.
|
|
9
|
+
#
|
|
10
|
+
# @return [String]
|
|
11
|
+
def read
|
|
12
|
+
raise "#read must be defined in subclasses"
|
|
13
|
+
end
|
|
14
|
+
# Get the script's path
|
|
15
|
+
#
|
|
16
|
+
# @return [String]
|
|
17
|
+
def path
|
|
18
|
+
raise "#path must be defined in subclasses"
|
|
19
|
+
end
|
|
20
|
+
# Get the absolute path of the script's original file
|
|
21
|
+
#
|
|
22
|
+
# @return [String]
|
|
23
|
+
def absolute_path
|
|
24
|
+
raise "#absolute_path must be defined in subclasses"
|
|
25
|
+
end
|
|
26
|
+
# @param other[Script::Base]
|
|
27
|
+
# @return [Boolean]
|
|
28
|
+
def==(other)
|
|
29
|
+
path == other.path
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Gamefic
|
|
2
|
+
|
|
3
|
+
class Script::Text < Script::Base
|
|
4
|
+
attr_reader :path, :absolute_path
|
|
5
|
+
def initialize path, code, absolute_path = nil
|
|
6
|
+
@path = path
|
|
7
|
+
@code = code
|
|
8
|
+
@absolute_path = absolute_path || path
|
|
9
|
+
end
|
|
10
|
+
def read
|
|
11
|
+
@code
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Gamefic
|
|
2
|
+
module Serialized
|
|
3
|
+
def serialized_attributes
|
|
4
|
+
self.class.serializer.keys
|
|
5
|
+
end
|
|
6
|
+
module ClassMethods
|
|
7
|
+
def serialize *args
|
|
8
|
+
args.each { |a|
|
|
9
|
+
serializer[a] = nil
|
|
10
|
+
}
|
|
11
|
+
end
|
|
12
|
+
def serializer
|
|
13
|
+
@@serialized_attributes ||= from_superclass(:serializer, {}).dup
|
|
14
|
+
end
|
|
15
|
+
private
|
|
16
|
+
def from_superclass(m, default = nil)
|
|
17
|
+
superclass.respond_to?(m) ? superclass.send(m) : default
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
def self.included(base)
|
|
21
|
+
base.extend(Gamefic::Serialized::ClassMethods)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|