Это может быть сделано без большого количества дополнительного кода. Секрет состоит в том, чтобы отделить шаг синтаксического анализа от этапа хранения, также как и в этом ответе .
Парсер вернет контейнер структур key / value при представлении опций от пользователя. Если параметр передается несколько раз, тогда у контейнера будет отдельная запись для каждой подачи параметра.
Вот пример, который выводит каждую опцию ввода нескольких токенов на отдельной строке:
#include
#include
#include
#include
namespace po = boost::program_options;
int main(int argc, char *argv[]) {
// Define a multi-token option.
po::options_description desc("Allowed options");
desc.add_options()
("list", po::value>()->multitoken(), "multiple values");
// Just parse the options without storing them in a map.
po::parsed_options parsed_options = po::command_line_parser(argc, argv)
.options(desc)
.run();
// Build list of multi-valued option instances. We iterate through
// each command-line option, whether it is repeated or not. We
// accumulate the values for our multi-valued option in a
// container.
std::vector> lists;
for (const po::option& o : parsed_options.options) {
if (o.string_key == "list")
lists.push_back(o.value);
}
// If we had other normal options, we would store them in a map
// here. In this demo program it isn't really necessary because
// we are only interested in our special multi-valued option.
po::variables_map vm;
po::store(parsed_options, vm);
// Print out the multi-valued option, each separate instance on its
// own line.
for (size_t i = 0; i < lists.size(); ++i) {
for (size_t j = 0; j < lists[i].size(); ++j)
std::cout << lists[i][j] << ' ';
std::cout << '\n';
}
return 0;
}
И вот пример вызова ( live at coliru ):
$ ./po --list 1 2 3 --list foo bar --list how now brown cow
1 2 3
foo bar
how now brown cow
Я бы предложил (как возможность рассмотреть среди других) использовать два отдельных поля:
Первый из них всегда сложен и, следовательно, может быть однозначно индексирован в агностической базе данных, а второе поле хранится дословно, что пользователь вводил и может иметь верхние символы.
Очевидно, что в модели пользователя необходимо настроить электронную почту на основе email_original при каждом сохранении и запретить прямое манипулирование с полем электронной почты.
Я бы упростил это ...
В вашей модели:
before_validation :downcase_email
def downcase_email
self.email = email.downcase
end
Таким образом, индекс является агностиком базы данных, а ваши электронные письма в базе данных являются строчными.
self.email = email.downcase if email
, чтобы уловить нуль, иначе Ruby выдаст исключение undefined method 'downcase' for nil:NilClass
– scarver2
15 October 2012 в 21:38
Поскольку индексы MySQL уже не учитывают регистр, я предполагаю, что вы имеете дело с PostgreSQL, который по умолчанию создает индексы с учетом регистра. Я отвечаю здесь на основе Rails 3.2.3 и PostgreSQL 8.4.
Кажется, функциональные индексы - еще один пример того, что ActiveRecord не может генерировать. Внешние ключи и столбцы UUID - еще два, которые приходят на ум. Таким образом, нет выбора (кроме использования ActiveRecord для обезьян), но для использования execute
операторов.
Это означает, что для точного дампа вашей базы данных вам нужно отказаться от схемы агностиков DB. rb в пользу структуры DB-specific.sql. См. Руководство по Rails по переходам, раздел 6.2 Типы дампов схемы . Это устанавливается следующим образом:
config / application.rb
config.active_record.schema_format = :sql
db / structure.sql следует обновлять автоматически при выполнении миграции. Вы можете сгенерировать его вручную с помощью этой команды:
rake db:structure:dump
Файл является чистым Postgres SQL. Несмотря на то, что вы не указали, когда используете rake -T
для списка задач рейка, кажется, что вы можете использовать эту команду для загрузки базы данных из файла struct.sql:
rake db:structure:load
Здесь нет ничего волшебного: [ исходный код (показано здесь из Rails 3.2.16) просто вызывает psql на struct.sql.
Наконец, вот моя миграция, чтобы удалить старое, чувствительное к регистру ограничение по электронной почте и добавить функциональный индекс, чувствительный к регистру:
class FixEmailUniqueIndexOnUsers < ActiveRecord::Migration
def up
remove_index :users, :email
execute "CREATE UNIQUE INDEX index_users_on_lowercase_email
ON users USING btree (lower(email));"
end
def down
execute "DROP INDEX index_users_on_lowercase_email;"
add_index :users, :email, :unique => true
end
end
btree
- почему вы указали его явно?
– Richard Michael
8 December 2012 в 16:11
Я думаю, что вам нужны имена столбцов, как показано ниже
add_index "users", [email], {:name => "index_users_on_lower_email_index", :unique => true }
И вы должны создать поле электронной почты в базе данных с надлежащей сортировкой нечувствительных к регистру, таким образом ваш индекс будет также нечувствительным к регистру.
в зависимости от используемого вами синтаксиса db, который вы используете, может отличаться, но
alter table [users] alter column [email] varchar(250) collate utf8_general_ci ...
, и когда вы добавляете индекс в этот столбец, регистр будет нечувствительным к регистру.
Документация неясно, как это сделать, но этот источник выглядит следующим образом:
def add_index(table_name, column_name, options = {})
index_name, index_type, index_columns = add_index_options(table_name, column_name, options)
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})"
end
Итак, если ваша [quote_column_name
в вашей базе данных является реализацией по умолчанию (которая ничего не делает), тогда это может сработать:
add_index "users", ['lower(email)'], :name => "index_users_on_lower_email_index", :unique => true
Вы замечаете, что вы пробовали этот, но он не работал (добавив, что к вашему вопросу может быть хорошей идеей). Похоже, ActiveRecord просто не понимает индексы по вычисленному значению. Я могу подумать об уродливом взломе, который будет выполнен, но он уродлив:
email_lc
. before_validation
или before_save
email_lc
. Это довольно уродливо, и вы можете почувствовать грязный для этого, но это лучшее, что я могу придумать прямо сейчас.
Если вы используете PostgreSQL, вы можете изменить свой тип столбца на citext
- строку без учета регистра. Он также делает поиск независимым от регистра.
def change
enable_extension :citext
change_column :users, :email, :citext
add_index :users, :email, unique: true
end
text
, а не string
(в Postgres, character varying
), что может иметь последствия производительности и хранения для массивных таблиц пользователей. (?)
– aec
23 January 2016 в 01:18
Для Rails 4.2 создайте уникальный индекс без учета регистра в таблице пользователей в столбце имени.
Создайте новый файл миграции с пустым методом изменения:
$ rails generate migration add_index_in_users_on_name
Добавьте вызов add_index метод для пустого метода изменения:
add_index :users, 'lower(name)', name: 'index_users_on_lower_name', unique: true
Run Rake db: migrate task:
$ rake db:migrate
В результате индекс будет добавлен правильно и файл db / schema.rb будет содержать правильные add_index:
add_index "users", ["LOWER(\"NAME\")"], name: "index_users_on_lower_name", unique: true
Это проверено только с RDB Oracle.
Считаете ли вы использование schema_plus
( https://github.com/lomba/schema_plus )? Среди прочего (поддержка принудительного использования внешних ключей в базе данных и для представлений), он поддерживает установку индексов без учета регистра для баз данных PostgreSQL и обрабатывает их сброс в схеме. Из Readme: «Если вы используете Postgresql, SchemaPlus обеспечивает поддержку условий, выражений, индексных методов и индексов без учета регистра».