[Groonga-commit] droonga/fluent-plugin-droonga at 18b2cb2 [master] Migrate Handler to new plugin style

Back to archive index

Kouhei Sutou null+****@clear*****
Fri Feb 7 18:48:30 JST 2014


Kouhei Sutou	2014-02-07 18:48:30 +0900 (Fri, 07 Feb 2014)

  New Revision: 18b2cb23b43c61f03c697ea44839742b67b4d9f0
  https://github.com/droonga/fluent-plugin-droonga/commit/18b2cb23b43c61f03c697ea44839742b67b4d9f0

  Message:
    Migrate Handler to new plugin style
    
    Note: This is not stable API yet.
    
    Before:
    
        # lib/droonga/plugin/handler/my_handler.rb
    
        require "droonga/handler_plugin"
    
        module Droonga
          class MyHandler < Droonga::HandlerPlugin
            repository.register("my-handler", self)
    
            command :my_command
            def my_command(message, messenger)
            end
          end
        end
    
    After:
    
        # lib/droonga/plugins/my_plugin.rb
    
        require "droonga/plugin"
    
        module Droonga
          module Plugins
            module MyPlugin
              Plugin.registry.register("my-plugin", self)
    
              class Handler < Droonga::Handler
                message.type = "my-command"
    
                def handle(message, messenger)
                end
              end
            end
          end
        end

  Added files:
    lib/droonga/plugins/groonga/column_create.rb
    lib/droonga/plugins/groonga/generic_command.rb
    lib/droonga/plugins/groonga/table_create.rb
    lib/droonga/plugins/groonga/table_remove.rb
    lib/droonga/plugins/watch.rb
  Copied files:
    lib/droonga/plugin/metadata/handler_action.rb
      (from lib/droonga/plugin.rb)
    lib/droonga/plugin/metadata/input_message.rb
      (from lib/droonga/plugin.rb)
    lib/droonga/plugins/search.rb
      (from lib/droonga/plugins/crud.rb)
  Removed files:
    lib/droonga/plugin/handler/add.rb
    lib/droonga/plugin/handler/forward.rb
    lib/droonga/plugin/handler/groonga.rb
    lib/droonga/plugin/handler/groonga/column_create.rb
    lib/droonga/plugin/handler/groonga/table_create.rb
    lib/droonga/plugin/handler/groonga/table_remove.rb
    lib/droonga/plugin/handler/watch.rb
  Modified files:
    lib/droonga/catalog/base.rb
    lib/droonga/handler_runner.rb
    lib/droonga/plugin.rb
    lib/droonga/plugins/crud.rb
    lib/droonga/plugins/groonga.rb
    sample/cluster/catalog.json
    test/command/config/default/catalog.json
    test/unit/catalog/test_version1.rb
  Renamed files:
    lib/droonga/handler.rb
      (from lib/droonga/plugin/handler/search.rb)
    lib/droonga/plugins/groonga/generic_response.rb
      (from lib/droonga/plugins/groonga/generic.rb)
    test/unit/plugins/crud/test_add.rb
      (from test/unit/plugin/handler/test_add.rb)
    test/unit/plugins/groonga/test_column_create.rb
      (from test/unit/plugin/handler/groonga/test_column_create.rb)
    test/unit/plugins/groonga/test_table_create.rb
      (from test/unit/plugin/handler/groonga/test_table_create.rb)
    test/unit/plugins/groonga/test_table_remove.rb
      (from test/unit/plugin/handler/groonga/test_table_remove.rb)
    test/unit/plugins/test_groonga.rb
      (from test/unit/plugin/handler/test_groonga.rb)
    test/unit/plugins/test_search.rb
      (from test/unit/plugin/handler/test_search.rb)
    test/unit/plugins/test_watch.rb
      (from test/unit/plugin/handler/test_watch.rb)

  Modified: lib/droonga/catalog/base.rb (+2 -3)
===================================================================
--- lib/droonga/catalog/base.rb    2014-02-07 18:29:19 +0900 (abe6f34)
+++ lib/droonga/catalog/base.rb    2014-02-07 18:48:30 +0900 (1865758)
@@ -54,8 +54,7 @@ module Droonga
         results = {}
         @data["datasets"].each do |key, dataset|
           workers = dataset["workers"]
-          handler = dataset["handler"] || {}
-          plugins = handler["plugins"] || dataset["plugins"]
+          plugins = dataset["plugins"]
           dataset["ring"].each do |key, part|
             part["partitions"].each do |range, partitions|
               partitions.each do |partition|
@@ -65,7 +64,7 @@ module Droonga
                   options = {
                     :database => path,
                     :n_workers => workers,
-                    :handlers => plugins
+                    :plugins => plugins
                   }
                   results[partition] = options
                 end

  Renamed: lib/droonga/handler.rb (+21 -16) 56%
===================================================================
--- lib/droonga/plugin/handler/search.rb    2014-02-07 18:29:19 +0900 (f7468de)
+++ lib/droonga/handler.rb    2014-02-07 18:48:30 +0900 (145381d)
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013 Droonga Project
+# Copyright (C) 2014 Droonga Project
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -15,23 +13,30 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
-require "droonga/handler_plugin"
-require "droonga/searcher"
+require "droonga/pluggable"
+require "droonga/plugin/metadata/input_message"
+require "droonga/plugin/metadata/handler_action"
 
 module Droonga
-  class SearchHandler < Droonga::HandlerPlugin
-    repository.register("search", self)
+  class Handler
+    extend Pluggable
+
+    class << self
+      def message
+        Plugin::Metadata::InputMessage.new(self)
+      end
 
-    command :search
-    def search(message, messenger)
-      searcher = Droonga::Searcher.new(@context)
-      values = {}
-      request = message.request
-      raise Droonga::Searcher::NoQuery.new unless request
-      searcher.search(request["queries"]).each do |output, value|
-        values[output] = value
+      def action
+        Plugin::Metadata::HandlerAction.new(self)
       end
-      messenger.emit(values)
+    end
+
+    def initialize(name, context)
+      @name = name
+      @context = context
+    end
+
+    def handle(message, messenger)
     end
   end
 end

  Modified: lib/droonga/handler_runner.rb (+23 -18)
===================================================================
--- lib/droonga/handler_runner.rb    2014-02-07 18:29:19 +0900 (38c0a87)
+++ lib/droonga/handler_runner.rb    2014-02-07 18:48:30 +0900 (517c47c)
@@ -18,15 +18,10 @@ require "groonga"
 require "droonga/forwarder"
 require "droonga/handler_message"
 require "droonga/handler_messenger"
-require "droonga/legacy_pluggable"
-require "droonga/handler_plugin"
+require "droonga/handler"
 
 module Droonga
   class HandlerRunner
-    include LegacyPluggable
-
-    attr_reader :context, :name
-
     def initialize(loop, options={})
       @loop = loop
       @options = options
@@ -43,7 +38,6 @@ module Droonga
 
     def shutdown
       $log.trace("#{log_tag}: shutdown: start")
-      super
       @forwarder.shutdown
       if @database
         @database.close
@@ -54,20 +48,24 @@ module Droonga
     end
 
     def prefer_synchronous?(command)
-      find_plugin(command).prefer_synchronous?(command)
+      find_handler_class(command).action.synchronous?
+    end
+
+    def processable?(command)
+      not find_handler_class(command).nil?
     end
 
     def process(message)
       $log.trace("#{log_tag}: process: start")
       command = message["type"]
-      plugin = find_plugin(command)
-      if plugin.nil?
-        $log.trace("#{log_tag}: process: done: no plugin: <#{command}>")
+      handler_class = find_handler_class(command)
+      if handler_class.nil?
+        $log.trace("#{log_tag}: process: done: no handler: <#{command}>")
         return
       end
