gamefic 1.7.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +13 -0
  5. data/.solargraph.yml +5 -0
  6. data/Gemfile +7 -0
  7. data/LICENSE +20 -0
  8. data/README.md +28 -0
  9. data/Rakefile +10 -0
  10. data/gamefic.gemspec +27 -0
  11. data/lib/gamefic.rb +7 -6
  12. data/lib/gamefic/action.rb +38 -28
  13. data/lib/gamefic/active.rb +325 -280
  14. data/lib/gamefic/actor.rb +8 -5
  15. data/lib/gamefic/command.rb +9 -7
  16. data/lib/gamefic/core_ext/array.rb +24 -49
  17. data/lib/gamefic/core_ext/string.rb +25 -16
  18. data/lib/gamefic/describable.rb +21 -23
  19. data/lib/gamefic/element.rb +43 -31
  20. data/lib/gamefic/entity.rb +6 -12
  21. data/lib/gamefic/index.rb +121 -0
  22. data/lib/gamefic/{matchable.rb → keywords.rb} +52 -50
  23. data/lib/gamefic/messaging.rb +43 -44
  24. data/lib/gamefic/node.rb +14 -5
  25. data/lib/gamefic/plot.rb +69 -89
  26. data/lib/gamefic/plot/darkroom.rb +92 -264
  27. data/lib/gamefic/plot/host.rb +42 -48
  28. data/lib/gamefic/plot/snapshot.rb +5 -18
  29. data/lib/gamefic/query.rb +14 -18
  30. data/lib/gamefic/query/base.rb +30 -18
  31. data/lib/gamefic/query/children.rb +0 -0
  32. data/lib/gamefic/query/external.rb +18 -14
  33. data/lib/gamefic/query/family.rb +1 -7
  34. data/lib/gamefic/query/matches.rb +75 -67
  35. data/lib/gamefic/query/parent.rb +0 -0
  36. data/lib/gamefic/query/siblings.rb +0 -0
  37. data/lib/gamefic/query/text.rb +2 -1
  38. data/lib/gamefic/scene.rb +0 -2
  39. data/lib/gamefic/scene/activity.rb +24 -26
  40. data/lib/gamefic/scene/base.rb +64 -8
  41. data/lib/gamefic/scene/conclusion.rb +0 -2
  42. data/lib/gamefic/scene/custom.rb +0 -2
  43. data/lib/gamefic/scene/multiple_choice.rb +18 -3
  44. data/lib/gamefic/scene/multiple_scene.rb +29 -20
  45. data/lib/gamefic/scene/pause.rb +7 -2
  46. data/lib/gamefic/scene/yes_or_no.rb +21 -9
  47. data/lib/gamefic/scriptable.rb +87 -0
  48. data/lib/gamefic/serialize.rb +68 -0
  49. data/lib/gamefic/subplot.rb +29 -35
  50. data/lib/gamefic/syntax.rb +14 -13
  51. data/lib/gamefic/version.rb +3 -3
  52. data/lib/gamefic/world.rb +16 -0
  53. data/lib/gamefic/world/callbacks.rb +135 -0
  54. data/lib/gamefic/world/commands.rb +184 -0
  55. data/lib/gamefic/{plot → world}/entities.rb +30 -29
  56. data/lib/gamefic/{plot → world}/playbook.rb +255 -240
  57. data/lib/gamefic/world/players.rb +21 -0
  58. data/lib/gamefic/world/scenes.rb +226 -0
  59. metadata +41 -92
  60. data/bin/gamefic +0 -9
  61. data/lib/gamefic/engine.rb +0 -7
  62. data/lib/gamefic/engine/base.rb +0 -59
  63. data/lib/gamefic/engine/tty.rb +0 -24
  64. data/lib/gamefic/grammar.rb +0 -13
  65. data/lib/gamefic/grammar/conjugator.rb +0 -20
  66. data/lib/gamefic/grammar/gender.rb +0 -11
  67. data/lib/gamefic/grammar/person.rb +0 -10
  68. data/lib/gamefic/grammar/plural.rb +0 -13
  69. data/lib/gamefic/grammar/pronouns.rb +0 -106
  70. data/lib/gamefic/grammar/tense.rb +0 -6
  71. data/lib/gamefic/grammar/verb_set.rb +0 -43
  72. data/lib/gamefic/grammar/verbs.rb +0 -26
  73. data/lib/gamefic/grammar/word_adapter.rb +0 -49
  74. data/lib/gamefic/plot/articles.rb +0 -22
  75. data/lib/gamefic/plot/callbacks.rb +0 -126
  76. data/lib/gamefic/plot/commands.rb +0 -120
  77. data/lib/gamefic/plot/players.rb +0 -15
  78. data/lib/gamefic/plot/scenes.rb +0 -187
  79. data/lib/gamefic/plot/theater.rb +0 -73
  80. data/lib/gamefic/plot/you_mount.rb +0 -22
  81. data/lib/gamefic/script.rb +0 -13
  82. data/lib/gamefic/script/base.rb +0 -42
  83. data/lib/gamefic/script/file.rb +0 -14
  84. data/lib/gamefic/script/text.rb +0 -14
  85. data/lib/gamefic/shell.rb +0 -76
  86. data/lib/gamefic/source.rb +0 -14
  87. data/lib/gamefic/source/base.rb +0 -12
  88. data/lib/gamefic/source/file.rb +0 -23
  89. data/lib/gamefic/source/text.rb +0 -16
  90. data/lib/gamefic/tester.rb +0 -19
  91. data/lib/gamefic/text.rb +0 -8
  92. data/lib/gamefic/text/ansi.rb +0 -53
  93. data/lib/gamefic/text/html.rb +0 -68
  94. data/lib/gamefic/text/html/conversions.rb +0 -250
  95. data/lib/gamefic/text/html/entities.rb +0 -9
  96. data/lib/gamefic/tty.rb +0 -10
  97. data/lib/gamefic/user.rb +0 -7
  98. data/lib/gamefic/user/base.rb +0 -29
  99. data/lib/gamefic/user/tty.rb +0 -38
