[Groonga-commit] ranguba/groonga-client-model at 62d97be [master] Start supporting migration

Back to archive index

Kouhei Sutou null+****@clear*****
Wed Mar 1 16:57:15 JST 2017


Kouhei Sutou	2017-03-01 16:57:15 +0900 (Wed, 01 Mar 2017)

  New Revision: 62d97bec147a686c5104a88806a30e8380a7eaa7
  https://github.com/ranguba/groonga-client-model/commit/62d97bec147a686c5104a88806a30e8380a7eaa7

  Message:
    Start supporting migration
    
    TODO:
    
      * Support recording schema version
      * Support multiple commands in a change
      * Support columns
      * Support migration file generation

  Added files:
    lib/groonga_client_model/migration.rb
    lib/groonga_client_model/migrator.rb
  Modified files:
    lib/groonga_client_model/railties/groonga.rake

  Added: lib/groonga_client_model/migration.rb (+134 -0) 100644
===================================================================
--- /dev/null
+++ lib/groonga_client_model/migration.rb    2017-03-01 16:57:15 +0900 (da02aa6)
@@ -0,0 +1,134 @@
+# Copyright (C) 2017  Kouhei Sutou <kou �� clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+module GroongaClientModel
+  class MigrationError < Error
+  end
+
+  class IrreversibleMigrationError < MigrationError
+  end
+
+  class Migration
+    def initialize(client)
+      @client = client
+      @reverting = false
+    end
+
+    def up
+      change
+    end
+
+    def down
+      revert do
+        change
+      end
+    end
+
+    def revert
+      @reverting = true
+      begin
+        yield
+      ensure
+        @reverting = false
+      end
+    end
+
+    def create_table(name, type: nil, key_type: nil)
+      return remove_table_raw(name) if @reverting
+
+      type = normalize_table_type(type || :array)
+      if type != "TABLE_NO_KEY" and key_type.nil?
+        key_type ||= "ShortText"
+      end
+      key_type = normalize_type(key_type)
+      report(__method__, [name, type: type, key_type: key_type]) do
+        @client.request(:table_create).
+          parameter(:name, name).
+          flags_parameter(:flags, [type]).
+          parameter(:key_type, key_type).
+          response
+      end
+    end
+
+    def remove_table(name)
+      if @reverting
+        raise IrreversibleMigrationError, "can't revert remove_table(#{name})"
+      end
+
+      remove_table_raw(name)
+    end
+
+    private
+    def report(method_name, arguments)
+      argument_list = arguments.collect(&:inspect).join(", ")
+      puts("-- #{method_name}(#{argument_list})")
+      time = Benchmark.measure do
+        response = yield
+      end
+      puts("   -> %.4fs" % time.real)
+    end
+
+    def normalize_table_type(type)
+      case type.to_s
+      when "array", /\A(?:TABLE_)?NO_KEY\z/i
+        "TABLE_NO_KEY"
+      when "hash", /\A(?:TABLE_)?HASH_KEY\z/i
+        "TABLE_HASH_KEY"
+      when "pat", "patricia_trie", /\A(?:TABLE_)?PAT_KEY\z/i
+        "TABLE_PAT_KEY"
+      when "dat", "double_array_trie", /\A(?:TABLE_)?DAT_KEY\z/i
+        "TABLE_DAT_KEY"
+      else
+        type
+      end
+    end
+
+    def normalize_type(type)
+      case type.to_s
+      when /\Abool(?:ean)?\z/i
+        "Bool"
+      when /\Aint(8|16|32|64)\z/i
+        "Int#{$1}"
+      when /\Auint(8|16|32|64)\z/i
+        "UInt#{$1}"
+      when /\Afloat\z/i
+        "Float"
+      when /\Atime\z/i
+        "Time"
+      when /\Ashort_?text\z/i
+        "ShortText"
+      when /\Atext\z/i
+        "Text"
+      when /\Along_?text\z/i
+        "LongText"
+      when /\Atokyo_?geo_?point\z/i
+        "TokyoGeoPoint"
+      when /\A(?:wgs84)?_?geo_?point\z/i
+        "WGS84GeoPoint"
+      else
+        type
+      end
+    end
+
+    def remove_table_raw(name)
+      report(:remove_table, [name]) do
+        @client.request(:table_remove).
+          parameter(:name, name).
+          response
+      end
+    end
+  end
+end

  Added: lib/groonga_client_model/migrator.rb (+125 -0) 100644