-      process_command(plugin, command, message)
+      process_command(handler_class, command, message)
       $log.trace("#{log_tag}: process: done: <#{command}>",
-                 :plugin => plugin.class)
+                 :handler => handler_class)
     end
 
     private
@@ -76,20 +74,27 @@ module Droonga
         @context = Groonga::Context.new
         @database =****@conte*****_database(@database_name)
       end
-      load_plugins(@options[:handlers] || [])
+      @handler_classes = Handler.find_sub_classes(@options[:plugins] || [])
       @forwarder = Forwarder.new(@loop)
     end
 
-    def instantiate_plugin(name)
-      HandlerPlugin.repository.instantiate(name, self)
+    def find_handler_class(command)
+      @handler_classes.find do |handler_class|
+        handler_class.message.type == command
+      end
     end
 
-    def process_command(plugin, command, raw_message)
+    def process_command(handler_class, command, raw_message)
       handler_message = HandlerMessage.new(raw_message)
       handler_message.validate
 
       messenger = HandlerMessenger.new(@forwarder, handler_message, @options)
-      plugin.process(command, handler_message, messenger)
+      handler = handler_class.new(@name, @context)
+      begin
+        handler.handle(handler_message, messenger)
+      rescue MessageProcessingError => error
+        messenger.error(error.status_code, error.response_body)
+      end
     end
 
     def log_tag

  Modified: lib/droonga/plugin.rb (+1 -0)
===================================================================
--- lib/droonga/plugin.rb    2014-02-07 18:29:19 +0900 (578522a)
+++ lib/droonga/plugin.rb    2014-02-07 18:48:30 +0900 (9f1d085)
@@ -15,6 +15,7 @@
 
 require "droonga/plugin_registry"
 require "droonga/adapter"
+require "droonga/handler"
 
 module Droonga
   module Plugin

  Deleted: lib/droonga/plugin/handler/add.rb (+0 -109) 100644
===================================================================
--- lib/droonga/plugin/handler/add.rb    2014-02-07 18:29:19 +0900 (662b9b7)
+++ /dev/null
@@ -1,109 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013 Droonga Project
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License version 2.1 as published by the Free Software Foundation.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-require "groonga"
-
-require "droonga/handler_plugin"
-require "droonga/message_processing_error"
-
-module Droonga
-  class AddHandler < Droonga::HandlerPlugin
-    repository.register("add", self)
-
-    class MissingTableParameter < BadRequest
-      def initialize
-        super("\"table\" must be specified.")
-      end
-    end
-
-    class MissingPrimaryKeyParameter < BadRequest
-      def initialize(table_name)
-        super("\"key\" must be specified. " +
-                "The table #{table_name.inspect} requires a primary key for a new record.")
-      end
-    end
-
-    class UnknownTable < NotFound
-      def initialize(table_name)
-        super("The table #{table_name.inspect} does not exist in the dataset.")
-      end
-    end
-
-    class InvalidValue < BadRequest
-      def initialize(column, value, request)
-        super("The column #{column.inspect} cannot store the value #{value.inspect}.",
-              request)
-      end
-    end
-
-    class UnknownColumn < NotFound
-      def initialize(column, table, request)
-        super("The column #{column.inspect} does not exist in the table #{table.inspect}.",
-              request)
-      end
-    end
-
-    command :add
-    def add(message, messenger)
-      succeeded = process_add(message.request)
-      outputs = {
-        "success" => succeeded,
-      }
-      messenger.emit(outputs)
-    end
-
-    private
-    def process_add(request)
-      raise MissingTableParameter.new unless request.include?("table")
-
-      table = @context[request["table"]]
-      raise UnknownTable.new(request["table"]) unless table
-
-      if table.support_key?
-        unless request.include?("key")
-          raise MissingPrimaryKeyParameter.new(request["table"])
-        end
-      end
-
-      add_record(table, request)
-      true
-    end
-
-    def add_record(table, request)
-      record = nil
-      if table.support_key?
-        record = table.add(request["key"])
-      else
-        record = table.add
-      end
-      (request["values"] || []).each do |column, value|
-        begin
-          record[column] = value
-        rescue Groonga::InvalidArgument => error
-          record.delete if record.added?
-          raise InvalidValue.new(column, value, request)
-        rescue ArgumentError => error
-          record.delete if record.added?
-          raise InvalidValue.new(column, value, request)
-        rescue Groonga::NoSuchColumn => error
-          record.delete if record.added?
-          raise UnknownColumn.new(column, request["table"], request)
-        end
-      end
-    end
-  end
-end

  Deleted: lib/droonga/plugin/handler/forward.rb (+0 -75) 100644
===================================================================
--- lib/droonga/plugin/handler/forward.rb    2014-02-07 18:29:19 +0900 (928207c)
+++ /dev/null
@@ -1,75 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013 Droonga Project
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License version 2.1 as published by the Free Software Foundation.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-=begin
-
-TODO: What is this?
-
-require "droonga/legacy_plugin"
-require "droonga/logger"
-
-module Droonga
-  class MergeHandler < Droonga::LegacyPlugin
-    Droonga::LegacyPlugin.repository.register("forward", self)
-
-    CONFIG_FILE_PATH = 'config.json'
-
-    def handlable?(command)
-      true
-    end
-
-    def handle(command, request, *arguments)
-      destination = get_destination
-      post(request,
-           "to" => destination, "type" => command, "arguments" => arguments)
-    rescue => exception
-      Logger.error("error while handling #{command}",
-                   request: request,
-                   arguments: arguments,
-                   exception: exception)
-    end
-
-    def get_destination
-      loop do
-        refresh_config
-        if @config && @config["forward"]
-          path =****@conte*****
-          destination = @config["forward"][path]
-          return destination unless destination.nil? || destination.empty?
-        end
-        sleep 5
-      end
-    end
-
-    def refresh_config
-      unless File.exists?(CONFIG_FILE_PATH)
-        @config = nil
-        return
-      end
-      mtime = File.mtime(CONFIG_FILE_PATH)
-      return if @config_mtime == mtime
-      open(CONFIG_FILE_PATH) do |file|
-        @config = JSON.parse(file.read)
-      end
-      @config_mtime = mtime
-    rescue => exception
-      Logger.error("error while refreshing config", exception: exception)
-      @config = nil
-    end
-  end
-end
-=end

  Deleted: lib/droonga/plugin/handler/groonga.rb (+0 -99) 100644
===================================================================
--- lib/droonga/plugin/handler/groonga.rb    2014-02-07 18:29:19 +0900 (959bcd0)
+++ /dev/null
@@ -1,99 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013 Droonga Project
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License version 2.1 as published by the Free Software Foundation.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-require "groonga"
-
-require "droonga/handler_plugin"
-
-module Droonga
-  class GroongaHandler < Droonga::HandlerPlugin
-    repository.register("groonga", self)
-
-    command :table_create
-    def table_create(message, messenger)
-      command = TableCreate.new(@context)
-      outputs = format_outputs(command.execute(message.request))
-      messenger.emit(outputs)
-    end
-
-    command :table_remove
-    def table_remove(message, messenger)
-      command = TableRemove.new(@context)
-      outputs = format_outputs(command.execute(message.request))
-      messenger.emit(outputs)
-    end
-
-    command :column_create
-    def column_create(message, messenger)
-      command = ColumnCreate.new(@context)
-      outputs = format_outputs(command.execute(message.request))
-      messenger.emit(outputs)
-    end
-
-    def prefer_synchronous?(command)
-      return true
-    end
-
-    private
-    def format_outputs(result)
-      {
-        "result" => result,
-      }
-    end
-
-    module Status
-      SUCCESS          = 0
-      INVALID_ARGUMENT = -22
-    end
-
-    class Command
-      class CommandError < StandardError
-        attr_reader :status, :message, :result
-
-        def initialize(params={})
-          @status = params[:status]
-          @message = params[:message]
-          @result = params[:result]
-        end
-      end
-
-      def initialize(context)
-        @context = context
-      end
-
-      def execute(request)
-        @start_time = Time.now.to_f
-        result = process_request(request)
-        [header(Status::SUCCESS), result]
-      rescue CommandError => error
-        [header(error.status, error.message), error.result]
-      end
-
-      private
-      def header(return_code, error_message="")
-        elapsed_time = Time.now.to_f - @start_time
-        header = [return_code, @start_time, elapsed_time]
-        header.push(error_message) unless error_message.empty?
-        header
-      end
-    end
-  end
-end
-
-require "droonga/plugin/handler/groonga/table_create"
-require "droonga/plugin/handler/groonga/table_remove"
-require "droonga/plugin/handler/groonga/column_create"

  Deleted: lib/droonga/plugin/handler/groonga/column_create.rb (+0 -108) 100644
