gamefic 1.6.0 → 2.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +16 -0
  5. data/.solargraph.yml +5 -0
  6. data/CHANGELOG.md +6 -0
  7. data/Gemfile +7 -0
  8. data/LICENSE +20 -0
  9. data/README.md +28 -0
  10. data/Rakefile +10 -0
  11. data/gamefic.gemspec +27 -0
  12. data/lib/gamefic.rb +11 -8
  13. data/lib/gamefic/action.rb +68 -58
  14. data/lib/gamefic/active.rb +331 -0
  15. data/lib/gamefic/actor.rb +8 -0
  16. data/lib/gamefic/command.rb +9 -7
  17. data/lib/gamefic/core_ext/array.rb +27 -49
  18. data/lib/gamefic/core_ext/string.rb +25 -16
  19. data/lib/gamefic/describable.rb +37 -22
  20. data/lib/gamefic/element.rb +47 -0
  21. data/lib/gamefic/entity.rb +24 -48
  22. data/lib/gamefic/{matchable.rb → keywords.rb} +52 -50
  23. data/lib/gamefic/messaging.rb +43 -45
  24. data/lib/gamefic/node.rb +14 -5
  25. data/lib/gamefic/plot.rb +73 -85
  26. data/lib/gamefic/plot/darkroom.rb +80 -0
  27. data/lib/gamefic/plot/host.rb +42 -46
  28. data/lib/gamefic/plot/snapshot.rb +14 -214
  29. data/lib/gamefic/query.rb +15 -17
  30. data/lib/gamefic/query/base.rb +51 -42
  31. data/lib/gamefic/query/children.rb +0 -0
  32. data/lib/gamefic/query/descendants.rb +2 -2
  33. data/lib/gamefic/query/external.rb +18 -0
  34. data/lib/gamefic/query/family.rb +3 -7
  35. data/lib/gamefic/query/matches.rb +75 -67
  36. data/lib/gamefic/query/parent.rb +0 -0
  37. data/lib/gamefic/query/siblings.rb +0 -0
  38. data/lib/gamefic/query/text.rb +12 -12
  39. data/lib/gamefic/query/tree.rb +17 -0
  40. data/lib/gamefic/scene.rb +1 -5
  41. data/lib/gamefic/scene/{active.rb → activity.rb} +4 -6
  42. data/lib/gamefic/scene/base.rb +77 -13
  43. data/lib/gamefic/scene/conclusion.rb +0 -2
  44. data/lib/gamefic/scene/custom.rb +0 -2
  45. data/lib/gamefic/scene/multiple_choice.rb +18 -16
  46. data/lib/gamefic/scene/multiple_scene.rb +29 -20
  47. data/lib/gamefic/scene/pause.rb +7 -2
  48. data/lib/gamefic/scene/yes_or_no.rb +21 -9
  49. data/lib/gamefic/scriptable.rb +88 -0
  50. data/lib/gamefic/serialize.rb +223 -0
  51. data/lib/gamefic/subplot.rb +47 -51
  52. data/lib/gamefic/syntax.rb +15 -13
  53. data/lib/gamefic/version.rb +3 -3
  54. data/lib/gamefic/world.rb +18 -0
  55. data/lib/gamefic/world/callbacks.rb +135 -0
  56. data/lib/gamefic/world/commands.rb +184 -0
  57. data/lib/gamefic/world/entities.rb +98 -0
  58. data/lib/gamefic/{plot → world}/playbook.rb +245 -236
  59. data/lib/gamefic/world/players.rb +37 -0
  60. data/lib/gamefic/world/scenes.rb +226 -0
  61. metadata +40 -108
  62. data/bin/gamefic +0 -9
  63. data/lib/gamefic/character.rb +0 -232
  64. data/lib/gamefic/character/state.rb +0 -12
  65. data/lib/gamefic/engine.rb +0 -7
  66. data/lib/gamefic/engine/base.rb +0 -66
  67. data/lib/gamefic/engine/tty.rb +0 -24
  68. data/lib/gamefic/grammar.rb +0 -13
  69. data/lib/gamefic/grammar/conjugator.rb +0 -20
  70. data/lib/gamefic/grammar/gender.rb +0 -11
  71. data/lib/gamefic/grammar/person.rb +0 -10
  72. data/lib/gamefic/grammar/plural.rb +0 -13
  73. data/lib/gamefic/grammar/pronouns.rb +0 -105
  74. data/lib/gamefic/grammar/tense.rb +0 -6
  75. data/lib/gamefic/grammar/verb_set.rb +0 -43
  76. data/lib/gamefic/grammar/verbs.rb +0 -26
  77. data/lib/gamefic/grammar/word_adapter.rb +0 -49
  78. data/lib/gamefic/plot/articles.rb +0 -22
  79. data/lib/gamefic/plot/callbacks.rb +0 -127
  80. data/lib/gamefic/plot/commands.rb +0 -121
  81. data/lib/gamefic/plot/entities.rb +0 -88
  82. data/lib/gamefic/plot/players.rb +0 -15
  83. data/lib/gamefic/plot/scenes.rb +0 -149
  84. data/lib/gamefic/plot/theater.rb +0 -73
  85. data/lib/gamefic/plot/you_mount.rb +0 -22
  86. data/lib/gamefic/script.rb +0 -13
  87. data/lib/gamefic/script/base.rb +0 -42
  88. data/lib/gamefic/script/file.rb +0 -14
  89. data/lib/gamefic/script/text.rb +0 -14
  90. data/lib/gamefic/shell.rb +0 -76
  91. data/lib/gamefic/source.rb +0 -14
  92. data/lib/gamefic/source/base.rb +0 -12
  93. data/lib/gamefic/source/file.rb +0 -23
  94. data/lib/gamefic/source/text.rb +0 -16
  95. data/lib/gamefic/tester.rb +0 -19
  96. data/lib/gamefic/text.rb +0 -8
  97. data/lib/gamefic/text/ansi.rb +0 -53
  98. data/lib/gamefic/text/html.rb +0 -68
  99. data/lib/gamefic/text/html/conversions.rb +0 -250
  100. data/lib/gamefic/text/html/entities.rb +0 -9
  101. data/lib/gamefic/tty.rb +0 -10
  102. data/lib/gamefic/user.rb +0 -8
  103. data/lib/gamefic/user/base.rb +0 -15
  104. data/lib/gamefic/user/buffer.rb +0 -32
  105. data/lib/gamefic/user/tty.rb +0 -54
