fluent-plugin-droonga 0.9.9 → 1.0.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.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/.dir-locals.el +3 -0
  3. data/.travis.yml +6 -2
  4. data/README.md +6 -7
  5. data/Rakefile +23 -6
  6. data/fluent-plugin-droonga.gemspec +2 -2
  7. data/lib/droonga/adapter.rb +12 -3
  8. data/lib/droonga/adapter_runner.rb +28 -23
  9. data/lib/droonga/catalog/base.rb +7 -111
  10. data/lib/droonga/catalog/dataset.rb +13 -25
  11. data/lib/droonga/catalog/errors.rb +94 -0
  12. data/lib/droonga/catalog/schema.rb +277 -0
  13. data/lib/droonga/catalog/version1.rb +404 -0
  14. data/lib/droonga/catalog/version2.rb +160 -0
  15. data/lib/droonga/catalog_loader.rb +27 -4
  16. data/lib/droonga/catalog_observer.rb +44 -6
  17. data/lib/droonga/collector.rb +12 -10
  18. data/lib/droonga/{handler_plugin.rb → collector_message.rb} +47 -20
  19. data/lib/droonga/collector_runner.rb +64 -0
  20. data/lib/droonga/collectors.rb +18 -0
  21. data/lib/droonga/{catalog.rb → collectors/add.rb} +9 -7
  22. data/lib/droonga/{command_repository.rb → collectors/and.rb} +7 -14
  23. data/lib/droonga/collectors/sum.rb +26 -0
  24. data/lib/droonga/dispatcher.rb +74 -41
  25. data/lib/droonga/distributed_command_planner.rb +2 -2
  26. data/lib/droonga/engine.rb +13 -5
  27. data/lib/droonga/{message_processing_error.rb → error.rb} +33 -12
  28. data/lib/droonga/{plugin/planner/search.rb → error_messages.rb} +12 -10
  29. data/lib/droonga/farm.rb +15 -14
  30. data/lib/droonga/fluent_message_sender.rb +15 -11
  31. data/lib/droonga/forwarder.rb +22 -18
  32. data/lib/droonga/handler.rb +8 -2
  33. data/lib/droonga/handler_runner.rb +47 -26
  34. data/lib/droonga/input_message.rb +6 -6
  35. data/lib/droonga/{command.rb → loggable.rb} +7 -14
  36. data/lib/droonga/logger.rb +56 -15
  37. data/lib/droonga/message_matcher.rb +12 -7
  38. data/lib/droonga/message_pusher.rb +8 -4
  39. data/lib/droonga/message_receiver.rb +11 -9
  40. data/lib/droonga/output_message.rb +2 -0
  41. data/lib/droonga/planner.rb +21 -10
  42. data/lib/droonga/plugin.rb +15 -0
  43. data/lib/droonga/plugin/metadata/{adapter_message.rb → adapter_input_message.rb} +6 -14
  44. data/lib/droonga/plugin/metadata/adapter_output_message.rb +39 -0
  45. data/lib/droonga/plugin/metadata/collector_message.rb +39 -0
  46. data/lib/droonga/plugin/metadata/input_message.rb +15 -0
  47. data/lib/droonga/plugin_loader.rb +33 -25
  48. data/lib/droonga/plugin_registry.rb +9 -1
  49. data/lib/droonga/plugins/basic.rb +54 -0
  50. data/lib/droonga/plugins/crud.rb +36 -15
  51. data/lib/droonga/plugins/error.rb +5 -4
  52. data/lib/droonga/plugins/groonga.rb +9 -6
  53. data/lib/droonga/plugins/groonga/column_create.rb +10 -5
  54. data/lib/droonga/plugins/groonga/generic_command.rb +2 -8
  55. data/lib/droonga/plugins/groonga/generic_response.rb +2 -2
  56. data/lib/droonga/plugins/groonga/select.rb +2 -2
  57. data/lib/droonga/plugins/groonga/table_create.rb +9 -4
  58. data/lib/droonga/plugins/groonga/table_remove.rb +10 -5
  59. data/lib/droonga/plugins/search.rb +106 -5
  60. data/lib/droonga/plugins/search/distributed_search_planner.rb +398 -0
  61. data/lib/droonga/plugins/watch.rb +41 -20
  62. data/lib/droonga/processor.rb +12 -9
  63. data/lib/droonga/{plugin/collector/basic.rb → reducer.rb} +36 -50
  64. data/lib/droonga/replier.rb +7 -4
  65. data/lib/droonga/searcher.rb +40 -37
  66. data/lib/droonga/server.rb +8 -6
  67. data/lib/droonga/session.rb +17 -7
  68. data/lib/droonga/single_step.rb +53 -0
  69. data/lib/droonga/{plugin/planner/watch.rb → single_step_definition.rb} +27 -26
  70. data/lib/droonga/{partition.rb → slice.rb} +23 -12
  71. data/lib/droonga/status_code.rb +25 -0
  72. data/lib/droonga/step_runner.rb +63 -0
  73. data/lib/droonga/watch_schema.rb +7 -3
  74. data/lib/droonga/watcher.rb +4 -4
  75. data/lib/droonga/worker.rb +6 -6
  76. data/lib/fluent/plugin/out_droonga.rb +27 -2
  77. data/sample/cluster/catalog.json +33 -32
  78. data/test/command/config/default/catalog.json +72 -45
  79. data/test/command/config/version1/catalog.json +68 -0
  80. data/test/command/config/version1/fluentd.conf +11 -0
  81. data/test/command/suite/message/error/missing-dataset.expected +1 -1
  82. data/test/command/suite/message/error/unknown-dataset.expected +1 -1
  83. data/test/command/suite/message/error/unknown-type.expected +13 -0
  84. data/test/command/suite/message/error/{unknown-command.test → unknown-type.test} +1 -1
  85. data/test/command/suite/search/error/missing-source-parameter.expected +1 -1
  86. data/test/command/suite/search/error/unknown-source.expected +15 -3
  87. data/test/command/suite/watch/subscribe.expected +1 -3
  88. data/test/command/suite/watch/unsubscribe.expected +1 -3
  89. data/test/performance/watch/catalog.json +1 -0
  90. data/test/unit/catalog/test_dataset.rb +16 -358
  91. data/test/unit/catalog/test_schema.rb +285 -0
  92. data/test/unit/catalog/test_version1.rb +222 -28
  93. data/test/unit/catalog/test_version2.rb +155 -0
  94. data/test/unit/fixtures/catalog/version2.json +62 -0
  95. data/test/unit/helper/distributed_search_planner_helper.rb +2 -2
  96. data/test/unit/plugins/crud/test_add.rb +13 -13
  97. data/test/unit/plugins/groonga/test_column_create.rb +14 -11
  98. data/test/unit/plugins/groonga/test_table_create.rb +4 -9
  99. data/test/unit/plugins/groonga/test_table_remove.rb +4 -9
  100. data/test/unit/{plugin/planner/search_planner → plugins/search/planner}/test_basic.rb +0 -0
  101. data/test/unit/{plugin/planner/search_planner → plugins/search/planner}/test_group_by.rb +0 -0
  102. data/test/unit/{plugin/planner/search_planner → plugins/search/planner}/test_output.rb +0 -0
  103. data/test/unit/{plugin/planner/search_planner → plugins/search/planner}/test_sort_by.rb +0 -0
  104. data/test/unit/{plugin/collector/test_search.rb → plugins/search/test_collector.rb} +40 -39
  105. data/test/unit/plugins/{test_search.rb → search/test_handler.rb} +6 -5
  106. data/test/unit/{plugin/planner/test_search.rb → plugins/search/test_planner.rb} +3 -3
  107. data/test/unit/{plugin/collector → plugins}/test_basic.rb +68 -50
  108. data/test/unit/plugins/test_groonga.rb +2 -15
  109. data/test/unit/plugins/test_watch.rb +25 -22
  110. data/test/unit/test_message_matcher.rb +29 -6
  111. data/test/unit/test_output.rb +4 -0
  112. metadata +58 -50
  113. data/lib/droonga/collector_plugin.rb +0 -50
  114. data/lib/droonga/legacy_pluggable.rb +0 -66
  115. data/lib/droonga/legacy_plugin.rb +0 -57
  116. data/lib/droonga/legacy_plugin_repository.rb +0 -54
  117. data/lib/droonga/planner_plugin.rb +0 -54
  118. data/lib/droonga/plugin/collector/search.rb +0 -98
  119. data/lib/droonga/plugin/planner/crud.rb +0 -49
  120. data/lib/droonga/plugin/planner/distributed_search_planner.rb +0 -393
  121. data/lib/droonga/plugin/planner/groonga.rb +0 -54
  122. data/lib/droonga/plugin_registerable.rb +0 -75
  123. data/test/command/suite/message/error/unknown-command.expected +0 -13
  124. data/test/unit/test_command_repository.rb +0 -39
  125. data/test/unit/test_legacy_plugin.rb +0 -50
  126. data/test/unit/test_legacy_plugin_repository.rb +0 -89