===================================================================
--- lib/droonga/plugin/handler/groonga/column_create.rb    2014-02-07 18:29:19 +0900 (16c25ce)
+++ /dev/null
@@ -1,108 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013 Droonga Project
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License version 2.1 as published by the Free Software Foundation.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-require "groonga"
-require "groonga/command/column-create"
-
-module Droonga
-  class GroongaHandler
-    class ColumnCreate < Command
-      def process_request(request)
-        command_class = Groonga::Command.find("column_create")
-        @command = command_class.new("column_create", request)
-
-        table_name = @command["table"]
-        if table_name.nil? || @context[table_name].nil?
-          raise CommandError.new(:status => Status::INVALID_ARGUMENT,
-                                 :message => "table doesn't exist: <#{table_name.to_s}>",
-                                 :result => false)
-        end
-
-        if****@comma*****_index?
-          define_index
-        else
-          define_column
-        end
-      end
-
-      private
-      def define_column
-        table_name = @command["table"]
-        column_name = @command["name"]
-        column_type = @command["type"]
-
-        options = create_column_options
-        Groonga::Schema.define(:context => @context) do |schema|
-          schema.change_table(table_name) do |table|
-            table.column(column_name, column_type, options)
-          end
-        end
-        true
-      end
-
-      def create_column_options
-        options = {}
-        create_column_options_flags(options)
-        options
-      end
-
-      def create_column_options_flags(options)
-        options[:type] = :scalar
-        if****@comma*****_scalar?
-          options[:type] = :scalar
-        elsif****@comma*****_vector?
-          options[:type] = :vector
-        end
-        options
-      end
-
-      def define_index
-        table_name = @command["table"]
-        target_table = @command["type"]
-        target_columns = (@command["source"] || "").split(/\s*,\s*/)
-
-        options = create_index_options
-        Groonga::Schema.define(:context => @context) do |schema|
-          schema.change_table(table_name) do |table|
-            arguments = [target_table, *target_columns]
-            arguments << options
-            table.index(*arguments)
-          end
-        end
-        true
-      end
-
-      def create_index_options
-        options = {}
-        create_index_options_name(options)
-        create_index_options_flags(options)
-        options
-      end
-
-      def create_index_options_name(options)
-        options[:name] = @command["name"]
-      end
-
-      def create_index_options_flags(options)
-        options[:with_section] = true if****@comma*****_section?
-        options[:with_weight] = true if****@comma*****_weight?
-        options[:with_position] = true if****@comma*****_position?
-        options
-      end
-    end
-  end
-end

  Deleted: lib/droonga/plugin/handler/groonga/table_create.rb (+0 -91) 100644
===================================================================
--- lib/droonga/plugin/handler/groonga/table_create.rb    2014-02-07 18:29:19 +0900 (6b69330)
+++ /dev/null
@@ -1,91 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013 Droonga Project
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License version 2.1 as published by the Free Software Foundation.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-require "groonga"
-require "groonga/command/table-create"
-
-module Droonga
-  class GroongaHandler
-    class TableCreate < Command
-      def process_request(request)
-        command_class = Groonga::Command.find("table_create")
-        @command = command_class.new("table_create", request)
-
-        name = @command["name"]
-        unless name
-          raise CommandError.new(:status => Status::INVALID_ARGUMENT,
-                                 :message => "Should not create anonymous table",
-                                 :result => false)
-        end
-
-        options = parse_command
-        Groonga::Schema.define(:context => @context) do |schema|
-          schema.create_table(name, options)
-        end
-        true
-      end
-
-      private
-      def parse_command
-        options = {}
-        parse_flags(options)
-        parse_key_type(options)
-        parse_value_type(options)
-        parse_default_tokenizer(options)
-        parse_normalizer(options)
-        options
-      end
-
-      def parse_flags(options)
-        options[:type] = :hash
-        if****@comma*****_no_key?
-          options[:type] = :array
-        elsif****@comma*****_hash_key?
-          options[:type] = :hash
-        elsif****@comma*****_pat_key?
-          options[:type] = :patricia_trie
-        elsif****@comma*****_dat_key?
-          options[:type] = :double_array_trie
-        end
-        if****@comma*****_with_sis? and****@comma*****_pat_key?
-          options[:key_with_sis] = true
-        end
-        options
-      end
-
-      def parse_key_type(options)
-        options[:key_type] = @command["key_type"] if @command["key_type"]
-        options
-      end
-
-      def parse_value_type(options)
-        options[:value_type] = @command["value_type"] if @command["value_type"]
-        options
-      end
-
-      def parse_default_tokenizer(options)
-        options[:default_tokenizer] = @command["default_tokenizer"] if @command["default_tokenizer"]
-        options
-      end
-
-      def parse_normalizer(options)
-        options[:normalizer] = @command["normalizer"] if @command["normalizer"]
-        options
-      end
-    end
-  end
-end

  Deleted: lib/droonga/plugin/handler/groonga/table_remove.rb (+0 -42) 100644
===================================================================
--- lib/droonga/plugin/handler/groonga/table_remove.rb    2014-02-07 18:29:19 +0900 (24dd536)
+++ /dev/null
@@ -1,42 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2014 Droonga Project
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License version 2.1 as published by the Free Software Foundation.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-require "groonga"
-require "groonga/command/table-remove"
-
-module Droonga
-  class GroongaHandler
-    class TableRemove < Command
-      def process_request(request)
-        command_class = Groonga::Command.find("table_remove")
-        @command = command_class.new("table_remove", request)
-
-        name = @command["name"]
-        if name.nil? || @context[name].nil?
-          raise CommandError.new(:status => Status::INVALID_ARGUMENT,
-                                 :message => "table not found",
-                                 :result => false)
-        end
-
-        Groonga::Schema.define(:context => @context) do |schema|
-          schema.remove_table(name)
-        end
-        true
-      end
-    end
-  end
-end

  Deleted: lib/droonga/plugin/handler/watch.rb (+0 -114) 100644