@@ -0,0 +1,98 @@
1
+ module Gamefic
2
+ module World
3
+ module Entities
4
+ # Make a new Entity with the provided properties.
5
+ #
6
+ # @example Create an Entity
7
+ # chair = make Entity, name: 'red chair'
8
+ # chair.name #=> 'red chair'
9
+ #
10
+ # @raise [ArgumentError] if class is not an Entity
11
+ #
12
+ # @param cls [Class] The Class of the Entity to be created.
13
+ # @param args [Hash] The entity's properties.
14
+ # @!macro [attach] make_entity
15
+ # @return [$1]
16
+ def make cls, args = {}, &block
17
+ raise ArgumentError, "Invalid Entity class" unless cls.is_a?(Class) && cls <= Entity
18
+ ent = cls.new args, &block
19
+ entities.push ent
20
+ ent
21
+ end
22
+
23
+ # Cast an active entity.
24
+ # This method is similar to make, but it also provides the plot's
25
+ # playbook to the entity so it can perform actions. The entity should
26
+ # either be a kind of Gamefic::Actor or include the Gamefic::Active
27
+ # module.
28
+ #
29
+ # @return [Gamefic::Actor, Gamefic::Active]
30
+ def cast cls, args = {}, &block
31
+ ent = make cls, args, &block
32
+ ent.playbooks.push playbook
33
+ ent
34
+ end
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
44
+ def destroy entity
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
51
+
52
+ # @todo It might make sense to destroy the entity completely now. It
53
+ # will still have a reference in the index, but that shouldn't impact
54
+ # the current state of the plot.
55
+ return if static.include?(entity)
56
+ entities.delete entity
57
+ players.delete entity
58
+ end
59
+
60
+ # Pick an entity based on its description.
61
+ # The description provided must match exactly one entity; otherwise
62
+ # an error is raised.
63
+ #
64
+ # @example Select the Entity that matches the description
65
+ # red_chair = make Entity, :name => 'red chair'
66
+ # blue_chair = make Entity, :name => 'blue chair'
67
+ # pick "red chair" #=> red_chair
68
+ # pick "blue chair" #=> blue_chair
69
+ # pick "chair" #=> IndexError: description is ambiguous
70
+ #
71
+ # @param description [String] The description of the entity
72
+ # @return [Gamefic::Entity] The entity that matches the description
73
+ def pick(description)
74
+ result = Query::Matches.execute(entities, description)
75
+ if result.objects.length == 0
76
+ raise IndexError.new("Unable to find entity from '#{description}'")
77
+ elsif result.objects.length > 1
78
+ raise IndexError.new("Ambiguous entities found from '#{description}'")
79
+ end
80
+ result.objects[0]
81
+ end
82
+
83
+ # Get an array of entities associated with this plot.
84
+ #
85
+ # @return [Array<Gamefic::Entity>]
86
+ def entities
87
+ @entities ||= []
88
+ end
89
+
90
+ # Get an array of players associated with this plot.
91
+ #
92
+ # @return [Array<Gamefic::Actor>]
93
+ def players
94
+ @players ||= []
95
+ end
96
+ end
97
+ end
98
+ end
@@ -1,236 +1,245 @@
1
- module Gamefic
2
-
3
- class Plot
4
- class Playbook
5
- def initialize commands: {}, syntaxes: [], validators: [], disambiguator: nil
6
- @commands = commands
7
- @syntaxes = syntaxes
8
- @validators = validators
9
- @disambiguator = disambiguator
10
- end
11
-
12
- def syntaxes
13
- @syntaxes
14
- end
15
-
16
- def actions
17
- @commands.values.flatten
18
- end
19
-
20
- def verbs
21
- @commands.keys
22
- end
23
-
24
- def validators
25
- @validators
26
- end
27
-
28
- def disambiguator
29
- @disambiguator ||= Action.new(nil, Query::Base.new) do |actor, entities|
30
- definites = []
31
- entities.each { |entity|
32
- definites.push entity.definitely
33
- }
34
- actor.tell "I don't know which you mean: #{definites.join_or}."
35
- end
36
- end
37
-
38
- #def disambiguate &block
39
- # @disambiguator = Action.new(nil, Query::Base.new, &block)
40
- # @disambiguator.meta = true
41
- # @disambiguator
42
- #end
43
-
44
- def validate &block
45
- @validators.push block
46
- end
47
-
48
- # Get an Array of all Actions associated with the specified verb.
49
- #
50
- # @param verb [Symbol] The Symbol for the verb (e.g., :go or :look)
51
- # @return [Array<Action>] The verb's associated Actions
52
- def actions_for verb
53
- @commands[verb] || []
54
- end
55
-
56
- # Create an Action that responds to a command.
57
- # An Action uses the command argument to identify the imperative verb that
58
- # triggers the action.
59
- # It can also accept queries to tokenize the remainder of the input and
60
- # filter for particular entities or properties.
61
- # The block argument contains the code to be executed when the input
62
- # matches all of the Action's criteria (i.e., verb and queries).
63
- #
64
- # @example A simple Action.
65
- # respond :salute do |actor|
66
- # actor.tell "Hello, sir!"
67
- # end
68
- # # The command "salute" will respond "Hello, sir!"
69
- #
70
- # @example An Action that accepts a Character
71
- # respond :salute, Use.visible(Character) do |actor, character|
72
- # actor.tell "#{The character} returns your salute."
73
- # end
74
- #
75
- # @param verb [Symbol] An imperative verb for the command
76
- # @param queries [Array<Query::Base>] Filters for the command's tokens
77
- # @yieldparam [Gamefic::Character]
78
- # @return [Gamefic::Action]
79
- def respond(verb, *queries, &proc)
80
- act = Action.subclass verb, *queries, order_key: raise_order_key, &proc
81
- add_action act
82
- act
83
- end
84
-
85
- # Create a Meta Action that responds to a command.
86
- # Meta Actions are very similar to standard Actions, except the Plot
87
- # understands them to be commands that operate above and/or outside of the
88
- # actual game world. Examples of Meta Actions are commands that report the
89
- # player's current score, save and restore saved games, or list the game's
90
- # credits.
91
- #
92
- # @example A simple Meta Action
93
- # meta :credits do |actor|
94
- # actor.tell "This game was written by John Smith."
95
- # end
96
- #
97
- # @param verb [Symbol] An imperative verb for the command
98
- # @param queries [Array<Query::Base>] Filters for the command's tokens
99
- # @yieldparam [Gamefic::Character]
100
- def meta(verb, *queries, &proc)
101
- act = Action.subclass verb, *queries, meta: true, &proc
102
- add_action act
103
- act
104
- end
105
-
106
- # Create an alternate Syntax for an Action.
107
- # The command and its translation can be parameterized.
108
- #
109
- # @example Create a synonym for the Inventory Action.
110
- # interpret "catalogue", "inventory"
111
- # # The command "catalogue" will be translated to "inventory"
112
- #
113
- # @example Create a parameterized synonym for the Look Action.
114
- # interpret "scrutinize :entity", "look :entity"
115
- # # The command "scrutinize chair" will be translated to "look chair"
116
- #
117
- # @param input [String] The format of the original command
118
- # @param translation [String] The format of the translated command
119
- # @return [Syntax] the Syntax object
120
- def interpret(input, translation)
121
- syn = Syntax.new(input, translation)
122
- add_syntax syn
123
- syn
124
- end
125
-
126
- def dispatch(actor, *command)
127
- result = []
128
- if command.length > 1
129
- result.concat dispatch_from_params(actor, command[0], command[1..-1])
130
- end
131
- if result.empty?
132
- result.concat dispatch_from_string(actor, command.join(' '))
133
- end
134
- result.sort! { |a,b|
135
- if a.rank == b.rank
136
- b.order_key <=> a.order_key
137
- else
138
- b.rank <=> a.rank
139
- end
140
- }
141
- result.uniq{|a| a.class}
142
- end
143
-
144
- def dispatch_from_string actor, text
145
- result = []
146
- commands = Syntax.tokenize(text, syntaxes)
147
- commands.each { |c|
148
- available = actions_for(c.verb)
149
- available.each { |a|
150
- o = a.attempt(actor, c.arguments)
151
- result.unshift o unless o.nil?
152
- }
153
- }
154
- result
155
- end
156
-
157
- def dispatch_from_params actor, verb, params
158
- result = []
159
- available = actions_for(verb)
160
- available.each { |a|
161
- result.unshift a.new(actor, params) if a.valid?(actor, params)
162
- }
163
- result
164
- end
165
-
166
- # Duplicate the playbook.
167
- # This method will duplicate the commands hash and the syntax array so
168
- # the new playbook can be modified without affecting the original.
169
- #
170
- # @return [Playbook]
171
- def dup
172
- Playbook.new commands: @commands.dup, syntaxes: @syntaxes.dup
173
- end
174
-
175
- def freeze
176
- @commands.freeze
177
- @syntaxes.freeze
178
- end
179
-
180
- private
181
-
182
- def add_action(action)
183
- @commands[action.verb] ||= []
184
- @commands[action.verb].push action
185
- #@commands[action.verb].uniq!
186
- #@commands[action.verb].sort! { |a, b|
187
- # b.rank <=> a.rank
188
- #}
189
- generate_default_syntax action
190
- end
191
-
192
- def generate_default_syntax action
193
- user_friendly = action.verb.to_s.gsub(/_/, ' ')
194
- args = []
195
- used_names = []
196
- action.queries.each { |c|
197
- num = 1
198
- new_name = ":var"
199
- while used_names.include? new_name
200
- num = num + 1
201
- new_name = ":var#{num}"
202
- end
203
- used_names.push new_name
204
- user_friendly += " #{new_name}"
205
- args.push new_name
206
- }
207
- add_syntax Syntax.new(user_friendly.strip, "#{action.verb} #{args.join(' ')}")
208
- end
209
-
210
- def add_syntax syntax
211
- if @commands[syntax.verb] == nil
212
- raise "No actions exist for \"#{syntax.verb}\""
213
- end
214
- @syntaxes.unshift syntax
215
- @syntaxes.uniq!
216
- @syntaxes.sort! { |a, b|
217
- if a.token_count == b.token_count
218
- # For syntaxes of the same length, length of action takes precedence
219
- b.first_word <=> a.first_word
220
- else
221
- b.token_count <=> a.token_count
222
- end
223
- }
224
- end
225
-
226
- def raise_order_key
227
- @order_key ||= 0
228
- tmp = @order_key
229
- @order_key += 1
230
- tmp
231
- end
232
-
233
- end
234
- end
235
-
236
- end
1
+ module Gamefic
2
+ module World
3
+ # A collection of rules for performing commands.
4
+ #
5
+ class Playbook
6
+ # An array of available syntaxes.
7
+ #
8
+ # @return [Array<Gamefic::Syntax>]
9
+ attr_reader :syntaxes
10
+
11
+ # An array of defined validators.
12
+ #
13
+ # @return [Array<Proc>]
14
+ attr_reader :validators
15
+
16
+ def initialize commands: {}, syntaxes: [], validators: [], disambiguator: nil
17
+ @commands = commands
18
+ @syntaxes = syntaxes
19
+ @validators = validators
20
+ @disambiguator = disambiguator
21
+ end
22
+
23
+ # An array of available actions.
24
+ #
25
+ # @return [Array<Gamefic::Action>]
26
+ def actions
27
+ @commands.values.flatten
28
+ end
29
+
30
+ # An array of recognized verbs.
31
+ #
32
+ # @return [Array<Symbol>]
33
+ def verbs
34
+ @commands.keys
35
+ end
36
+
37
+ # Get the action for handling ambiguous entity references.
38
+ #
39
+ def disambiguator
40
+ @disambiguator ||= Action.subclass(nil, Query::Base.new) do |actor, entities|
41
+ definites = []
42
+ entities.each do |entity|
43
+ definites.push entity.definitely
44
+ end
45
+ actor.tell "I don't know which you mean: #{definites.join_or}."
46
+ end
47
+ end
48
+
49
+ # Set the action for handling ambiguous entity references.
50
+ #
51
+ def disambiguate &block
52
+ @disambiguator = Action.subclass(nil, Query::Base.new, meta: true, &block)
53
+ @disambiguator
54
+ end
55
+
56
+ # Add a block that determines whether an action can be executed.
57
+ #
58
+ def validate &block
59
+ @validators.push block
60
+ end
61
+
62
+ # Get an Array of all Actions associated with the specified verb.
63
+ #
64
+ # @param verb [Symbol] The Symbol for the verb (e.g., :go or :look)
65
+ # @return [Array<Class<Action>>] The verb's associated Actions
66
+ def actions_for verb
67
+ @commands[verb] || []
68
+ end
69
+
70
+ # Create an Action that responds to a command.
71
+ # An Action uses the command argument to identify the imperative verb that
72
+ # triggers the action.
73
+ # It can also accept queries to tokenize the remainder of the input and
74
+ # filter for particular entities or properties.
75
+ # The block argument contains the code to be executed when the input
76
+ # matches all of the Action's criteria (i.e., verb and queries).
77
+ #
78
+ # @example A simple Action.
79
+ # respond :salute do |actor|
80
+ # actor.tell "Hello, sir!"
81
+ # end
82
+ # # The command "salute" will respond "Hello, sir!"
83
+ #
84
+ # @example An Action that accepts a Character
85
+ # respond :salute, Use.visible(Character) do |actor, character|
86
+ # actor.tell "#{The character} returns your salute."
87
+ # end
88
+ #
89
+ # @param verb [Symbol] An imperative verb for the command
90
+ # @param queries [Array<Query::Base>] Filters for the command's tokens
91
+ # @yieldparam [Gamefic::Actor]
92
+ # @return [Class<Gamefic::Action>]
93
+ def respond(verb, *queries, &proc)
94
+ act = Action.subclass verb, *queries, &proc
95
+ add_action act
96
+ act
97
+ end
98
+
99
+ # Create a Meta Action that responds to a command.
100
+ # Meta Actions are very similar to standard Actions, except the Plot
101
+ # understands them to be commands that operate above and/or outside of the
102
+ # actual game world. Examples of Meta Actions are commands that report the
103
+ # player's current score, save and restore saved games, or list the game's
104
+ # credits.
105
+ #
106
+ # @example A simple Meta Action
107
+ # meta :credits do |actor|
108
+ # actor.tell "This game was written by John Smith."
109
+ # end
110
+ #
111
+ # @param verb [Symbol] An imperative verb for the command
112
+ # @param queries [Array<Query::Base>] Filters for the command's tokens
113
+ # @yieldparam [Gamefic::Actor]
114
+ # @return [Class<Gamefic::Action>]
115
+ def meta(verb, *queries, &proc)
116
+ act = Action.subclass verb, *queries, meta: true, &proc
117
+ add_action act
118
+ act
119
+ end
120
+
121
+ # Create an alternate Syntax for an Action.
122
+ # The command and its translation can be parameterized.
123
+ #
124
+ # @example Create a synonym for the Inventory Action.
125
+ # interpret "catalogue", "inventory"
126
+ # # The command "catalogue" will be translated to "inventory"
127
+ #
128
+ # @example Create a parameterized synonym for the Look Action.
129
+ # interpret "scrutinize :entity", "look :entity"
130
+ # # The command "scrutinize chair" will be translated to "look chair"
131
+ #
132
+ # @param input [String] The format of the original command
133
+ # @param translation [String] The format of the translated command
134
+ # @return [Syntax] the Syntax object
135
+ def interpret(input, translation)
136
+ syn = Syntax.new(input, translation)
137
+ add_syntax syn
138
+ syn
139
+ end
140
+
141
+ # Get an array of actions, derived from the specified command, that the
142
+ # actor can potentially execute.
143
+ # The command can either be a single string (e.g., "examine book") or a
144
+ # list of tokens (e.g., :examine, @book).
145
+ #
146
+ # @return [Array<Gamefic::Action>]
147
+ def dispatch(actor, *command)
148
+ result = []
149
+ result.concat dispatch_from_params(actor, command[0], command[1..-1]) if command.length > 1
150
+ result.concat dispatch_from_string(actor, command.join(' ')) if result.empty?
151
+ result
152
+ end
153
+
154
+ # Get an array of actions, derived from the specified command, that the
155
+ # actor can potentially execute.
156
+ # The command should be a plain-text string, e.g., "examine the book."
157
+ #
158
+ # @return [Array<Gamefic::Action>]
159
+ def dispatch_from_string actor, text
160
+ result = []
161
+ commands = Syntax.tokenize(text, actor.syntaxes)
162
+ commands.each do |c|
163
+ actions_for(c.verb).each do |a|
164
+ next if a.hidden?
165
+ o = a.attempt(actor, c.arguments)
166
+ result.unshift o unless o.nil?
167
+ end
168
+ end
169
+ sort_and_reduce_actions result
170
+ end
171
+
172
+ # Get an array of actions, derived from the specified verb and params,
173
+ # that the actor can potentially execute.
174
+ #
175
+ # @return [Array<Gamefic::Action>]
176
+ def dispatch_from_params actor, verb, params
177
+ result = []
178
+ available = actions_for(verb)
179
+ available.each do |a|
180
+ result.unshift a.new(actor, params) if a.valid?(actor, params)
181
+ end
182
+ sort_and_reduce_actions result
183
+ end
184
+
185
+ # Duplicate the playbook.
186
+ # This method will duplicate the commands hash and the syntax array so
187
+ # the new playbook can be modified without affecting the original.
188
+ #
189
+ # @return [Playbook]
190
+ def dup
191
+ Playbook.new commands: @commands.dup, syntaxes: @syntaxes.dup
192
+ end
193
+
194
+ def freeze
195
+ @commands.freeze
196
+ @syntaxes.freeze
197
+ end
198
+
199
+ private
200
+
201
+ def add_action(action)
202
+ @commands[action.verb] ||= []
203
+ @commands[action.verb].push action
204
+ generate_default_syntax action
205
+ end
206
+
207
+ def generate_default_syntax action
208
+ user_friendly = action.verb.to_s.gsub(/_/, ' ')
209
+ args = []
210
+ used_names = []
211
+ action.queries.each do |_c|
212
+ num = 1
213
+ new_name = ":var"
214
+ while used_names.include? new_name
215
+ num += 1
216
+ new_name = ":var#{num}"
217
+ end
218
+ used_names.push new_name
219
+ user_friendly += " #{new_name}"
220
+ args.push new_name
221
+ end
222
+ add_syntax Syntax.new(user_friendly.strip, "#{action.verb} #{args.join(' ')}") unless action.verb.to_s.start_with?('_')
223
+ end
224
+
225
+ def add_syntax syntax
226
+ raise "No actions exist for \"#{syntax.verb}\"" if @commands[syntax.verb].nil?
227
+
228
+ @syntaxes.unshift syntax
229
+ @syntaxes.uniq!(&:signature)
230
+ @syntaxes.sort! do |a, b|
231
+ if a.token_count == b.token_count
232
+ # For syntaxes of the same length, length of action takes precedence
233
+ b.first_word <=> a.first_word
234
+ else
235
+ b.token_count <=> a.token_count
236
+ end
237
+ end
238
+ end
239
+
240
+ def sort_and_reduce_actions arr
241
+ arr.sort_by.with_index { |a, i| [a.rank, -i]}.reverse.uniq(&:class)
242
+ end
243
+ end
244
+ end
245
+ end