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