===================================================================
--- lib/droonga/plugin/handler/watch.rb    2014-02-07 18:29:19 +0900 (21866ee)
+++ /dev/null
@@ -1,114 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013 Droonga Project
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License version 2.1 as published by the Free Software Foundation.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-require "droonga/watcher"
-require "droonga/sweeper"
-require "droonga/watch_schema"
-require "droonga/handler_plugin"
-
-module Droonga
-  class WatchHandler < Droonga::HandlerPlugin
-    repository.register("watch", self)
-
-    def initialize(*args)
-      super
-
-      # XXX just workaround. This must be re-written.
-      # When secondary and later processes opens the database,
-      # creation processes of tables by the first process is
-      # not finished yet. Then secondary and others tries to
-      # create tables and raises errors. To avoid such a problem,
-      # the creation processes of tables is disabled on workers.
-      if $0 !~ /\AServer/
-        ensure_schema_created
-      else
-        until @context["Keyword"]
-          sleep 0.1
-        end
-        sleep 1
-      end
-
-      @watcher = Watcher.new(@context)
-      @sweeper = Sweeper.new(@context)
-    end
-
-    command "watch.subscribe" => :subscribe
-    def subscribe(message, messenger)
-      subscriber, condition, query, route = parse_message(message)
-      normalized_request = {
-        :subscriber => subscriber,
-        :condition  => condition,
-        :query      => query,
-        :route      => route,
-      }
-      @watcher.subscribe(normalized_request)
-      outputs = {
-        "success" => true,
-      }
-      messenger.emit(outputs)
-    end
-
-    command "watch.unsubscribe" => :unsubscribe
-    def unsubscribe(message, messenger)
-      subscriber, condition, query, route = parse_message(message)
-      normalized_request = {
-        :subscriber => subscriber,
-        :condition  => condition,
-        :query      => query,
-      }
-      @watcher.unsubscribe(normalized_request)
-      outputs = {
-        "success" => true,
-      }
-      messenger.emit(outputs)
-    end
-
-    command "watch.feed" => :feed
-    def feed(message, messenger)
-      request = message.request
-      @watcher.feed(:targets => request["targets"]) do |route, subscribers|
-        published_message = {
-          "to"   => subscribers,
-          "body" => request,
-        }
-        published_message = message.raw.merge(published_message)
-        messenger.forward(published_message,
-                          "to" => route, "type" => "watch.publish")
-      end
-    end
-
-    command "watch.sweep" => :sweep
-    def sweep(message, messenger)
-      @sweeper.sweep_expired_subscribers
-    end
-
-    private
-    def parse_message(message)
-      request = message.request
-      subscriber = request["subscriber"]
-      condition = request["condition"]
-      route = request["route"] || message["from"]
-      query = condition && condition.to_json
-      [subscriber, condition, query, route]
-    end
-
-    def ensure_schema_created
-      schema = WatchSchema.new(@context)
-      schema.ensure_created
-    end
-  end
-end

  Copied: lib/droonga/plugin/metadata/handler_action.rb (+18 -6) 64%
===================================================================
--- lib/droonga/plugin.rb    2014-02-07 18:29:19 +0900 (578522a)
+++ lib/droonga/plugin/metadata/handler_action.rb    2014-02-07 18:48:30 +0900 (3cbd692)
@@ -13,14 +13,26 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
-require "droonga/plugin_registry"
-require "droonga/adapter"
-
 module Droonga
   module Plugin
-    class << self
-      def registry
-        @@registry ||= PluginRegistry.new
+    module Metadata
+      class HandlerAction
+        def initialize(handler_class)
+          @handler_class = handler_class
+        end
+
+        def synchronous?
+          configuration[:synchronous]
+        end
+
+        def synchronous=(boolean)
+          configuration[:synchronous] = boolean
+        end
+
+        private
+        def configuration
+          @handler_class.options[:action] ||= {}
+        end
       end
     end
   end

  Copied: lib/droonga/plugin/metadata/input_message.rb (+18 -6) 67%
===================================================================
--- lib/droonga/plugin.rb    2014-02-07 18:29:19 +0900 (578522a)
+++ lib/droonga/plugin/metadata/input_message.rb    2014-02-07 18:48:30 +0900 (ca18c80)
@@ -13,14 +13,26 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
-require "droonga/plugin_registry"
-require "droonga/adapter"
-
 module Droonga
   module Plugin
-    class << self
-      def registry
-        @@registry ||= PluginRegistry.new
+    module Metadata
+      class InputMessage
+        def initialize(plugin_class)
+          @plugin_class = plugin_class
+        end
+
+        def type
+          configuration[:type]
+        end
+
+        def type=(type)
+          configuration[:type] = type
+        end
+
+        private
+        def configuration
+          @plugin_class.options[:message] ||= {}
+        end
       end
     end
   end

  Modified: lib/droonga/plugins/crud.rb (+88 -0)
===================================================================
--- lib/droonga/plugins/crud.rb    2014-02-07 18:29:19 +0900 (9842787)
+++ lib/droonga/plugins/crud.rb    2014-02-07 18:48:30 +0900 (316930b)
@@ -13,7 +13,10 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
+require "groonga"
+
 require "droonga/plugin"
+require "droonga/message_processing_error"
 
 module Droonga
   module Plugins
@@ -31,6 +34,91 @@ module Droonga
           end
         end
       end
+
+      class Handler < Droonga::Handler
+        message.type = "add"
+
+        class MissingTableParameter < BadRequest
+          def initialize
+            super("\"table\" must be specified.")
+          end
+        end
+
+        class MissingPrimaryKeyParameter < BadRequest
+          def initialize(table_name)
+            super("\"key\" must be specified. " +
+                    "The table #{table_name.inspect} requires a primary key for a new record.")
+          end
+        end
+
+        class UnknownTable < NotFound
+          def initialize(table_name)
+            super("The table #{table_name.inspect} does not exist in the dataset.")
+          end
+        end
+
+        class InvalidValue < BadRequest
+          def initialize(column, value, request)
+            super("The column #{column.inspect} cannot store the value #{value.inspect}.",
+                  request)
+          end
+        end
+
+        class UnknownColumn < NotFound
+          def initialize(column, table, request)
+            super("The column #{column.inspect} does not exist in the table #{table.inspect}.",
+                  request)
+          end
+        end
+
+        def handle(message, messenger)
+          succeeded = process_add(message.request)
+          outputs = {
+            "success" => succeeded,
+          }
+          messenger.emit(outputs)
+        end
+
+        private
+        def process_add(request)
+          raise MissingTableParameter.new unless request.include?("table")
+
+          table = @context[request["table"]]
+          raise UnknownTable.new(request["table"]) unless table
+
+          if table.support_key?
+            unless request.include?("key")
+              raise MissingPrimaryKeyParameter.new(request["table"])
+            end
+          end
+
+          add_record(table, request)
+          true
+        end
+
+        def add_record(table, request)
+          record = nil
+          if table.support_key?
+            record = table.add(request["key"])
+          else
+            record = table.add
+          end
+          (request["values"] || []).each do |column, value|
+            begin
+              record[column] = value
+            rescue ::Groonga::InvalidArgument => error
+              record.delete if record.added?
+              raise InvalidValue.new(column, value, request)
+            rescue ArgumentError => error
+              record.delete if record.added?
+              raise InvalidValue.new(column, value, request)
+            rescue ::Groonga::NoSuchColumn => error
+              record.delete if record.added?
+              raise UnknownColumn.new(column, request["table"], request)
+            end
+          end
+        end
+      end
     end
   end
 end

  Modified: lib/droonga/plugins/groonga.rb (+4 -1)
===================================================================
--- lib/droonga/plugins/groonga.rb    2014-02-07 18:29:19 +0900 (4429c4b)
+++ lib/droonga/plugins/groonga.rb    2014-02-07 18:48:30 +0900 (9cd6b7e)
@@ -14,8 +14,11 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 require "droonga/plugin"
-require "droonga/plugins/groonga/generic"
+require "droonga/plugins/groonga/generic_response"
 require "droonga/plugins/groonga/select"
+require "droonga/plugins/groonga/table_create"
+require "droonga/plugins/groonga/table_remove"
+require "droonga/plugins/groonga/column_create"
 
 module Droonga
   module Plugins

  Added: lib/droonga/plugins/groonga/column_create.rb (+123 -0) 100644