@@ -1,11 +1,9 @@
1
1
  require 'gamefic/command'
2
2
 
3
3
  module Gamefic
4
-
5
4
  class Syntax
6
5
  attr_reader :token_count, :first_word, :verb, :template, :command
7
- @@phrase = '([\w\W\s\S]*?)'
8
-
6
+
9
7
  def initialize template, command
10
8
  words = template.split_words
11
9
  @token_count = words.length
@@ -15,8 +13,8 @@ module Gamefic
15
13
  @token_count -= 1
16
14
  @first_word = ''
17
15
  else
18
- @verb = command_words[0].to_sym if !command_words[0].nil?
19
16
  @first_word = words[0].to_s
17
+ @verb = command_words[0].to_sym
20
18
  end
21
19
  @command = command_words.join(' ')
22
20
  @template = words.join(' ')
@@ -29,7 +27,7 @@ module Gamefic
29
27
  if last_token_is_reg
30
28
  next
31
29
  else
32
- tokens.push @@phrase
30
+ tokens.push '([\w\W\s\S]*?)'
33
31
  last_token_is_reg = true
34
32
  end
35
33
  else
@@ -50,11 +48,11 @@ module Gamefic
50
48
  @replace = subs.join(' ')
51
49
  @regexp = Regexp.new("^#{tokens.join(' ')}$", Regexp::IGNORECASE)
52
50
  end
53
-
51
+
54
52
  # Convert a String into a Command.
55
53
  #
56
54
  # @param text [String]
57
- # @return [Command]
55
+ # @return [Gamefic::Command]
58
56
  def tokenize text
59
57
  m = text.match(@regexp)
60
58
  return nil if m.nil?
@@ -71,7 +69,11 @@ module Gamefic
71
69
  }
72
70
  Command.new @verb, arguments
73
71
  end
74
-
72
+
73
+ # Determine if the specified text matches the syntax's expected pattern.
74
+ #
75
+ # @param text [String]
76
+ # @return [Boolean]
75
77
  def accept? text
76
78
  !text.match(@regexp).nil?
77
79
  end
@@ -82,16 +84,16 @@ module Gamefic
82
84
  def signature
83
85
  [@regexp, @replace]
84
86
  end
85
-
87
+
86
88
  def ==(other)
87
89
  signature == other.signature
88
90
  end
89
-
91
+
90
92
  # Tokenize an Array of Commands from the specified text.
91
93
  #
