gamefic 3.3.0 → 3.5.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/CHANGELOG.md +20 -0
- data/README.md +1 -1
- data/lib/gamefic/action.rb +4 -4
- data/lib/gamefic/active/epic.rb +1 -0
- data/lib/gamefic/active.rb +4 -0
- data/lib/gamefic/callback.rb +16 -0
- data/lib/gamefic/chapter.rb +71 -0
- data/lib/gamefic/command.rb +40 -1
- data/lib/gamefic/dispatcher.rb +5 -31
- data/lib/gamefic/entity.rb +26 -0
- data/lib/gamefic/narrative.rb +30 -8
- data/lib/gamefic/plot.rb +28 -1
- data/lib/gamefic/proxy.rb +46 -0
- data/lib/gamefic/query/base.rb +28 -12
- data/lib/gamefic/query/general.rb +3 -12
- data/lib/gamefic/query/result.rb +4 -1
- data/lib/gamefic/query/scoped.rb +1 -18
- data/lib/gamefic/query/text.rb +13 -12
- data/lib/gamefic/response.rb +66 -38
- data/lib/gamefic/rulebook/calls.rb +0 -4
- data/lib/gamefic/rulebook/events.rb +10 -24
- data/lib/gamefic/rulebook/hooks.rb +10 -10
- data/lib/gamefic/rulebook.rb +8 -22
- data/lib/gamefic/scanner/base.rb +44 -0
- data/lib/gamefic/scanner/fuzzy.rb +17 -0
- data/lib/gamefic/scanner/fuzzy_nesting.rb +14 -0
- data/lib/gamefic/scanner/nesting.rb +39 -0
- data/lib/gamefic/scanner/result.rb +50 -0
- data/lib/gamefic/scanner/strict.rb +31 -0
- data/lib/gamefic/scanner.rb +33 -111
- data/lib/gamefic/scope/descendants.rb +16 -0
- data/lib/gamefic/scope/family.rb +31 -8
- data/lib/gamefic/scope.rb +1 -0
- data/lib/gamefic/scriptable/actions.rb +8 -27
- data/lib/gamefic/scriptable/entities.rb +4 -1
- data/lib/gamefic/scriptable/events.rb +13 -7
- data/lib/gamefic/scriptable/plot_proxies.rb +16 -0
- data/lib/gamefic/scriptable/proxies.rb +31 -0
- data/lib/gamefic/scriptable/queries.rb +15 -7
- data/lib/gamefic/scriptable/scenes.rb +10 -4
- data/lib/gamefic/scriptable.rb +73 -42
- data/lib/gamefic/stage.rb +2 -2
- data/lib/gamefic/subplot.rb +31 -7
- data/lib/gamefic/version.rb +1 -1
- data/lib/gamefic.rb +3 -1
- metadata +14 -4
- data/lib/gamefic/composer.rb +0 -70
- data/lib/gamefic/scriptable/proxy.rb +0 -69
data/lib/gamefic/query/text.rb
CHANGED
@@ -4,16 +4,20 @@ module Gamefic
|
|
4
4
|
module Query
|
5
5
|
# A special query that handles text instead of entities.
|
6
6
|
#
|
7
|
-
class Text
|
7
|
+
class Text < Base
|
8
8
|
# @param argument [String, Regexp]
|
9
9
|
def initialize argument = /.*/
|
10
|
-
|
10
|
+
super
|
11
11
|
validate
|
12
12
|
end
|
13
13
|
|
14
|
+
def argument
|
15
|
+
arguments.first
|
16
|
+
end
|
17
|
+
|
14
18
|
# @return [String, Regexp]
|
15
19
|
def select(_subject)
|
16
|
-
|
20
|
+
argument
|
17
21
|
end
|
18
22
|
|
19
23
|
def query _subject, token
|
@@ -23,6 +27,7 @@ module Gamefic
|
|
23
27
|
Result.new(nil, token)
|
24
28
|
end
|
25
29
|
end
|
30
|
+
alias filter query
|
26
31
|
|
27
32
|
def precision
|
28
33
|
0
|
@@ -32,25 +37,21 @@ module Gamefic
|
|
32
37
|
match? argument
|
33
38
|
end
|
34
39
|
|
35
|
-
def ambiguous?
|
36
|
-
true
|
37
|
-
end
|
38
|
-
|
39
40
|
private
|
40
41
|
|
41
42
|
def match? token
|
42
|
-
return false unless token.is_a?(String)
|
43
|
+
return false unless token.is_a?(String) && !token.empty?
|
43
44
|
|
44
|
-
case
|
45
|
+
case argument
|
45
46
|
when Regexp
|
46
|
-
token =~
|
47
|
+
token =~ argument
|
47
48
|
else
|
48
|
-
token ==
|
49
|
+
token == argument
|
49
50
|
end
|
50
51
|
end
|
51
52
|
|
52
53
|
def validate
|
53
|
-
return if
|
54
|
+
return if argument.is_a?(String) || argument.is_a?(Regexp)
|
54
55
|
|
55
56
|
raise ArgumentError, 'Invalid text query argument'
|
56
57
|
end
|
data/lib/gamefic/response.rb
CHANGED
@@ -8,23 +8,19 @@ module Gamefic
|
|
8
8
|
# @return [Symbol]
|
9
9
|
attr_reader :verb
|
10
10
|
|
11
|
-
# @return [Array<Query::Base>]
|
11
|
+
# @return [Array<Query::Base, Query::Text>]
|
12
12
|
attr_reader :queries
|
13
13
|
|
14
|
-
# @return [Narrative]
|
15
|
-
attr_reader :narrative
|
16
|
-
|
17
14
|
# @param verb [Symbol]
|
18
15
|
# @param narrative [Narrative]
|
19
|
-
# @param
|
16
|
+
# @param args [Array<Object>]
|
20
17
|
# @param meta [Boolean]
|
21
18
|
# @param block [Proc]
|
22
|
-
def initialize verb, narrative, *
|
19
|
+
def initialize verb, narrative, *args, meta: false, &block
|
23
20
|
@verb = verb
|
24
|
-
@
|
25
|
-
@queries = map_queryable_objects(queries)
|
21
|
+
@queries = map_queries(args, narrative)
|
26
22
|
@meta = meta
|
27
|
-
@
|
23
|
+
@callback = Callback.new(narrative, block)
|
28
24
|
end
|
29
25
|
|
30
26
|
# The `meta?` flag is just a way for authors to identify responses that
|
@@ -60,53 +56,85 @@ module Gamefic
|
|
60
56
|
# @param actor [Active]
|
61
57
|
# @param command [Command]
|
62
58
|
def accept? actor, command
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
return false unless query.accept?(actor, command.arguments[idx])
|
67
|
-
end
|
68
|
-
|
69
|
-
true
|
59
|
+
command.verb == verb &&
|
60
|
+
command.arguments.length == queries.length &&
|
61
|
+
queries.zip(command.arguments).all? { |query, argument| query.accept?(actor, argument) }
|
70
62
|
end
|
71
63
|
|
72
64
|
def execute *args
|
73
|
-
|
65
|
+
@callback.run(*args)
|
74
66
|
end
|
75
67
|
|
76
68
|
def precision
|
77
69
|
@precision ||= calculate_precision
|
78
70
|
end
|
79
71
|
|
72
|
+
# Turn an actor and an expression into a command by matching the
|
73
|
+
# expression's tokens to queries. Return nil if the expression
|
74
|
+
# could not be matched.
|
75
|
+
#
|
76
|
+
# @param actor [Actor]
|
77
|
+
# @param expression [Expression]
|
78
|
+
# @return [Command, nil]
|
79
|
+
def to_command actor, expression
|
80
|
+
return nil unless expression.verb == verb && expression.tokens.length <= queries.length
|
81
|
+
|
82
|
+
results = filter(actor, expression)
|
83
|
+
return nil unless results
|
84
|
+
|
85
|
+
Command.new(
|
86
|
+
verb,
|
87
|
+
results.map(&:match),
|
88
|
+
results.sum(&:strictness),
|
89
|
+
precision
|
90
|
+
)
|
91
|
+
end
|
92
|
+
|
80
93
|
private
|
81
94
|
|
95
|
+
def filter actor, expression
|
96
|
+
remainder = ''
|
97
|
+
result = queries.zip(expression.tokens)
|
98
|
+
.map do |query, token|
|
99
|
+
token = "#{remainder} #{token}".strip
|
100
|
+
result = query.filter(actor, token)
|
101
|
+
return nil unless result.match
|
102
|
+
|
103
|
+
remainder = result.remainder
|
104
|
+
result
|
105
|
+
end
|
106
|
+
result if remainder.empty?
|
107
|
+
end
|
108
|
+
|
82
109
|
def generate_default_syntax
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
queries.each do |_c|
|
87
|
-
num = 1
|
88
|
-
new_name = ":var"
|
89
|
-
while used_names.include? new_name
|
90
|
-
num += 1
|
91
|
-
new_name = ":var#{num}"
|
92
|
-
end
|
93
|
-
used_names.push new_name
|
94
|
-
user_friendly += " #{new_name}"
|
95
|
-
args.push new_name
|
96
|
-
end
|
97
|
-
Syntax.new(user_friendly.strip, "#{verb} #{args.join(' ')}".strip)
|
110
|
+
args = queries.length.times.map { |num| num.zero? ? ':var' : ":var#{num + 1}" }
|
111
|
+
tmpl = "#{verb} #{args.join(' ')}".strip
|
112
|
+
Syntax.new(tmpl.gsub('_', ' '), tmpl)
|
98
113
|
end
|
99
114
|
|
100
115
|
def calculate_precision
|
101
|
-
total =
|
102
|
-
|
103
|
-
total -= 1000 if verb.nil?
|
116
|
+
total = queries.sum(&:precision)
|
117
|
+
total -= 1000 unless verb
|
104
118
|
total
|
105
119
|
end
|
106
120
|
|
107
|
-
def
|
108
|
-
|
109
|
-
|
121
|
+
def map_queries args, narrative
|
122
|
+
args.map do |arg|
|
123
|
+
select_query(arg, narrative).tap { |qry| qry.narrative = narrative }
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def select_query arg, narrative
|
128
|
+
case arg
|
129
|
+
when Entity, Class, Module, Proc, Proxy
|
130
|
+
narrative.available(arg)
|
131
|
+
when String, Regexp
|
132
|
+
narrative.plaintext(arg)
|
133
|
+
when Query::Base, Query::Text
|
134
|
+
arg
|
135
|
+
else
|
136
|
+
raise ArgumentError, "invalid argument in response: #{arg.inspect}"
|
137
|
+
end
|
110
138
|
end
|
111
139
|
end
|
112
140
|
end
|
@@ -35,44 +35,30 @@ module Gamefic
|
|
35
35
|
end
|
36
36
|
|
37
37
|
# @return [void]
|
38
|
-
def on_ready
|
39
|
-
@ready_blocks.push
|
38
|
+
def on_ready callback
|
39
|
+
@ready_blocks.push callback
|
40
40
|
end
|
41
41
|
|
42
|
-
|
43
|
-
|
44
|
-
def on_player_ready &block
|
45
|
-
@ready_blocks.push(proc do
|
46
|
-
players.each { |plyr| instance_exec plyr, &block }
|
47
|
-
end)
|
48
|
-
end
|
49
|
-
|
50
|
-
def on_update &block
|
51
|
-
@update_blocks.push block
|
52
|
-
end
|
53
|
-
|
54
|
-
def on_player_update &block
|
55
|
-
@update_blocks.push(proc do
|
56
|
-
players.each { |plyr| instance_exec plyr, &block }
|
57
|
-
end)
|
42
|
+
def on_update callback
|
43
|
+
@update_blocks.push callback
|
58
44
|
end
|
59
45
|
|
60
46
|
# @return [void]
|
61
|
-
def on_conclude
|
62
|
-
@conclude_blocks.push
|
47
|
+
def on_conclude callback
|
48
|
+
@conclude_blocks.push callback
|
63
49
|
end
|
64
50
|
|
65
51
|
# @yieldparam [Actor]
|
66
52
|
# @return [void]
|
67
|
-
def on_player_conclude
|
68
|
-
@player_conclude_blocks.push
|
53
|
+
def on_player_conclude callback
|
54
|
+
@player_conclude_blocks.push callback
|
69
55
|
end
|
70
56
|
|
71
57
|
# @yieldparam [Actor]
|
72
58
|
# @yieldparam [Hash]
|
73
59
|
# @return [void]
|
74
|
-
def on_player_output
|
75
|
-
@player_output_blocks.push
|
60
|
+
def on_player_output callback
|
61
|
+
@player_output_blocks.push callback
|
76
62
|
end
|
77
63
|
end
|
78
64
|
end
|
@@ -21,35 +21,35 @@ module Gamefic
|
|
21
21
|
self
|
22
22
|
end
|
23
23
|
|
24
|
-
def before_action *verbs, &block
|
25
|
-
before_actions.push Action::Hook.new(
|
24
|
+
def before_action narrative, *verbs, &block
|
25
|
+
before_actions.push Action::Hook.new(verbs, Callback.new(narrative, block))
|
26
26
|
end
|
27
27
|
|
28
|
-
def after_action *verbs, &block
|
29
|
-
after_actions.push Action::Hook.new(
|
28
|
+
def after_action narrative, *verbs, &block
|
29
|
+
after_actions.push Action::Hook.new(verbs, Callback.new(narrative, block))
|
30
30
|
end
|
31
31
|
|
32
32
|
def empty?
|
33
33
|
before_actions.empty? && after_actions.empty?
|
34
34
|
end
|
35
35
|
|
36
|
-
def run_before action
|
37
|
-
run_action_hooks action,
|
36
|
+
def run_before action
|
37
|
+
run_action_hooks action, before_actions
|
38
38
|
end
|
39
39
|
|
40
|
-
def run_after action
|
41
|
-
run_action_hooks action,
|
40
|
+
def run_after action
|
41
|
+
run_action_hooks action, after_actions
|
42
42
|
end
|
43
43
|
|
44
44
|
private
|
45
45
|
|
46
|
-
def run_action_hooks action,
|
46
|
+
def run_action_hooks action, hooks
|
47
47
|
hooks.each do |hook|
|
48
48
|
break if action.cancelled?
|
49
49
|
|
50
50
|
next unless hook.match?(action.verb)
|
51
51
|
|
52
|
-
|
52
|
+
hook.callback.run action
|
53
53
|
end
|
54
54
|
end
|
55
55
|
end
|
data/lib/gamefic/rulebook.rb
CHANGED
@@ -25,12 +25,7 @@ module Gamefic
|
|
25
25
|
# @return [Scenes]
|
26
26
|
attr_reader :scenes
|
27
27
|
|
28
|
-
|
29
|
-
attr_reader :narrative
|
30
|
-
|
31
|
-
# @param narrative [Narrative]
|
32
|
-
def initialize(narrative)
|
33
|
-
@narrative = narrative
|
28
|
+
def initialize
|
34
29
|
@calls = Calls.new
|
35
30
|
@events = Events.new
|
36
31
|
@hooks = Hooks.new
|
@@ -96,44 +91,35 @@ module Gamefic
|
|
96
91
|
end
|
97
92
|
|
98
93
|
def run_ready_blocks
|
99
|
-
events.ready_blocks.each
|
94
|
+
events.ready_blocks.each(&:run)
|
100
95
|
end
|
101
96
|
|
102
97
|
def run_update_blocks
|
103
|
-
events.update_blocks.each
|
98
|
+
events.update_blocks.each(&:run)
|
104
99
|
end
|
105
100
|
|
106
101
|
def run_before_actions action
|
107
|
-
hooks.run_before action
|
102
|
+
hooks.run_before action
|
108
103
|
end
|
109
104
|
|
110
105
|
def run_after_actions action
|
111
|
-
hooks.run_after action
|
106
|
+
hooks.run_after action
|
112
107
|
end
|
113
108
|
|
114
109
|
def run_conclude_blocks
|
115
|
-
events.conclude_blocks.each
|
110
|
+
events.conclude_blocks.each(&:run)
|
116
111
|
end
|
117
112
|
|
118
113
|
def run_player_conclude_blocks player
|
119
|
-
events.player_conclude_blocks.each { |blk|
|
114
|
+
events.player_conclude_blocks.each { |blk| blk.run(player) }
|
120
115
|
end
|
121
116
|
|
122
117
|
def run_player_output_blocks player, output
|
123
|
-
events.player_output_blocks.each { |blk|
|
118
|
+
events.player_output_blocks.each { |blk| blk.run(player, output) }
|
124
119
|
end
|
125
120
|
|
126
121
|
def empty?
|
127
122
|
calls.empty? && hooks.empty? && scenes.empty? && events.empty?
|
128
123
|
end
|
129
|
-
|
130
|
-
def script
|
131
|
-
narrative.class.included_blocks.select(&:script?).each { |blk| Stage.run(narrative, &blk.code) }
|
132
|
-
end
|
133
|
-
|
134
|
-
def script_with_defaults
|
135
|
-
script
|
136
|
-
scenes.with_defaults narrative
|
137
|
-
end
|
138
124
|
end
|
139
125
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gamefic
|
4
|
+
module Scanner
|
5
|
+
# A base class for scanners that match tokens to entities.
|
6
|
+
#
|
7
|
+
class Base
|
8
|
+
# @return [Array<Entity>]
|
9
|
+
attr_reader :selection
|
10
|
+
|
11
|
+
# @return [String]
|
12
|
+
attr_reader :token
|
13
|
+
|
14
|
+
# @param selection [Array<Entity>]
|
15
|
+
# @param token [String]
|
16
|
+
def initialize selection, token
|
17
|
+
@selection = selection
|
18
|
+
@token = token
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [Result]
|
22
|
+
def scan
|
23
|
+
unmatched_result
|
24
|
+
end
|
25
|
+
|
26
|
+
# @param selection [Array<Entity>]
|
27
|
+
# @param token [String]
|
28
|
+
# @return [Result]
|
29
|
+
def self.scan selection, token
|
30
|
+
new(selection, token).scan
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def unmatched_result
|
36
|
+
Result.unmatched(selection, token, self.class)
|
37
|
+
end
|
38
|
+
|
39
|
+
def matched_result matched, remainder
|
40
|
+
Result.new(selection, token, matched, remainder, self.class)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gamefic
|
4
|
+
module Scanner
|
5
|
+
# Fuzzy token matching.
|
6
|
+
#
|
7
|
+
# An entity will match a word in a fuzzy scan if it matches the beginning
|
8
|
+
# of one of the entity's keywords, e.g., `pen` is a fuzzy token match for
|
9
|
+
# the keyword `pencil`.
|
10
|
+
#
|
11
|
+
class Fuzzy < Strict
|
12
|
+
def match_word available, word
|
13
|
+
available.select { |obj| obj.keywords.any? { |wrd| wrd.start_with?(word) } }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gamefic
|
4
|
+
module Scanner
|
5
|
+
# Fuzzy scanning for entities inside of other entities, e.g., `soc in draw`
|
6
|
+
# would match `sock in drawer`.
|
7
|
+
#
|
8
|
+
class FuzzyNesting < Nesting
|
9
|
+
def subprocessor
|
10
|
+
Fuzzy
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gamefic
|
4
|
+
module Scanner
|
5
|
+
# Strict scanning for entities inside of other entities, e.g., `sock inside drawer`.
|
6
|
+
#
|
7
|
+
class Nesting < Base
|
8
|
+
NEST_REGEXP = / in | on | of | from | inside | inside of | from inside | off | out | out of /.freeze
|
9
|
+
|
10
|
+
def subprocessor
|
11
|
+
Strict
|
12
|
+
end
|
13
|
+
|
14
|
+
def scan
|
15
|
+
return Result.unmatched(selection, token, self.class) unless token =~ NEST_REGEXP
|
16
|
+
|
17
|
+
denest selection, token
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def denest objects, token
|
23
|
+
near = objects
|
24
|
+
far = objects
|
25
|
+
parts = token.split(NEST_REGEXP)
|
26
|
+
until parts.empty?
|
27
|
+
current = parts.pop
|
28
|
+
last_result = subprocessor.scan(near, current)
|
29
|
+
last_result = subprocessor.scan(far, current) if last_result.matched.empty? && near != far
|
30
|
+
return Result.unmatched(selection, token, self.class) if last_result.matched.empty? || last_result.matched.length > 1
|
31
|
+
|
32
|
+
near = last_result.matched.first.children & objects
|
33
|
+
far = last_result.matched.first.flatten & objects
|
34
|
+
end
|
35
|
+
last_result
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gamefic
|
4
|
+
module Scanner
|
5
|
+
# The result of an attempt to scan objects against a token in a Scanner. It
|
6
|
+
# provides an array of matching objects, the text that matched them, and the
|
7
|
+
# text that remains unmatched.
|
8
|
+
#
|
9
|
+
class Result
|
10
|
+
# The scanned objects
|
11
|
+
#
|
12
|
+
# @return [Array<Entity>, String, Regexp]
|
13
|
+
attr_reader :scanned
|
14
|
+
|
15
|
+
# The scanned token
|
16
|
+
#
|
17
|
+
# @return [String]
|
18
|
+
attr_reader :token
|
19
|
+
|
20
|
+
# The matched objects
|
21
|
+
#
|
22
|
+
# @return [Array<Entity>, String]
|
23
|
+
attr_reader :matched
|
24
|
+
alias match matched
|
25
|
+
|
26
|
+
# The remaining (unmatched) portion of the token
|
27
|
+
#
|
28
|
+
# @return [String]
|
29
|
+
attr_reader :remainder
|
30
|
+
|
31
|
+
attr_reader :processor
|
32
|
+
|
33
|
+
def initialize scanned, token, matched, remainder, processor
|
34
|
+
@scanned = scanned
|
35
|
+
@token = token
|
36
|
+
@matched = matched
|
37
|
+
@remainder = remainder
|
38
|
+
@processor = processor
|
39
|
+
end
|
40
|
+
|
41
|
+
def strictness
|
42
|
+
@strictness ||= Scanner.strictness(processor)
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.unmatched scanned, token, processor
|
46
|
+
new(scanned, token, [], token, processor)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gamefic
|
4
|
+
module Scanner
|
5
|
+
# Strict token matching.
|
6
|
+
#
|
7
|
+
# An entity will only match a word in a strict scan if the entire word
|
8
|
+
# matches one of the entity's keywords.
|
9
|
+
#
|
10
|
+
class Strict < Base
|
11
|
+
# @return [Result]
|
12
|
+
def scan
|
13
|
+
words = token.keywords
|
14
|
+
available = selection.clone
|
15
|
+
filtered = []
|
16
|
+
words.each_with_index do |word, idx|
|
17
|
+
tested = match_word(available, word)
|
18
|
+
return matched_result(filtered, words[idx..].join(' ')) if tested.empty?
|
19
|
+
|
20
|
+
filtered = tested
|
21
|
+
available = filtered
|
22
|
+
end
|
23
|
+
matched_result filtered, ''
|
24
|
+
end
|
25
|
+
|
26
|
+
def match_word available, word
|
27
|
+
available.select { |obj| obj.keywords.include?(word) }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|