===================================================================
--- /dev/null
+++ lib/droonga/plugins/groonga/column_create.rb    2014-02-07 18:48:30 +0900 (3772f1f)
@@ -0,0 +1,123 @@
+# Copyright (C) 2013-2014 Droonga Project
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+require "groonga/command/column-create"
+
+require "droonga/plugin"
+require "droonga/plugins/groonga/generic_command"
+
+module Droonga
+  module Plugins
+    module Groonga
+      module ColumnCreate
+        class Command < GenericCommand
+          def process_request(request)
+            command_class = ::Groonga::Command.find("column_create")
+            @command = command_class.new("column_create", request)
+
+            table_name = @command["table"]
+            if table_name.nil? || @context[table_name].nil?
+              message = "table doesn't exist: <#{table_name.to_s}>"
+              raise CommandError.new(:status => Status::INVALID_ARGUMENT,
+                                     :message => message,
+                                     :result => false)
+            end
+
+            if****@comma*****_index?
+              define_index
+            else
+              define_column
+            end
+          end
+
+          private
+          def define_column
+            table_name = @command["table"]
+            column_name = @command["name"]
+            column_type = @command["type"]
+
+            options = create_column_options
+            ::Groonga::Schema.define(:context => @context) do |schema|
+              schema.change_table(table_name) do |table|
+                table.column(column_name, column_type, options)
+              end
+            end
+            true
+          end
+
+          def create_column_options
+            options = {}
+            create_column_options_flags(options)
+            options
+          end
+
+          def create_column_options_flags(options)
+            options[:type] = :scalar
+            if****@comma*****_scalar?
+              options[:type] = :scalar
+            elsif****@comma*****_vector?
+              options[:type] = :vector
+            end
+            options
+          end
+
+          def define_index
+            table_name = @command["table"]
+            target_table = @command["type"]
+            target_columns = (@command["source"] || "").split(/\s*,\s*/)
+
+            options = create_index_options
+            ::Groonga::Schema.define(:context => @context) do |schema|
+              schema.change_table(table_name) do |table|
+                arguments = [target_table, *target_columns]
+                arguments << options
+                table.index(*arguments)
+              end
+            end
+            true
+          end
+
+          def create_index_options
+            options = {}
+            create_index_options_name(options)
+            create_index_options_flags(options)
+            options
+          end
+
+          def create_index_options_name(options)
+            options[:name] = @command["name"]
+          end
+
+          def create_index_options_flags(options)
+            options[:with_section] = true if****@comma*****_section?
+            options[:with_weight] = true if****@comma*****_weight?
+            options[:with_position] = true if****@comma*****_position?
+          end
+        end
+
+        class Handler < Droonga::Handler
+          message.type = "column_create"
+          action.synchronous = true
+
+          def handle(message, messenger)
+            command = Command.new(@context)
+            outputs = command.execute(message.request)
+            messenger.emit(outputs)
+          end
+        end
+      end
+    end
+  end
+end

  Added: lib/droonga/plugins/groonga/generic_command.rb (+65 -0) 100644
===================================================================
--- /dev/null
+++ lib/droonga/plugins/groonga/generic_command.rb    2014-02-07 18:48:30 +0900 (8d21581)
@@ -0,0 +1,65 @@
+# Copyright (C) 2013-2014 Droonga Project
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+require "groonga"
+
+module Droonga
+  module Plugins
+    module Groonga
+      module Status
+        SUCCESS          = 0
+        INVALID_ARGUMENT = -22
+      end
+
+      class GenericCommand
+        class CommandError < StandardError
+          attr_reader :status, :message, :result
+
+          def initialize(params={})
+            @status = params[:status]
+            @message = params[:message]
+            @result = params[:result]
+          end
+        end
+
+        def initialize(context)
+          @context = context
+        end
+
+        def execute(request)
+          @start_time = Time.now.to_f
+          result = process_request(request)
+          format(header(Status::SUCCESS), result)
+        rescue CommandError => error
+          format(header(error.status, error.message), error.result)
+        end
+
+        private
+        def header(return_code, error_message="")
+          elapsed_time = Time.now.to_f - @start_time
+          header = [return_code, @start_time, elapsed_time]
+          header.push(error_message) unless error_message.empty?
+          header
+        end
+
+        def format(header, body)
+          {
+            "result" => [header, body],
+          }
+        end
+      end
+    end
+  end
+end

  Renamed: lib/droonga/plugins/groonga/generic_response.rb (+0 -0) 100%
===================================================================

  Added: lib/droonga/plugins/groonga/table_create.rb (+106 -0) 100644
===================================================================
--- /dev/null
+++ lib/droonga/plugins/groonga/table_create.rb    2014-02-07 18:48:30 +0900 (3e2e1f0)
@@ -0,0 +1,106 @@
+# Copyright (C) 2013-2014 Droonga Project
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+require "groonga/command/table-create"
+
+require "droonga/plugin"
+require "droonga/plugins/groonga/generic_command"
+
+module Droonga
+  module Plugins
+    module Groonga
+      module TableCreate
+        class Command < GenericCommand
+          def process_request(request)
+            command_class = ::Groonga::Command.find("table_create")
+            @command = command_class.new("table_create", request)
+
+            name = @command["name"]
+            unless name
+              message = "Should not create anonymous table"
+              raise CommandError.new(:status => Status::INVALID_ARGUMENT,
+                                     :message => message,
+                                     :result => false)
+            end
+
+            options = parse_command
+            ::Groonga::Schema.define(:context => @context) do |schema|
+              schema.create_table(name, options)
+            end
+            true
+          end
+
+          private
+          def parse_command
+            options = {}
+            parse_flags(options)
+            parse_key_type(options)
+            parse_value_type(options)
+            parse_default_tokenizer(options)
+            parse_normalizer(options)
+            options
+          end
+
+          def parse_flags(options)
+            options[:type] = :hash
+            if****@comma*****_no_key?
+              options[:type] = :array
+            elsif****@comma*****_hash_key?
+              options[:type] = :hash
+            elsif****@comma*****_pat_key?
+              options[:type] = :patricia_trie
+            elsif****@comma*****_dat_key?
+              options[:type] = :double_array_trie
+            end
+            if****@comma*****_with_sis? and****@comma*****_pat_key?
+              options[:key_with_sis] = true
+            end
+          end
+
+          def parse_key_type(options)
+            return unless @command["key_type"]
+            options[:key_type] = @command["key_type"]
+          end
+
+          def parse_value_type(options)
+            return unless @command["value_type"]
+            options[:value_type] = @command["value_type"]
+          end
+
+          def parse_default_tokenizer(options)
+            return unless @command["default_tokenizer"]
+            options[:default_tokenizer] = @command["default_tokenizer"]
+          end
+
+          def parse_normalizer(options)
+            return unless @command["normalizer"]
+            options[:normalizer] = @command["normalizer"]
+          end
+        end
+
+        class Handler < Droonga::Handler
+          message.type = "table_create"
+          action.synchronous = true
+
+          def handle(message, messenger)
+            command = Command.new(@context)
+            outputs = command.execute(message.request)
+            messenger.emit(outputs)
+          end
+        end
+      end
+    end
+  end
+end

  Added: lib/droonga/plugins/groonga/table_remove.rb (+57 -0) 100644