92
94
  # @param text [String] The text to tokenize.
93
- # @param syntaxes [Array<Syntax>] The Syntaxes to use.
94
- # @return [Array<Command>] The tokenized commands.
95
+ # @param syntaxes [Array<Gamefic::Syntax>] The Syntaxes to use.
96
+ # @return [Array<Gamefic::Command>] The tokenized commands.
95
97
  def self.tokenize text, syntaxes
96
98
  matches = []
97
99
  syntaxes.each { |syntax|
@@ -108,5 +110,4 @@ module Gamefic
108
110
  matches
109
111
  end
110
112
  end
111
-
112
113
  end
@@ -1,3 +1,3 @@
1
- module Gamefic
2
- VERSION = '1.7.0'
3
- end
1
+ module Gamefic
2
+ VERSION = '2.0.0'
3
+ end
@@ -0,0 +1,16 @@
1
+ module Gamefic
2
+ module World
3
+ autoload :Playbook, 'gamefic/world/playbook'
4
+ autoload :Entities, 'gamefic/world/entities'
5
+ autoload :Commands, 'gamefic/world/commands'
6
+ autoload :Callbacks, 'gamefic/world/callbacks'
7
+ autoload :Scenes, 'gamefic/world/scenes'
8
+ autoload :Players, 'gamefic/world/players'
9
+
10
+ include Entities
11
+ include Commands
12
+ include Callbacks
13
+ include Scenes
14
+ include Players
15
+ end
16
+ end
@@ -0,0 +1,135 @@
1
+ module Gamefic
2
+ module World
3
+ module Callbacks
4
+ # Add a block to be executed on preparation of every turn.
5
+ #
6
+ # @example Increment a turn counter
7
+ # turn = 0
8
+ # on_ready do
9
+ # turn += 1
10
+ # end
11
+ #
12
+ def on_ready &block
13
+ ready_procs.push block
14
+ end
15
+
16
+ # Add a block to be executed after the Plot is finished updating a turn.
17
+ #
18
+ def on_update &block
19
+ update_procs.push block
20
+ end
21
+
22
+ # Add a block to be executed for each player at the beginning of a turn.
23
+ #
24
+ # @example Tell the player how many turns they've played.
25
+ # on_player_ready do |player|
26
+ # player[:turns] ||= 0
27
+ # if player[:turns] > 0
28
+ # player.tell "Turn #{player[:turns]}"
29
+ # end
30
+ # player[:turns] += 1
31
+ # end
32
+ #
33
+ # @yieldparam [Gamefic::Actor]
34
+ def on_player_ready &block
35
+ player_ready_procs.push block
36
+ end
37
+
38
+ # Add a block to be executed for each player before an update.
39
+ #
40
+ # @yieldparam[Gamefic::Actor]
41
+ def before_player_update &block
42
+ before_player_update_procs.push block
43
+ end
44
+
45
+ # Add a block to be executed for each player at the end of a turn.
46
+ #
47
+ # @yieldparam [Gamefic::Actor]
48
+ def on_player_update &block
49
+ player_update_procs.push block
50
+ end
51
+
52
+ # Add a block to be executed at the conclusion of the plot.
53
+ #
54
+ # @yieldparam [Gamefic::Actor]
55
+ def on_player_conclude &block
56
+ player_conclude_procs.push block
57
+ end
58
+
59
+ def player_conclude_procs
60
+ @player_conclude_procs ||= []
61
+ end
62
+
63
+ def ready_procs
64
+ @ready_procs ||= []
65
+ end
66
+
67
+ def update_procs
68
+ @update_procs ||= []
69
+ end
70
+
71
+ def player_ready_procs
72
+ @player_ready_procs ||= []
73
+ end
74
+
75
+ def before_player_update_procs
76
+ @before_player_update_procs ||= []
77
+ end
78
+
79
+ def player_update_procs
80
+ @player_update_procs ||= []
81
+ end
82
+
83
+ private
84
+
85
+ # Execute the on_ready blocks. This method is typically called by the
86
+ # Plot while beginning a turn.
87
+ #
88
+ def call_ready
89
+ ready_procs.each { |p| p.call }
90
+ end
91
+
92
+ # Execute the on_update blocks. This method is typically called by the
93
+ # Plot while ending a turn.
94
+ #
95
+ def call_update
96
+ update_procs.each { |p| p.call }
97
+ end
98
+
99
+ # Execute the before_player_update blocks for each player. This method is
100
+ # typically called by the Plot while updating a turn, immediately before
101
+ # processing player input.
102
+ #
103
+ def call_before_player_update
104
+ players.each { |player|
105
+ player.flush
106
+ before_player_update_procs.each { |block| block.call player }
107
+ }
108
+ end
109
+
110
+ # Execute the on_player_ready blocks for each player. This method is
111
+ # typically called by the Plot while beginning a turn, immediately after
112
+ # the on_ready blocks.
113
+ #
114
+ def call_player_ready
115
+ players.each { |player|
116
+ unless player.next_scene.nil? || !player.scene.finished?
117
+ player.cue player.next_scene, **player.next_options
118
+ end
119
+ player.cue default_scene if player.scene.nil?
120
+ player_ready_procs.each { |block| block.call player }
121
+ }
122
+ end
123
+
124
+ # Execute the on_player_update blocks for each player. This method is
125
+ # typically called by the Plot while ending a turn, immediately before the
126
+ # on_ready blocks.
127
+ #
128
+ def call_player_update
129
+ players.each { |player|
130
+ player_update_procs.each { |block| block.call player }
131
+ }
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,184 @@
1
+ require 'gamefic/action'
2
+
3
+ module Gamefic
4
+ module World
5
+ module Commands
6
+ include Gamefic::World::Entities
7
+
8
+ # @return [Gamefic::World::Playbook]
9
+ def playbook
10
+ @playbook ||= Gamefic::World::Playbook.new
11
+ end
12
+
13
+ # Create an Action that responds to a command.
14
+ # An Action uses the command argument to identify the imperative verb that
15
+ # triggers the action.
16
+ # It can also accept queries to tokenize the remainder of the input and
17
+ # filter for particular entities or properties.
18
+ # The block argument contains the code to be executed when the input
19
+ # matches all of the Action's criteria (i.e., verb and queries).
20
+ #
21
+ # @example A simple Action.
22
+ # respond :salute do |actor|
23
+ # actor.tell "Hello, sir!"
24
+ # end
25
+ # # The command "salute" will respond "Hello, sir!"
26
+ #
27
+ # @example An Action that accepts a Character
28
+ # respond :salute, Use.visible(Character) do |actor, character|
29
+ # actor.tell "#{The character} returns your salute."
30
+ # end
31
+ #
32
+ # @param command [Symbol] An imperative verb for the command
33
+ # @param queries [Array<Query::Base>] Filters for the command's tokens
34
+ # @yieldparam [Gamefic::Actor]
35
+ # @return [Class] The resulting Action subclass
36
+ def respond(command, *queries, &proc)
37
+ playbook.respond(command, *map_response_args(queries), &proc)
38
+ end
39
+ alias action respond
40
+
41
+ # Parse a verb and a list of arguments into an action.
42
+ # This method serves as a shortcut to creating an action with one or more
43
+ # arguments that identify specific entities.
44
+ #
45
+ # @example
46
+ # @thing = make Entity, name: 'a thing'
47
+ # parse "use", "the thing" do |actor, thing|
48
+ # actor.tell "You use it."
49
+ # end
50
+ #
51
+ # @raise [ArgumentError] if tokens are unrecognized or ambiguous
52
+ #
53
+ # @param verb [String, Symbol] The command's verb
54
+ # @param tokens [Array<String>] The arguments passed to the action
55
+ # @return [Class] The resulting Action subclass
56
+ def parse verb, *tokens, &proc
57
+ query = Query::External.new(entities)
58
+ params = []
59
+ tokens.each do |arg|
60
+ matches = query.resolve(nil, arg)
61
+ raise ArgumentError, "Unable to resolve token '#{arg}'" if matches.objects.empty?
62
+ raise ArgumentError, "Ambiguous results for '#{arg}'" if matches.objects.length > 1
63
+ params.push Query::Family.new(matches.objects[0])
64
+ end
65
+ respond(verb.to_sym, *params, &proc)
66
+ end
67
+
68
+ # Tokenize and parse a command to create a new Action subclass.
69
+ #
70
+ # @param command [String] The command
71
+ # @yieldparam [Gamefic::Actor]
72
+ # @return [Class] the resulting Action subclass
73
+ def override(command, &proc)
74
+ cmd = Syntax.tokenize(command, playbook.syntaxes).first
75
+ raise "Unable to tokenize command '#{command}'" if cmd.nil?
76
+ parse cmd.verb, *cmd.arguments, &proc
77
+ end
78
+
79
+ # Create a Meta Action that responds to a command.
80
+ # Meta Actions are very similar to standard Actions, except the Plot
81
+ # understands them to be commands that operate above and/or outside of the
82
+ # actual game world. Examples of Meta Actions are commands that report the
83
+ # player's current score, save and restore saved games, or list the game's
84
+ # credits.
85
+ #
86
+ # @example A simple Meta Action
87
+ # meta :credits do |actor|
88
+ # actor.tell "This game was written by John Smith."
89
+ # end
90
+ #
91
+ # @param command [Symbol] An imperative verb for the command
92
+ # @param queries [Array<Query::Base>] Filters for the command's tokens
93
+ # @yieldparam [Gamefic::Actor]
94
+ def meta(command, *queries, &proc)
95
+ playbook.meta command, *queries, &proc
96
+ end
97
+
98
+ # Declare a dismabiguation response for actions.
99
+ # The disambigurator is executed when an action expects an argument to
100
+ # reference a specific entity but its query matched more than one. For
101
+ # example, "red" might refer to either a red key or a red book.
102
+ #
103
+ # If a disambiguator is not defined, the playbook will use its default
104
+ # implementation.
105
+ #
106
+ # @example Tell the player the list of ambiguous entities.
107
+ # disambiguate do |actor, entities|
108
+ # actor.tell "I don't know which you mean: #{entities.join_or}."
109
+ # end
110
+ #
111
+ # @yieldparam [Gamefic::Actor]
112
+ # @yieldparam [Array<Gamefic::Entity>]
113
+ def disambiguate &block
114
+ playbook.disambiguate &block
115
+ end
116
+
117
+ # Validate an order before a character can execute its command.
118
+ #
119
+ # @yieldparam [Gamefic::Director::Order]
120
+ def validate &block
121
+ playbook.validate &block
122
+ end
123
+
124
+ # Create an alternate Syntax for an Action.
125
+ # The command and its translation can be parameterized.
126
+ #
127
+ # @example Create a synonym for the Inventory Action.
128
+ # interpret "catalogue", "inventory"
129
+ # # The command "catalogue" will be translated to "inventory"
130
+ #
131
+ # @example Create a parameterized synonym for the Look Action.
132
+ # interpret "scrutinize :entity", "look :entity"
133
+ # # The command "scrutinize chair" will be translated to "look chair"
134
+ #
135
+ # @param command [String] The format of the original command
136
+ # @param translation [String] The format of the translated command
137
+ # @return [Syntax] the Syntax object
138
+ def interpret command, translation
139
+ playbook.interpret command, translation
140
+ end
141
+ alias xlate interpret
142
+
143
+ # Get an Array of available verbs.
144
+ #
145
+ # @return [Array<String>]
146
+ def verbs
147
+ playbook.verbs.map { |v| v.to_s }.reject{ |v| v.start_with?('_') }
148
+ end
149
+
150
+ # Get an Array of all Actions defined in the Plot.
151
+ #
152
+ # @return [Array<Action>]
153
+ def actions
154
+ playbook.actions
155
+ end
156
+
157
+ def get_default_query
158
+ @default_query_class ||= Gamefic::Query::Family
159
+ end
160
+
161
+ def set_default_query cls
162
+ @default_query_class = cls
163
+ end
164
+
165
+ private
166
+
167
+ def map_response_args queries
168
+ result = []
169
+ queries.each do |q|
170
+ if q.is_a?(Regexp)
171
+ result.push Gamefic::Query::Text.new(q)
172
+ elsif q.is_a?(Gamefic::Query::Base)
173
+ result.push q
174
+ elsif q.is_a?(Gamefic::Element) || (q.is_a?(Class) && q <= Gamefic::Element)
175
+ result.push get_default_query.new(q)
176
+ else
177
+ raise ArgumentError.new("Invalid argument for response: #{q}")
178
+ end
179
+ end
180
+ result
181
+ end
182
+ end
183
+ end
184
+ end
@@ -1,6 +1,5 @@
1
1
  module Gamefic
2
-
3
- class Plot
2
+ module World
4
3
  module Entities
5
4
  # Make a new Entity with the provided properties.
6
5
  #
@@ -8,16 +7,16 @@ module Gamefic
8
7
  # chair = make Entity, name: 'red chair'
9
8
  # chair.name #=> 'red chair'
10
9
  #
10
+ # @raise [ArgumentError] if class is not an Entity
11
+ #
11
12
  # @param cls [Class] The Class of the Entity to be created.
12
13
  # @param args [Hash] The entity's properties.
13
- # @return [Gamefic::Entity]
14
+ # @!macro [attach] make_entity
15
+ # @return [$1]
14
16
  def make cls, args = {}, &block
17
+ raise ArgumentError, "Invalid Entity class" unless cls.is_a?(Class) && cls <= Entity
15
18
  ent = cls.new args, &block
16
- if ent.kind_of?(Entity) == false
17
- raise "Invalid entity class"
18
- end
19
- p_entities.push ent
20
- p_dynamic.push ent if running?
19
+ entities.push ent
21
20
  ent
22
21
  end
23
22
 
@@ -34,13 +33,21 @@ module Gamefic
34
33
  ent
35
34
  end
36
35
 
36
+ # Safely remove an entity from a plot.
37
+ #
38
+ # If the entity is dynamic (e.g., created after a plot is already
39
+ # running), it is safe to delete it completely. Otherwise the entity
40
+ # will still be referenced in the entities array, but its parent will be
41
+ # set to nil.
42
+ #
43
+ # @param [Gamefic::Entity] The entity to remove
37
44
  def destroy entity
38
- if p_dynamic.include?(entity)
39
- p_entities.delete entity
40
- p_dynamic.delete entity
41
- p_players.delete entity
42
- end
43
45
  entity.parent = nil
46
+ index = entities.index(entity)
47
+ return if index.nil? || index < static_entity_length - 1
48
+ entities.delete_at index
49
+ players.delete entity
50
+ entity.destroy
44
51
  end
45
52
 
46
53
  # Pick an entity based on its description.
@@ -49,7 +56,7 @@ module Gamefic
49
56
  #
50
57
  # @example Select the Entity that matches the description
51
58
  # red_chair = make Entity, :name => 'red chair'
52
- # blue_chair make Entity, :name => 'blue chair'
59
+ # blue_chair = make Entity, :name => 'blue chair'
53
60
  # pick "red chair" #=> red_chair
54
61
  # pick "blue chair" #=> blue_chair
55
62
  # pick "chair" #=> IndexError: description is ambiguous
@@ -57,8 +64,7 @@ module Gamefic
57
64
  # @param description [String] The description of the entity
58
65
  # @return [Gamefic::Entity] The entity that matches the description
59
66
  def pick(description)
60
- query = Gamefic::Query::Base.new
61
- result = query.match(description, entities)
67
+ result = Query::Matches.execute(entities, description)
62
68
  if result.objects.length == 0
63
69
  raise IndexError.new("Unable to find entity from '#{description}'")
64
70
  elsif result.objects.length > 1
@@ -69,32 +75,27 @@ module Gamefic
69
75
 
70
76
  # Get an array of entities associated with this plot.
71
77
  #
72
- # @return [Array<Entity>]
78
+ # @return [Array<Gamefic::Entity>]
73
79
  def entities
74
- p_entities.clone
80
+ @entities ||= []
75
81
  end
76
82
 
77
83
  # Get an array of players associated with this plot.
78
84
  #
79
- # @return [Array<Character>]
85
+ # @return [Array<Gamefic::Actor>]
80
86
  def players
81
- p_players.clone
87
+ @players ||= []
82
88
  end
83
89
 
84
90
  private
85
91
 
86
- def p_entities
87
- @p_entities ||= []
88
- end
89
-
90
- def p_players
91
- @p_players ||= []
92
+ def mark_static_entities
93
+ @static_entity_length ||= entities.length
92
94
  end
93
95
 
94
- def p_dynamic
95
- @p_dynamic ||= []
96
+ def static_entity_length
97
+ @static_entity_length || 0
96
98
  end
97
99
  end
98
100
  end
99
-
100
101
  end