===================================================================
--- /dev/null
+++ lib/groonga_client_model/migrator.rb    2017-03-01 16:57:15 +0900 (be31462)
@@ -0,0 +1,125 @@
+# Copyright (C) 2017  Kouhei Sutou <kou �� clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+require "groonga_client_model/migration"
+
+module GroongaClientModel
+  class Migrator
+    def initialize(search_paths, target_version)
+      @search_paths = Array(search_paths)
+      @target_version = target_version
+    end
+
+    def migrate
+      each do |definition|
+        Client.open do |client|
+          migration = definition.create_migration(client)
+          report(definition) do
+            if forward?
+              migration.up
+            else
+              migration.down
+            end
+          end
+        end
+      end
+    end
+
+    def each(&block)
+      paths = []
+      @search_paths.each do |search_path|
+        paths |= Dir.glob("#{search_path}/**/[0-9]*_*.rb").collect do |path|
+          File.expand_path(path)
+        end
+      end
+      definitions = []
+      paths.each do |path|
+        definition = Definition.new(path)
+        definitions << definition if definition.valid?
+      end
+      sorted_definitions = definitions.sort_by(&:version)
+
+      if forward?
+        sorted_definitions.each(&block)
+      else
+        sorted_definitions.reverse_each(&block)
+      end
+    end
+
+    private
+    def forward?
+      @target_version.nil? or
+        (@target_version > current_version)
+    end
+
+    def current_version
+      0 # TODO
+    end
+
+    def report(definition)
+      version = definition.version
+      name = definition.name
+      if forward?
+        action = "forward"
+      else
+        action = "rollback"
+      end
+      mark("#{version} #{name}: #{action}")
+      time = Benchmark.measure do
+        yield
+      end
+      mark("%s %s: %.4fs" % [version, name, time.real])
+      puts
+    end
+
+    def mark(text)
+      pre = "=="
+      max_width = 79
+      post_length = [0, max_width - pre.length - 1 - text.length - 1].max
+      post = "=" * post_length
+      puts("#{pre} #{text} #{post}")
+    end
+
+    class Definition
+      attr_reader :version
+      attr_reader :name
+      def initialize(path)
+        @path = path
+        parse_path
+      end
+
+      def valid?
+        @version and @name and File.exist?(@path)
+      end
+
+      def create_migration(client)
+        require(@path)
+        @name.camelize.constantize.new(client)
+      end
+
+      private
+      def parse_path
+        if /\A([0-9]+)_([_a-z0-9]+)\.rb\z/ =~ File.basename(@path)
+          @version = $1.to_i
+          @name = $2
+        else
+          @version = nil
+          @name = nil
+        end
+      end
+    end
+  end
+end

  Modified: lib/groonga_client_model/railties/groonga.rake (+14 -2)
===================================================================
--- lib/groonga_client_model/railties/groonga.rake    2017-03-01 15:21:03 +0900 (815fa8d)
+++ lib/groonga_client_model/railties/groonga.rake    2017-03-01 16:57:15 +0900 (49c9326)
@@ -1,6 +1,6 @@
 # -*- ruby -*-
 #
-# Copyright (C) 2016  Kouhei Sutou <kou �� clear-code.com>
+# Copyright (C) 2016-2017  Kouhei Sutou <kou �� clear-code.com>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -16,6 +16,8 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
+require "groonga_client_model/migrator"
+
 namespace :groonga do
   namespace :config do
     desc "Load config/groonga.rb"
@@ -26,10 +28,20 @@ namespace :groonga do
   end
 
   namespace :schema do
-    desc "Loads db/schema.grn into Groonga"
+    desc "Loads db/schema.grn into the Groonga database"
     task load: ["config:load"] do
       schema_loader = GroongaClientModel::SchemaLoader.new(Rails.root)
       schema_loader.load
     end
   end
+
+  desc "Migrate the Groonga database"
+  task :migrate do
+    Rails.application.paths["db/groonga/migrate"] ||= "db/groonga/migrate"
+    migration_paths = Rails.application.paths["db/groonga/migrate"].to_a
+    version = nil
+    version = Integer(ENV["VERSION"]) if ENV["VERSION"]
+    migrator = GroongaClientModel::Migrator.new(migration_paths, version)
+    migrator.migrate
+  end
 end
-------------- next part --------------
HTML����������������������������...
Download 



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