===================================================================
--- /dev/null
+++ lib/droonga/plugins/groonga/table_remove.rb    2014-02-07 18:48:30 +0900 (b1b1815)
@@ -0,0 +1,57 @@
+# Copyright (C) 2014 Droonga Project
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+require "groonga/command/table-remove"
+
+require "droonga/plugin"
+require "droonga/plugins/groonga/generic_command"
+
+module Droonga
+  module Plugins
+    module Groonga
+      module TableRemove
+        class Command < GenericCommand
+          def process_request(request)
+            command_class = ::Groonga::Command.find("table_remove")
+            @command = command_class.new("table_remove", request)
+
+            name = @command["name"]
+            if name.nil? || @context[name].nil?
+              raise CommandError.new(:status => Status::INVALID_ARGUMENT,
+                                     :message => "table not found",
+                                     :result => false)
+            end
+
+            ::Groonga::Schema.define(:context => @context) do |schema|
+              schema.remove_table(name)
+            end
+            true
+          end
+        end
+
+        class Handler < Droonga::Handler
+          message.type = "table_remove"
+          action.synchronous = true
+
+          def handle(message, messenger)
+            command = Command.new(@context)
+            outputs = command.execute(message.request)
+            messenger.emit(outputs)
+          end
+        end
+      end
+    end
+  end
+end

  Copied: lib/droonga/plugins/search.rb (+13 -9) 61%
===================================================================
--- lib/droonga/plugins/crud.rb    2014-02-07 18:29:19 +0900 (9842787)
+++ lib/droonga/plugins/search.rb    2014-02-07 18:48:30 +0900 (84bffc1)
@@ -14,21 +14,25 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 require "droonga/plugin"
+require "droonga/searcher"
 
 module Droonga
   module Plugins
-    module CRUD
-      Plugin.registry.register("crud", self)
+    module Search
+      Plugin.registry.register("search", self)
 
-      class Adapter < Droonga::Adapter
-        message.input_pattern  = ["type", :equal, "add"]
-        message.output_pattern = ["body.success", :exist?]
+      class Handler < Droonga::Handler
+        message.type = "search"
 
-        def adapt_output(output_message)
-          success = output_message.body["success"]
-          unless success.nil?
-            output_message.body = output_message.body["success"]
+        def handle(message, messenger)
+          searcher = Droonga::Searcher.new(@context)
+          values = {}
+          request = message.request
+          raise Droonga::Searcher::NoQuery.new unless request
+          searcher.search(request["queries"]).each do |output, value|
+            values[output] = value
           end
+          messenger.emit(values)
         end
       end
     end

  Added: lib/droonga/plugins/watch.rb (+156 -0) 100644
===================================================================
--- /dev/null
+++ lib/droonga/plugins/watch.rb    2014-02-07 18:48:30 +0900 (4e8187f)
@@ -0,0 +1,156 @@
+# Copyright (C) 2013-2014 Droonga Project
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+require "droonga/plugin"
+require "droonga/watcher"
+require "droonga/sweeper"
+require "droonga/watch_schema"
+
+module Droonga
+  module Plugins
+    module Watch
+      Plugin.registry.register("watch", self)
+
+      module SchemaCreatable
+        private
+        def ensure_schema_created
+          # XXX just workaround. This must be re-written.
+          # When secondary and later processes opens the database,
+          # creation processes of tables by the first process is
+          # not finished yet. Then secondary and others tries to
+          # create tables and raises errors. To avoid such a problem,
+          # the creation processes of tables is disabled on workers.
+          if $0 !~ /\AServer/
+            schema = WatchSchema.new(@context)
+            schema.ensure_created
+          else
+            until @context["Keyword"]
+              sleep 0.1
+            end
+            sleep 1
+          end
+        end
+      end
+
+      module MessageParsable
+        private
+        def parse_message(message)
+          request = message.request
+          subscriber = request["subscriber"]
+          condition = request["condition"]
+          route = request["route"] || message["from"]
+          query = condition && condition.to_json
+          [subscriber, condition, query, route]
+        end
+      end
+
+      class SubscribeHandler < Droonga::Handler
+        include SchemaCreatable
+        include MessageParsable
+
+        message.type = "watch.subscribe"
+
+        def initialize(*args)
+          super
+          ensure_schema_created # TODO: REMOVE ME
+        end
+
+        def handle(message, messenger)
+          subscriber, condition, query, route = parse_message(message)
+          normalized_request = {
+            :subscriber => subscriber,
+            :condition  => condition,
+            :query      => query,
+            :route      => route,
+          }
+          watcher = Watcher.new(@context)
+          watcher.subscribe(normalized_request)
+          outputs = {
+            "success" => true,
+          }
+          messenger.emit(outputs)
+        end
+      end
+
+      class UnsubscribeHandler < Droonga::Handler
+        include SchemaCreatable
+        include MessageParsable
+
+        message.type = "watch.unsubscribe"
+
+        def initialize(*args)
+          super
+          ensure_schema_created # TODO: REMOVE ME
+        end
+
+        def handle(message, messenger)
+          subscriber, condition, query, route = parse_message(message)
+          normalized_request = {
+            :subscriber => subscriber,
+            :condition  => condition,
+            :query      => query,
+          }
+          watcher = Watcher.new(@context)
+          watcher.unsubscribe(normalized_request)
+          outputs = {
+            "success" => true,
+          }
+          messenger.emit(outputs)
+        end
+      end
+
+      class FeedHandler < Droonga::Handler
+        include SchemaCreatable
+
+        message.type = "watch.feed"
+
+        def initialize(*args)
+          super
+          ensure_schema_created # TODO: REMOVE ME
+        end
+
+        def handle(message, messenger)
+          request = message.request
+          watcher = Watcher.new(@context)
+          watcher.feed(:targets => request["targets"]) do |route, subscribers|
+            published_message = {
+              "to"   => subscribers,
+              "body" => request,
+            }
+            published_message = message.raw.merge(published_message)
+            messenger.forward(published_message,
+                              "to" => route, "type" => "watch.publish")
+          end
+        end
+      end
+
+      class SweepHandler < Droonga::Handler
+        include SchemaCreatable
+
+        message.type = "watch.sweep"
+
+        def initialize(*args)
+          super
+          ensure_schema_created # TODO: REMOVE ME
+        end
+
+        def sweep(message, messenger)
+          sweeper = Sweeper.new(@context)
+          sweeper.sweep_expired_subscribers
+        end
+      end
+    end
+  end
+end

  Modified: sample/cluster/catalog.json (+1 -4)