@@ -0,0 +1,398 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2013-2014 Droonga Project
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License version 2.1 as published by the Free Software Foundation.
8
+ #
9
+ # This library is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this library; if not, write to the Free Software
16
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
+
18
+ require "droonga/searcher"
19
+ require "droonga/distributed_command_planner"
20
+
21
+ module Droonga
22
+ module Plugins
23
+ module Search
24
+ class DistributedSearchPlanner < DistributedCommandPlanner
25
+ def initialize(search_request_message)
26
+ super
27
+
28
+ @request = @source_message["body"]
29
+ raise NoQuery.new unless @request
30
+
31
+ @request = Marshal.load(Marshal.dump(@request))
32
+ @queries = @request["queries"]
33
+ end
34
+
35
+ def plan
36
+ raise Searcher::NoQuery.new if @queries.nil? or @queries.empty?
37
+
38
+ Searcher::QuerySorter.validate_dependencies(@queries)
39
+
40
+ ensure_unifiable!
41
+
42
+ @queries.each do |input_name, query|
43
+ transform_query(input_name, query)
44
+ end
45
+
46
+ @dataset = @source_message["dataset"] || @request["dataset"]
47
+ broadcast(:body => @request)
48
+
49
+ super
50
+ end
51
+
52
+ private
53
+ UNLIMITED = -1
54
+
55
+ def reduce_command
56
+ "search_reduce"
57
+ end
58
+
59
+ def gather_command
60
+ "search_gather"
61
+ end
62
+
63
+ def ensure_unifiable!
64
+ @queries.each do |name, query|
65
+ if unifiable?(name) and query["output"]
66
+ query["output"]["unifiable"] = true
67
+ end
68
+ end
69
+ end
70
+
71
+ def unifiable?(name)
72
+ query = @queries[name]
73
+ return true if query["groupBy"]
74
+ name = query["source"]
75
+ return false unless @queries.keys.include?(name)
76
+ unifiable?(name)
77
+ end
78
+
79
+ def transform_query(input_name, query)
80
+ output = query["output"]
81
+
82
+ # Skip reducing phase for a result with no output.
83
+ if output.nil? or
84
+ output["elements"].nil? or
85
+ (!output["elements"].include?("count") and
86
+ !output["elements"].include?("records"))
87
+ return
88
+ end
89
+
90
+ transformer = QueryTransformer.new(query)
91
+
92
+ elements = transformer.mappers
93
+ mapper = {}
94
+ mapper["elements"] = elements unless elements.empty?
95
+ reduce(input_name => { :reduce => transformer.reducers,
96
+ :gather => mapper })
97
+ end
98
+
99
+ class QueryTransformer
100
+ attr_reader :reducers, :mappers
101
+
102
+ def initialize(query)
103
+ @query = query
104
+ @output = @query["output"]
105
+ @reducers = {}
106
+ @mappers = {}
107
+ @output_records = true
108
+ transform!
109
+ end
110
+
111
+ def transform!
112
+ # The collector module supports only "simple" format search results.
113
+ # So we have to override the format and restore it on the gathering
114
+ # phase.
115
+ @records_format = @output["format"] || "simple"
116
+ if @output["format"] and @output["format"] != "simple"
117
+ @output["format"] = "simple"
118
+ end
119
+
120
+ @sort_keys = @query["sortBy"] || []
121
+ @sort_keys = @sort_keys["keys"] || [] if @sort_keys.is_a?(Hash)
122
+
123
+ calculate_offset_and_limit!
124
+ build_count_mapper_and_reducer!
125
+ build_records_mapper_and_reducer!
126
+ end
127
+
128
+ def calculate_offset_and_limit!
129
+ @original_sort_offset = sort_offset
130
+ @original_output_offset = output_offset
131
+ @original_sort_limit = sort_limit
132
+ @original_output_limit = output_limit
133
+
134
+ calculate_sort_offset!
135
+ calculate_output_offset!
136
+
137
+ # We have to calculate limit based on offset.
138
+ # <A, B = limited integer (0...MAXINT)>
139
+ # | sort limit | output limit | => | worker's sort limit | worker's output limit | final limit |
140
+ # ============================= ====================================================================
141
+ # | UNLIMITED | UNLIMITED | => | UNLIMITED | UNLIMITED | UNLIMITED |
142
+ # | UNLIMITED | B | => | final_offset + B | final_offset + B | B |
143
+ # | A | UNLIMITED | => | final_offset + A | final_offset + A | A |
144
+ # | A | B | => | final_offset + max(A, B) | final_offset + min(A, B)| min(A, B) |
145
+
146
+ # XXX final_limit and final_offset calculated in many times
147
+
148
+ @records_offset = final_offset
149
+ @records_limit = final_limit
150
+
151
+ updated_sort_limit = nil
152
+ updated_output_limit = nil
153
+ if final_limit == UNLIMITED
154
+ updated_output_limit = UNLIMITED
155
+ else
156
+ if rich_sort?
157
+ updated_sort_limit = final_offset + [sort_limit, output_limit].max
158
+ end
159
+ updated_output_limit = final_offset + final_limit
160
+ end
161
+
162
+ if updated_sort_limit and updated_sort_limit != @query["sortBy"]["limit"]
163
+ @query["sortBy"]["limit"] = updated_sort_limit
164
+ end
165
+ if updated_output_limit and @output["limit"] and updated_output_limit != @output["limit"]
166
+ @output["limit"] = updated_output_limit
167
+ end
168
+ end
169
+
170
+ def calculate_sort_offset!
171
+ # Offset for workers must be zero, because we have to apply "limit" and
172
+ # "offset" on the last gathering phase instead of each reducing phase.
173
+ if rich_sort?
174
+ @query["sortBy"]["offset"] = 0
175
+ end
176
+ end
177
+
178
+ def sort_offset
179
+ if rich_sort?
180
+ @query["sortBy"]["offset"] || 0
181
+ else
182
+ 0
183
+ end
184
+ end
185
+
186
+ def output_offset
187
+ @output["offset"] || 0
188
+ end
189
+
190
+ def sort_limit
191
+ if rich_sort?
192
+ @query["sortBy"]["limit"] || UNLIMITED
193
+ else
194
+ UNLIMITED
195
+ end
196
+ end
197
+
198
+ def output_limit
199
+ @output["limit"] || 0
200
+ end
201
+
202
+ def calculate_output_offset!
203
+ @output["offset"] = 0 if have_records? and @output["offset"]
204
+ end
205
+
206
+ def final_offset
207
+ @original_sort_offset + @original_output_offset
208
+ end
209
+
210
+ def final_limit
211
+ if @original_sort_limit == UNLIMITED and
212
+ @original_output_limit == UNLIMITED
213
+ UNLIMITED
214
+ else
215
+ if @original_sort_limit == UNLIMITED
216
+ @original_output_limit
217
+ elsif @original_output_limit == UNLIMITED
218
+ @original_sort_limit
219
+ else
220
+ [@original_sort_limit, @original_output_limit].min
221
+ end
222
+ end
223
+ end
224
+
225
+ def have_records?
226
+ @output["elements"].include?("records")
227
+ end
228
+
229
+ def rich_sort?
230
+ @query["sortBy"].is_a?(Hash)
231
+ end
232
+
233
+ def unifiable?
234
+ @output["unifiable"]
235
+ end
236
+
237
+ def build_count_mapper_and_reducer!
238
+ return unless @output["elements"].include?("count")
239
+
240
+ @reducers["count"] = {
241
+ "type" => "sum",
242
+ }
243
+ if unifiable?
244
+ @query["sortBy"]["limit"] = -1 if @query["sortBy"].is_a?(Hash)
245
+ @output["limit"] = -1
246
+ mapper = {
247
+ "target" => "records",
248
+ }
249
+ unless @output["elements"].include?("records")
250
+ @records_limit = -1
251
+ @output["elements"] << "records"
252
+ @output["attributes"] ||= ["_key"]
253
+ @output_records = false
254
+ end
255
+ @mappers["count"] = mapper
256
+ end
257
+ end
258
+
259
+ def build_records_mapper_and_reducer!
260
+ # Skip reducing phase for a result with no record output.
261
+ return if !@output["elements"].include?("records") || @records_limit.zero?
262
+
263
+ # Append sort key attributes to the list of output attributes
264
+ # temporarily, for the reducing phase. After all extra columns
265
+ # are removed on the gathering phase.
266
+ final_attributes = output_attribute_names
267
+ update_output_attributes!
268
+
269
+ @reducers["records"] = build_records_reducer
270
+
271
+ mapper = {}
272
+ if @output_records
273
+ mapper["format"] = @records_format unless @records_format == "simple"
274
+ mapper["attributes"] = final_attributes unless final_attributes.empty?
275
+ mapper["offset"] = @records_offset unless @records_offset.zero?
276
+ mapper["limit"] = @records_limit unless @records_limit.zero?
277
+ else
278
+ mapper["no_output"] = true
279
+ end
280
+ @mappers["records"] = mapper
281
+ end
282
+
283
+ def output_attribute_names
284
+ attributes = @output["attributes"] || []
285
+ if attributes.is_a?(Hash)
286
+ attributes.keys
287
+ else
288
+ attributes.collect do |attribute|
289
+ if attribute.is_a?(Hash)
290
+ attribute["label"] || attribute["source"]
291
+ else
292
+ attribute
293
+ end
294
+ end
295
+ end
296
+ end
297
+
298
+ def update_output_attributes!
299
+ @output["attributes"] = array_style_attributes
300
+ @output["attributes"] += sort_attribute_names
301
+ if unifiable? and !source_column_names.include?("_key")
302
+ @output["attributes"] << "_key"
303
+ end
304
+ end
305
+
306
+ def array_style_attributes
307
+ attributes = @output["attributes"] || []
308
+ if attributes.is_a?(Hash)
309
+ attributes.keys.collect do |key|
310
+ attribute = attributes[key]
311
+ case attribute
312
+ when String
313
+ {
314
+ "label" => key,
315
+ "source" => attribute,
316
+ }
317
+ when Hash
318
+ attribute["label"] = key
319
+ attribute
320
+ end
321
+ end
322
+ else
323
+ attributes
324
+ end
325
+ end
326
+
327
+ def source_column_names
328
+ attributes = @output["attributes"] || []
329
+ if attributes.is_a?(Hash)
330
+ attributes_hash = attributes
331
+ attributes = []
332
+ attributes_hash.each do |key, attribute|
333
+ attributes << attribute["source"] || key
334
+ end
335
+ attributes
336
+ else
337
+ attributes.collect do |attribute|
338
+ if attribute.is_a?(Hash)
339
+ attribute["source"] || attribute["label"]
340
+ else
341
+ attribute
342
+ end
343
+ end
344
+ end
345
+ end
346
+
347
+ def sort_attribute_names
348
+ sort_attributes = @sort_keys.collect do |key|
349
+ key = key[1..-1] if key[0] == "-"
350
+ key
351
+ end
352
+ attributes = source_column_names
353
+ sort_attributes.reject! do |attribute|
354
+ attributes.include?(attribute)
355
+ end
356
+ sort_attributes
357
+ end
358
+
359
+ ASCENDING_OPERATOR = "<"
360
+ DESCENDING_OPERATOR = ">"
361
+
362
+ def build_records_reducer
363
+ attributes = source_column_names
364
+ key_column_index = attributes.index("_key")
365
+
366
+ operators = @sort_keys.collect do |sort_key|
367
+ operator = ASCENDING_OPERATOR
368
+ if sort_key[0] == "-"
369
+ operator = DESCENDING_OPERATOR
370
+ sort_key = sort_key[1..-1]
371
+ end
372
+ {
373
+ "operator" => operator,
374
+ "column" => attributes.index(sort_key),
375
+ }
376
+ end
377
+
378
+ reducer = {
379
+ "type" => "sort",
380
+ "operators" => operators,
381
+ }
382
+ if unifiable? and !key_column_index.nil?
383
+ reducer["key_column"] = key_column_index
384
+ end
385
+
386
+ # On the reducing phase, we apply only "limit". We cannot apply
387
+ # "offset" on this phase because the collector merges a pair of
388
+ # results step by step even if there are three or more results.
389
+ # Instead, we apply "offset" on the gathering phase.
390
+ reducer["limit"] = @output["limit"]
391
+
392
+ reducer
393
+ end
394
+ end
395
+ end
396
+ end
397
+ end
398
+ end
@@ -21,7 +21,8 @@ require "droonga/watch_schema"
21
21
  module Droonga
