diff --git a/.travis.yml b/.travis.yml index bd2e1eb..be08722 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ rvm: - 2.7 script: bundle exec rake env: - - RAILS_VERSION=6.0.3.1 + - RAILS_VERSION=6.0.3.5 - RAILS_VERSION=5.2.0 services: - mysql diff --git a/Gemfile b/Gemfile index 5e12aeb..152273a 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,7 @@ source "https://rubygems.org" gemspec -DEFAULT_RAILS_VERSION = '6.0.3' +DEFAULT_RAILS_VERSION = '6.0.3.5' ENV['RAILS_VERSION'] ||= DEFAULT_RAILS_VERSION if ENV['RAILS_VERSION'] == '4.2.10' diff --git a/README.md b/README.md index 9cb788f..15352ba 100644 --- a/README.md +++ b/README.md @@ -37,9 +37,13 @@ rails generate github:ds:active_record rails db:migrate ``` -If you need to change the name of the table used for storing the key-values, you can configure your table name as such, before running the migration: +If you need to change the name of the table used for storing the key-values, you can set your table name on the command line. Don't forget to configure your table name in the library, too: ``` +rails generate github:ds:active_record --table-name new_key_values_table +``` + +```ruby GitHub::KV.configure do |config| config.table_name = "new_key_values_table" end @@ -94,6 +98,18 @@ pp kv.mdel(["foo", "bar"]) # nil ``` +Due to MySQL's default collation, `GitHub::KV` keys are case-insensitive. If you require case sensitivity, you can set that when generating the migration and configuring the library: + +``` +rails generate github:ds:active_record --case-sensitive +``` + +```ruby +GitHub::KV.configure do |config| + config.case_sensitive = true +end +``` + ### GitHub::SQL ```ruby diff --git a/lib/generators/github/ds/active_record_generator.rb b/lib/generators/github/ds/active_record_generator.rb index 45e86e7..5530733 100644 --- a/lib/generators/github/ds/active_record_generator.rb +++ b/lib/generators/github/ds/active_record_generator.rb @@ -4,13 +4,16 @@ module Github module Ds module Generators class ActiveRecordGenerator < ::Rails::Generators::Base + class_option :table_name, type: :string, default: ::GitHub::KV.config.table_name + class_option :case_sensitive, type: :boolean, default: false + include ::Rails::Generators::Migration desc "Generates migration for KV table" source_paths << File.join(File.dirname(__FILE__), "templates") def create_migration_file - migration_template "migration.rb", "db/migrate/create_key_values_table.rb", migration_version: migration_version + migration_template "migration.rb", "db/migrate/create_#{table_name}_table.rb", migration_version: migration_version end def self.next_migration_number(dirname) @@ -23,16 +26,20 @@ def self.migration_version end end + def migration_name + "create_#{table_name}_table".camelize + end + def migration_version self.class.migration_version end - def self.table_name - ":#{GitHub::KV.config.table_name}" + def case_sensitive? + @options["case_sensitive"] end def table_name - self.class.table_name + @options["table_name"] end end end diff --git a/lib/generators/github/ds/templates/migration.rb b/lib/generators/github/ds/templates/migration.rb index 079e95c..01944d8 100644 --- a/lib/generators/github/ds/templates/migration.rb +++ b/lib/generators/github/ds/templates/migration.rb @@ -1,19 +1,23 @@ -class CreateKeyValuesTable < ActiveRecord::Migration<%= migration_version %> +class <%= migration_name %> < ActiveRecord::Migration<%= migration_version %> def self.up - create_table <%= table_name %> do |t| + create_table :<%= table_name %> do |t| t.string :key, :null => false t.binary :value, :null => false t.datetime :expires_at, :null => true t.timestamps :null => false end - add_index <%= table_name %>, :key, :unique => true - add_index <%= table_name %>, :expires_at + add_index :<%= table_name %>, :key, :unique => true + add_index :<%= table_name %>, :expires_at - change_column <%= table_name %>, :id, "bigint(20) NOT NULL AUTO_INCREMENT" + change_column :<%= table_name %>, :id, "bigint(20) NOT NULL AUTO_INCREMENT" + <%- if case_sensitive? %> + execute "ALTER TABLE <%= table_name %> CHARACTER SET utf8 COLLATE utf8_bin" + change_column :<%= table_name %>, :key, :string, :null => false, :collate => "utf8_bin" + <%- end %> end def self.down - drop_table <%= table_name %> + drop_table :<%= table_name %> end end diff --git a/lib/github/ds/version.rb b/lib/github/ds/version.rb index 32aa81f..ea78770 100644 --- a/lib/github/ds/version.rb +++ b/lib/github/ds/version.rb @@ -1,5 +1,5 @@ module GitHub module DS - VERSION = "0.5.0" + VERSION = "0.6.0" end end diff --git a/lib/github/kv.rb b/lib/github/kv.rb index 9777a9a..2d98030 100644 --- a/lib/github/kv.rb +++ b/lib/github/kv.rb @@ -86,6 +86,7 @@ def initialize(config: GitHub::KV.config, &conn_block) @use_local_time = config.use_local_time @table_name = config.table_name @conn_block = conn_block + @case_sensitive = config.case_sensitive? end def connection @@ -129,7 +130,12 @@ def mget(keys) SELECT `key`, value FROM #{@table_name} WHERE `key` IN :keys AND (`expires_at` IS NULL OR `expires_at` > :now) SQL - keys.map { |key| kvs[key] } + if @case_sensitive + keys.map { |key| kvs[key] } + else + kvs.keys.each { |key| kvs[key.downcase] = kvs[key] } + keys.map { |key| kvs[key.downcase] } + end } end diff --git a/lib/github/kv/config.rb b/lib/github/kv/config.rb index d61ff04..bb743d0 100644 --- a/lib/github/kv/config.rb +++ b/lib/github/kv/config.rb @@ -2,11 +2,18 @@ module GitHub class KV class Config attr_accessor :table_name, :encapsulated_errors, :use_local_time + attr_writer :case_sensitive def initialize @table_name = 'key_values' @encapsulated_errors = [SystemCallError] @use_local_time = false + @case_sensitive = false + yield self if block_given? + end + + def case_sensitive? + @case_sensitive end end end diff --git a/test/generators/github/store/active_record_generator_test.rb b/test/generators/github/store/active_record_generator_test.rb index cd72630..a211b28 100644 --- a/test/generators/github/store/active_record_generator_test.rb +++ b/test/generators/github/store/active_record_generator_test.rb @@ -28,16 +28,28 @@ def self.up t.timestamps :null => false end - add_index :#{table_name}, :key, :unique => true - add_index :#{table_name}, :expires_at + add_index :#{table_name}, :key, :unique => true + add_index :#{table_name}, :expires_at + + change_column :#{table_name}, :id, "bigint(20) NOT NULL AUTO_INCREMENT" - change_column :#{table_name}, :id, "bigint(20) NOT NULL AUTO_INCREMENT" end def self.down - drop_table :#{table_name} + drop_table :#{table_name} end end EOM end + + def test_generate_with_arguments + run_generator %w(--table-name kv_test --case-sensitive) + + assert_migration "db/migrate/create_kvtest_table.rb" do |migration| + assert_match "class CreateKvTest <", migration + assert_match "create_table :kvtest do", migration + assert_match %r(ALTER TABLE kvtest.*COLLATE utf8_bin), migration + assert_match %r(change_column.*:collate => "utf8_bin"), migration + end + end end diff --git a/test/github/kv_test.rb b/test/github/kv_test.rb index 2be27ad..d42b191 100644 --- a/test/github/kv_test.rb +++ b/test/github/kv_test.rb @@ -3,7 +3,13 @@ class GitHub::KVTest < Minitest::Test def setup ActiveRecord::Base.connection.execute("TRUNCATE `key_values`") + ActiveRecord::Base.connection.execute("TRUNCATE `kv_case_sensitive`") @kv = GitHub::KV.new { ActiveRecord::Base.connection } + case_sensitive = GitHub::KV::Config.new do |cfg| + cfg.case_sensitive = true + cfg.table_name = "kv_case_sensitive" + end + @kv_case_sensitive = GitHub::KV.new(config: case_sensitive) { ActiveRecord::Base.connection } GitHub::KV.reset end @@ -61,6 +67,28 @@ def test_mget_and_mset assert_equal ["2", "1"], @kv.mget(["b", "a"]).value! end + def test_get_and_set_case_insensitive + assert_nil @kv.get("foo").value! + + @kv.set "foo", "lowercase" + assert_equal "lowercase", @kv.get("foo").value! + assert_equal "lowercase", @kv.get("FOO").value! + + @kv.set "FOO", "uppercase" + assert_equal "uppercase", @kv.get("foo").value! + assert_equal "uppercase", @kv.get("FOO").value! + end + + def test_get_and_set_case_insensitive + assert_nil @kv_case_sensitive.get("foo").value! + assert_nil @kv_case_sensitive.get("FOO").value! + + @kv_case_sensitive.set "foo", "lowercase" + @kv_case_sensitive.set "FOO", "uppercase" + assert_equal "lowercase", @kv_case_sensitive.get("foo").value! + assert_equal "uppercase", @kv_case_sensitive.get("FOO").value! + end + def test_get_failure ActiveRecord::Base.connection.stubs(:select_all).raises(Errno::ECONNRESET) diff --git a/test/github/sql_test.rb b/test/github/sql_test.rb index 12e72a7..2d0479e 100644 --- a/test/github/sql_test.rb +++ b/test/github/sql_test.rb @@ -11,8 +11,8 @@ class GitHub::SQLTest < Minitest::Test [DateTime.now.utc, "'1970-01-01 00:00:00'"], [Time.now.utc, "'1970-01-01 00:00:00'"], [Time.now.utc.to_date, "'1970-01-01'"], - [true, "1", {"5.2.0" => "TRUE", "6.0.3.1" => "TRUE"}], - [false, "0", {"5.2.0" => "FALSE", "6.0.3.1" => "FALSE"}], + [true, "1", {"5.2.0" => "TRUE", "6.0.3.5" => "TRUE"}], + [false, "0", {"5.2.0" => "FALSE", "6.0.3.5" => "FALSE"}], [17, "17"], [1.7, "1.7"], ["corge", "'corge'"], diff --git a/test/test_helper.rb b/test/test_helper.rb index fae2033..fbb04a5 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -31,6 +31,7 @@ begin ActiveRecord::Base.establish_connection :with_database ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS `key_values`") + ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS `kv_case_sensitive`") # remove db tree if present so we can start fresh db_path = root_path.join("db") @@ -40,11 +41,15 @@ require "rails/generators" require "generators/github/ds/active_record_generator" Rails::Generators.invoke "github:ds:active_record" + Rails::Generators.invoke "github:ds:active_record", %w(--table-name kv_case_sensitive --case-sensitive) # require migration and run it so we have the key values table - require db_path.join("migrate").children.first.to_s + db_path.join("migrate").children.each do |migration| + require migration + end ActiveRecord::Migration.verbose = false CreateKeyValuesTable.up + CreateKvCaseSensitiveTable.up rescue raise if attempts >= 1 ActiveRecord::Base.establish_connection :without_database