===================================================================
--- sample/cluster/catalog.json    2014-02-07 18:29:19 +0900 (902c12b)
+++ sample/cluster/catalog.json    2014-02-07 18:48:30 +0900 (6448075)
@@ -11,10 +11,7 @@
   "datasets": {
     "Droonga": {
       "workers": 2,
-      "plugins": ["crud", "groonga"],
-      "handler": {
-        "plugins": ["search", "groonga", "add"]
-      },
+      "plugins": ["groonga", "search", "crud"],
       "number_of_replicas": 2,
       "number_of_partitions": 2,
       "partition_key": "_key",

  Modified: test/command/config/default/catalog.json (+2 -7)
===================================================================
--- test/command/config/default/catalog.json    2014-02-07 18:29:19 +0900 (beeaee4)
+++ test/command/config/default/catalog.json    2014-02-07 18:48:30 +0900 (0285fc8)
@@ -10,10 +10,7 @@
   "datasets": {
     "Droonga": {
       "workers": 2,
-      "handler": {
-        "plugins": ["search", "groonga", "add"]
-      },
-      "plugins": ["groonga", "crud"],
+      "plugins": ["groonga", "crud", "search"],
       "number_of_replicas": 2,
       "number_of_partitions": 2,
       "partition_key": "_key",
@@ -41,9 +38,7 @@
     },
     "Watch": {
       "workers": 2,
-      "handler": {
-        "plugins": ["search", "groonga", "add", "watch"]
-      },
+      "plugins": ["groonga", "watch", "search", "crud"],
       "number_of_replicas": 1,
       "number_of_partitions": 1,
       "partition_key": "_key",

  Modified: test/unit/catalog/test_version1.rb (+8 -17)
===================================================================
--- test/unit/catalog/test_version1.rb    2014-02-07 18:29:19 +0900 (850f460)
+++ test/unit/catalog/test_version1.rb    2014-02-07 18:48:30 +0900 (e2465c0)
@@ -55,22 +55,22 @@ class CatalogTest < Test::Unit::TestCase
       assert_equal({
                      "localhost:23003/test.000" => {
                        :database  => "#{base_path}/000/db",
-                       :handlers  => ["for_dataset"],
+                       :plugins   => ["for_dataset"],
                        :n_workers => 0
                      },
                      "localhost:23003/test.001" => {
                        :database  => "#{base_path}/001/db",
-                       :handlers  => ["for_dataset"],
+                       :plugins   => ["for_dataset"],
                        :n_workers => 0
                      },
                      "localhost:23003/test.002" => {
                        :database  => "#{base_path}/002/db",
-                       :handlers  => ["for_dataset"],
+                       :plugins   => ["for_dataset"],
                        :n_workers => 0
                      },
                      "localhost:23003/test.003" => {
                        :database  => "#{base_path}/003/db",
-                       :handlers  => ["for_dataset"],
+                       :plugins   => ["for_dataset"],
                        :n_workers => 0
                      },
                    },
@@ -89,7 +89,7 @@ class CatalogTest < Test::Unit::TestCase
       File.dirname(catalog_path)
     end
 
-    class HandlersTest < self
+    class PluginsTest < self
       def setup
         @data = {
           "farms" => {
@@ -119,26 +119,17 @@ class CatalogTest < Test::Unit::TestCase
         "localhost:23041/droonga"
       end
 
-      def handlers(data)
+      def plugins(data)
         catalog = create_catalog(data, base_path)
         catalog.get_partitions(farm_name).collect do |partition, options|
-          options[:handlers]
+          options[:plugins]
         end
       end
 
       def test_plugins
         @data["datasets"]["Droonga"]["plugins"] = ["search", "groonga", "add"]
         assert_equal([["search", "groonga", "add"]],
-                     handlers(@data))
-
-      end
-
-      def test_handler_plugins
-        @data["datasets"]["Droonga"]["handler"] = {
-          "plugins" => ["search", "groonga", "add"],
-        }
-        assert_equal([["search", "groonga", "add"]],
-                     handlers(@data))
+                     plugins(@data))
 
       end
     end

  Renamed: test/unit/plugins/crud/test_add.rb (+11 -11) 85%
===================================================================
--- test/unit/plugin/handler/test_add.rb    2014-02-07 18:29:19 +0900 (ac6aac9)
+++ test/unit/plugins/crud/test_add.rb    2014-02-07 18:48:30 +0900 (0498baf)
@@ -1,4 +1,4 @@
-# Copyright (C) 2013 Droonga Project
+# Copyright (C) 2013-2014 Droonga Project
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -13,9 +13,9 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
-require "droonga/plugin/handler/add"
+require "droonga/plugins/crud"
 
-class AddHandlerTest < Test::Unit::TestCase
+class CRUDAddHandlerTest < Test::Unit::TestCase
   SUCCESS_RESPONSE_BODY = {
     "success" => true,
   }
@@ -37,7 +37,7 @@ class AddHandlerTest < Test::Unit::TestCase
 
   def setup_handler
     @worker = StubWorker.new
-    @handler = Droonga::AddHandler.new(@worker)
+    @handler = Droonga::Plugins::CRUD::Handler.new("name", @worker.context)
     @messenger = Droonga::Test::StubHandlerMessenger.new
   end
 
@@ -47,7 +47,7 @@ class AddHandlerTest < Test::Unit::TestCase
 
   def process(request)
     message = Droonga::Test::StubHandlerMessage.new(request)
-    @handler.add(message, @messenger)
+    @handler.handle(message, @messenger)
   end
 
   public
@@ -93,7 +93,7 @@ class AddHandlerTest < Test::Unit::TestCase
         "table"  => "Users",
         "values" => {"country" => "japan"},
       }
-      assert_raise(Droonga::AddHandler::MissingPrimaryKeyParameter) do
+      assert_raise(Droonga::Plugins::CRUD::Handler::MissingPrimaryKeyParameter) do
         process(request)
       end
     end
@@ -104,7 +104,7 @@ class AddHandlerTest < Test::Unit::TestCase
         "key"    => "mori",
         "values" => {"age" => "secret"},
       }
-      assert_raise(Droonga::AddHandler::InvalidValue) do
+      assert_raise(Droonga::Plugins::CRUD::Handler::InvalidValue) do
         process(request)
       end
     end
@@ -115,7 +115,7 @@ class AddHandlerTest < Test::Unit::TestCase
         "key"    => "mori",
         "values" => {"birthday" => "today"},
       }
-      assert_raise(Droonga::AddHandler::InvalidValue) do
+      assert_raise(Droonga::Plugins::CRUD::Handler::InvalidValue) do
         process(request)
       end
     end
@@ -126,7 +126,7 @@ class AddHandlerTest < Test::Unit::TestCase
         "key"    => "mori",
         "values" => {"unknown" => "unknown"},
       }
-      assert_raise(Droonga::AddHandler::UnknownColumn) do
+      assert_raise(Droonga::Plugins::CRUD::Handler::UnknownColumn) do
         process(request)
       end
     end
@@ -170,7 +170,7 @@ class AddHandlerTest < Test::Unit::TestCase
       request = {
         "values" => {},
       }
-      assert_raise(Droonga::AddHandler::MissingTableParameter) do
+      assert_raise(Droonga::Plugins::CRUD::Handler::MissingTableParameter) do
         process(request)
       end
     end
@@ -180,7 +180,7 @@ class AddHandlerTest < Test::Unit::TestCase
         "table"  => "Nonexistent",
         "values" => {},
       }
-      assert_raise(Droonga::AddHandler::UnknownTable) do
+      assert_raise(Droonga::Plugins::CRUD::Handler::UnknownTable) do
         process(request)
       end
     end

  Renamed: test/unit/plugins/groonga/test_column_create.rb (+20 -5) 84%
===================================================================
--- test/unit/plugin/handler/groonga/test_column_create.rb    2014-02-07 18:29:19 +0900 (e55a5ca)
+++ test/unit/plugins/groonga/test_column_create.rb    2014-02-07 18:48:30 +0900 (3bf410d)
@@ -14,8 +14,15 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 class ColumnCreateTest < GroongaHandlerTest
+  def create_handler
+    Droonga::Plugins::Groonga::ColumnCreate::Handler.new("droonga",
+                                                         @handler.context)
+  end
+
   def test_success
-    process(:table_create, {"name" => "Books"})
+    Groonga::Schema.define(:context => @context) do |schema|
+      schema.create_table("Books", :type => :hash)
+    end
     process(:column_create,
             {"table" => "Books", "name" => "title", "type" => "ShortText"})
     response =****@messe*****
@@ -40,7 +47,9 @@ class ColumnCreateTest < GroongaHandlerTest
   end
 
   def test_name
-    process(:table_create, {"name" => "Books"})
+    Groonga::Schema.define(:context => @context) do |schema|
+      schema.create_table("Books", :type => :hash)
+    end
     process(:column_create,
             {"table" => "Books", "name" => "title", "type" => "ShortText"})
     assert_equal(<<-SCHEMA, dump)
@@ -50,7 +59,9 @@ column_create Books title COLUMN_SCALAR ShortText
   end
 
   def test_type
-    process(:table_create, {"name" => "Books"})
+    Groonga::Schema.define(:context => @context) do |schema|
+      schema.create_table("Books", :type => :hash)
+    end
     process(:column_create,
             {"table" => "Books", "name" => "main_text", "type" => "LongText"})
     assert_equal(<<-SCHEMA, dump)
@@ -76,7 +87,9 @@ column_create Books main_text COLUMN_SCALAR LongText
           "type"  => "ShortText",
           "flags" => data[:flags],
         }