22
22
  module Plugins
23
23
  module Watch
24
- Plugin.registry.register("watch", self)
24
+ extend Plugin
25
+ register("watch")
25
26
 
26
27
  module SchemaCreatable
27
28
  private
@@ -51,7 +52,11 @@ module Droonga
51
52
  subscriber = request["subscriber"]
52
53
  condition = request["condition"]
53
54
  route = request["route"] || message["from"]
54
- query = condition && condition.to_json
55
+ if condition
56
+ query = condition.to_json
57
+ else
58
+ query = nilondition
59
+ end
55
60
  [subscriber, condition, query, route]
56
61
  end
57
62
  end
@@ -60,14 +65,12 @@ module Droonga
60
65
  include SchemaCreatable
61
66
  include MessageParsable
62
67
 
63
- message.type = "watch.subscribe"
64
-
65
68
  def initialize(*args)
66
69
  super
67
70
  ensure_schema_created # TODO: REMOVE ME
68
71
  end
69
72
 
70
- def handle(message, messenger)
73
+ def handle(message)
71
74
  subscriber, condition, query, route = parse_message(message)
72
75
  normalized_request = {
73
76
  :subscriber => subscriber,
@@ -77,25 +80,27 @@ module Droonga
77
80
  }
78
81
  watcher = Watcher.new(@context)