-        process(:table_create, {"name" => "Books"})
+        Groonga::Schema.define(:context => @context) do |schema|
+          schema.create_table("Books", :type => :hash)
+        end
         process(:column_create, request)
         assert_equal(<<-EXPECTED, dump)
 table_create Books TABLE_HASH_KEY --key_type ShortText
@@ -88,7 +101,9 @@ column_create Books title #{data[:flags]} ShortText
     class IndexTest < self
       def setup
         super
-        process(:table_create, {"name" => "Books"})
+        Groonga::Schema.define(:context => @context) do |schema|
+          schema.create_table("Books", :type => :hash)
+        end
         process(:column_create,
                 {"table" => "Books", "name" => "title", "type" => "ShortText"})
       end

  Renamed: test/unit/plugins/groonga/test_table_create.rb (+5 -0) 96%
===================================================================
--- test/unit/plugin/handler/groonga/test_table_create.rb    2014-02-07 18:29:19 +0900 (2f0820b)
+++ test/unit/plugins/groonga/test_table_create.rb    2014-02-07 18:48:30 +0900 (90a2273)
@@ -14,6 +14,11 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 class TableCreateTest < GroongaHandlerTest
+  def create_handler
+    Droonga::Plugins::Groonga::TableCreate::Handler.new("droonga",
+                                                        @handler.context)
+  end
+
   def test_success
     process(:table_create, {"name" => "Books"})
     response =****@messe*****

  Renamed: test/unit/plugins/groonga/test_table_remove.rb (+8 -1) 85%
===================================================================
--- test/unit/plugin/handler/groonga/test_table_remove.rb    2014-02-07 18:29:19 +0900 (3c3272e)
+++ test/unit/plugins/groonga/test_table_remove.rb    2014-02-07 18:48:30 +0900 (264d074)
@@ -14,9 +14,16 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 class TableRemoveTest < GroongaHandlerTest
+  def create_handler
+    Droonga::Plugins::Groonga::TableRemove::Handler.new("droonga",
+                                                        @handler.context)
+  end
+
   def setup
     super
-    process(:table_create, {"name" => "Books"})
+    Groonga::Schema.define(:context => @context) do |schema|
+      schema.create_table("Books", :type => :hash)
+    end
   end
 
   def test_success

  Renamed: test/unit/plugins/test_groonga.rb (+5 -5) 89%
===================================================================
--- test/unit/plugin/handler/test_groonga.rb    2014-02-07 18:29:19 +0900 (1abae91)
+++ test/unit/plugins/test_groonga.rb    2014-02-07 18:48:30 +0900 (972f84a)
@@ -13,7 +13,7 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
-require "droonga/plugin/handler/groonga"
+require "droonga/plugins/groonga"
 
 class GroongaHandlerTest < Test::Unit::TestCase
   include PluginHelper
@@ -31,7 +31,6 @@ class GroongaHandlerTest < Test::Unit::TestCase
   private
   def setup_plugin
     @handler = Droonga::Test::StubHandler.new
-    @plugin = Droonga::GroongaHandler.new(@handler)
     @messenger = Droonga::Test::StubHandlerMessenger.new
   end
 
@@ -45,7 +44,8 @@ class GroongaHandlerTest < Test::Unit::TestCase
 
   def process(command, request)
     message = Droonga::Test::StubHandlerMessage.new(request)
-    @plugin.send(command, message, @messenger)
+    handler = create_handler
+    handler.handle(message, @messenger)
   end
 
   NORMALIZED_START_TIME = Time.parse("2013-07-11T16:04:28+0900").to_i
@@ -57,12 +57,12 @@ class GroongaHandlerTest < Test::Unit::TestCase
   end
 
   NORMALIZED_HEADER_SUCCESS = [
-    Droonga::GroongaHandler::Status::SUCCESS,
+    Droonga::Plugins::Groonga::Status::SUCCESS,
     NORMALIZED_START_TIME,
     NORMALIZED_ELAPSED_TIME,
   ]
   NORMALIZED_HEADER_INVALID_ARGUMENT = [
-    Droonga::GroongaHandler::Status::INVALID_ARGUMENT,
+    Droonga::Plugins::Groonga::Status::INVALID_ARGUMENT,
     NORMALIZED_START_TIME,
     NORMALIZED_ELAPSED_TIME,
   ]

  Renamed: test/unit/plugins/test_search.rb (+3 -3) 99%
===================================================================
--- test/unit/plugin/handler/test_search.rb    2014-02-07 18:29:19 +0900 (abe3502)
+++ test/unit/plugins/test_search.rb    2014-02-07 18:48:30 +0900 (b70b613)
@@ -13,7 +13,7 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
-require "droonga/plugin/handler/search"
+require "droonga/plugins/search"
 
 class SearchHandlerTest < Test::Unit::TestCase
   def setup
@@ -29,7 +29,7 @@ class SearchHandlerTest < Test::Unit::TestCase
 
   def setup_plugin
     @handler = Droonga::Test::StubHandler.new
-    @plugin = Droonga::SearchHandler.new(@handler)
+    @plugin = Droonga::Plugins::Search::Handler.new("droonga", @handler.context)
     @messenger = Droonga::Test::StubHandlerMessenger.new
   end
 
@@ -41,7 +41,7 @@ class SearchHandlerTest < Test::Unit::TestCase
   private
   def search(request, headers={})
     message = Droonga::Test::StubHandlerMessage.new(request, headers)
-    @plugin.search(message, @messenger)
+    @plugin.handle(message, @messenger)
     results_to_result_set(@messenger.values.first)
   end
 

  Renamed: test/unit/plugins/test_watch.rb (+16 -3) 90%
===================================================================
--- test/unit/plugin/handler/test_watch.rb    2014-02-07 18:29:19 +0900 (0d26d2a)
+++ test/unit/plugins/test_watch.rb    2014-02-07 18:48:30 +0900 (c943521)
@@ -15,7 +15,7 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
-require "droonga/plugin/handler/watch"
+require "droonga/plugins/watch"
 
 class WatchHandlerTest < Test::Unit::TestCase
   include WatchHelper
@@ -36,7 +36,6 @@ class WatchHandlerTest < Test::Unit::TestCase
   private
   def setup_plugin
     @handler = Droonga::Test::StubHandler.new
-    @plugin = Droonga::WatchHandler.new(@handler)
     @messenger = Droonga::Test::StubHandlerMessenger.new
   end
 
@@ -46,11 +45,15 @@ class WatchHandlerTest < Test::Unit::TestCase
 
   def process(command, request, headers={})
     message = Droonga::Test::StubHandlerMessage.new(request, headers)
-    @plugin.send(command, message, @messenger)
+    create_plugin.handle(message, @messenger)
   end
 
   public
   class SubscribeTest < self
+    def create_plugin
+      Droonga::Plugins::Watch::SubscribeHandler.new("droonga", @handler.context)
+    end
+
     def test_subscribe
       request = {
         "route" => "localhost:23003/output",
@@ -111,6 +114,11 @@ class WatchHandlerTest < Test::Unit::TestCase
       setup_subscription
     end
 
+    def create_plugin
+      Droonga::Plugins::Watch::UnsubscribeHandler.new("droonga",
+                                                      @handler.context)
+    end
+
     def test_unsubscribe
       request = {
         "route" => "localhost:23003/output",
@@ -140,6 +148,11 @@ class WatchHandlerTest < Test::Unit::TestCase
       setup_subscription
     end
 
+    def create_plugin
+      Droonga::Plugins::Watch::FeedHandler.new("droonga",
+                                               @handler.context)
+    end
+
     def test_feed_match
       request = {
         "targets" => {




More information about the Groonga-commit mailing list
Back to archive index