79
82
  watcher.subscribe(normalized_request)
80
- outputs = {
81
- "success" => true,
82
- }
83
- messenger.emit(outputs)
83
+ true
84
84
  end
85
85
  end
86
86
 
87
+ define_single_step do |step|
88
+ step.name = "watch.subscribe"
89
+ step.write = true
90
+ step.handler = SubscribeHandler
91
+ step.collector = Collectors::And
92
+ end
93
+
87
94
  class UnsubscribeHandler < Droonga::Handler
88
95
  include SchemaCreatable
89
96
  include MessageParsable
90
97
 
91
- message.type = "watch.unsubscribe"
92
-
93
98
  def initialize(*args)
94
99
  super
95
100
  ensure_schema_created # TODO: REMOVE ME
96
101
  end
97
102
 
98
- def handle(message, messenger)
103
+ def handle(message)
99
104
  subscriber, condition, query, route = parse_message(message)
100
105
  normalized_request = {
101
106
  :subscriber => subscriber,
@@ -104,24 +109,26 @@ module Droonga
104
109
  }
105
110
  watcher = Watcher.new(@context)
106
111
  watcher.unsubscribe(normalized_request)
107
- outputs = {
108
- "success" => true,
109
- }
110
- messenger.emit(outputs)
112
+ true
111
113
  end
112
114
  end
113
115
 
116
+ define_single_step do |step|
117
+ step.name = "watch.unsubscribe"
118
+ step.write = true
119
+ step.handler = UnsubscribeHandler
120
+ step.collector = Collectors::And
121
+ end
122
+
114
123
  class FeedHandler < Droonga::Handler
115
124
  include SchemaCreatable
116
125
 
117
- message.type = "watch.feed"
118
-
119
126
  def initialize(*args)
120
127
  super
121
128
  ensure_schema_created # TODO: REMOVE ME
122
129
  end
123
130
 
124
- def handle(message, messenger)
131
+ def handle(message)
125
132
  request = message.request
126
133
  watcher = Watcher.new(@context)
127
134
  watcher.feed(:targets => request["targets"]) do |route, subscribers|
@@ -133,9 +140,16 @@ module Droonga
133
140
  messenger.forward(published_message,
134
141
  "to" => route, "type" => "watch.publish")
135
142
  end
143
+ nil
136
144
  end
137
145
  end
138
146
 
147
+ define_single_step do |step|
148
+ step.name = "watch.feed"
149
+ step.write = true
150
+ step.handler = FeedHandler
151
+ end
152
+
139
153
  class SweepHandler < Droonga::Handler
140
154
  include SchemaCreatable
141
155
 
@@ -146,11 +160,18 @@ module Droonga
146
160
  ensure_schema_created # TODO: REMOVE ME
147
161
  end
148
162
 
149
- def sweep(message, messenger)
163
+ def handle(message)
150
164
  sweeper = Sweeper.new(@context)
151
165
  sweeper.sweep_expired_subscribers
166
+ nil
152
167
  end
153
168
  end
169
+
170
+ define_single_step do |step|
171
+ step.name = "watch.sweep"
172
+ step.write = true
173
+ step.handler = SweepHandler
174
+ end
154
175
  end
155
176
  end
156
177
  end