Строка «истина» и «ложь» в логическое значение

85

У меня есть приложение Rails, и я использую jQuery для запроса своего представления поиска в фоновом режиме. Есть поля q(поисковый запрос) start_date,, end_dateи internal. internalПоле флажок и я использую is(:checked)метод для создания URL , который опрашивается:

$.getScript(document.URL + "?q=" + $("#search_q").val() + "&start_date=" + $("#search_start_date").val() + "&end_date=" + $("#search_end_date").val() + "&internal=" + $("#search_internal").is(':checked'));

Теперь моя проблема заключается в том, params[:internal]что есть строка, содержащая либо «истина», либо «ложь», и мне нужно преобразовать ее в логическое значение. Конечно, я могу сделать это так:

def to_boolean(str)
     return true if str=="true"
     return false if str=="false"
     return nil
end

Но я думаю, что должен быть более рубиновый способ справиться с этой проблемой! Разве нет ...?

Дэвидб
источник

Ответы:

133

Насколько я знаю , там нет встроенного в способе литья строк в булевы, но если ваши строки состоят только из 'true'и 'false'вы могли бы сократить свой метод к следующему:

def to_boolean(str)
  str == 'true'
end
cvshepherd
источник
8
просто небольшая модификация str == 'true' || str = '1'
AMTourky
30
возможно str.downcase == 'true' для полноты
JEMaddux
@AMTourky не должно быть str == 'true' || str == '1' с двумя "=="?
Паскаль
@Lowryder Да! при использовании флажка по умолчанию.
7urkm3n
49

ActiveRecord предоставляет простой способ сделать это.

def is_true?(string)
  ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(string)
end

ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES имеет все очевидные представления истинных значений в виде строк.

Сатья Каллури
источник
16
Еще проще, просто используйте ActiveRecord::ConnectionAdapters::Column.value_to_boolean(string)(источник) apidock.com/rails/v3.0.9/ActiveRecord/ConnectionAdapters/Column/…
Майк Атлас
Да, в последних версиях!
Сатья Каллури
6
ActiveRecord::Type::Boolean.new.type_cast_from_user("true")=> true ActiveRecord::Type::Boolean.new.type_cast_from_user("T")=> true
AlexChaffee
Список ложных значений был перемещен в ActiveModel :: Type :: Boolean в Rails 5
divByZero
ActiveModel::Type::Booleanкажется гораздо более подходящим путем - хотя он и ActiveRecord::ConnectionAdapters::Column::TRUE_VALUESсодержит «правдивые» значения, можно утверждать, что это случайный случай, и что значения, которые следует считать правдивыми для этого конкретного варианта использования, но не могут быть включены в другие. С другой стороны, ActiveModel::Type::Booleanочевидно, что он предназначен для общего использования.
Lyndsy Simon
24

Уведомление о безопасности

Обратите внимание, что этот ответ в чистом виде подходит только для другого варианта использования, указанного ниже, а не для указанного в вопросе. Хотя в основном это исправлено, существует множество уязвимостей безопасности, связанных с YAML, которые были вызваны загрузкой пользовательского ввода как YAML.


Уловка, которую я использую для преобразования строк в bools YAML.load, например:

YAML.load(var) # -> true/false if it's one of the below

YAML bool принимает довольно много правдивых / ложных строк:

y|Y|yes|Yes|YES|n|N|no|No|NO
|true|True|TRUE|false|False|FALSE
|on|On|ON|off|Off|OFF

Другой вариант использования

Предположим, у вас есть такой фрагмент кода конфигурации:

config.etc.something = ENV['ETC_SOMETHING']

И в командной строке:

$ export ETC_SOMETHING=false

Теперь, поскольку ENVvars являются строками внутри кода, config.etc.somethingзначением будет строка, "false"и она будет неправильно оцениваться true. Но если вам это нравится:

config.etc.something = YAML.load(ENV['ETC_SOMETHING'])

все было бы хорошо. Это также совместимо с загрузкой конфигураций из файлов .yml.

Халил Озгюр
источник
1
Это хорошо, если переданная строка находится под вашим контролем. В этом случае предоставленные значения поступают из браузера пользователя, и поэтому их следует считать небезопасными. YAML позволяет сериализовать / десериализовать любой объект Ruby, и это потенциально опасно. Произошло множество инцидентов: google.com/webhp?q=rails+yaml+vulnerability
Teoulas,
1
@Teoulas, я полностью с тобой согласен. Фактически, я добавляю уведомление, чтобы люди не использовали это небезопасным образом.
Халил Озгюр
16

Нет встроенного способа справиться с этим (хотя в пакете действий может быть помощник для этого). Я бы посоветовал что-то подобное

def to_boolean(s)
  s and !!s.match(/^(true|t|yes|y|1)$/i)
end

# or (as Pavling pointed out)

def to_boolean(s)
  !!(s =~ /^(true|t|yes|y|1)$/i)
end

Что также работает, так это использовать 0 и не-0 вместо литералов false / true:

def to_boolean(s)
  !s.to_i.zero?
end
Марсель Джекверт
источник
3
вам не нужна защита «s and ...», если вы используете «!! (s = ~ / regex_here /)», потому что «nil = ~ / something /» возвращает nil.
Павлинг
Да действительно. Я добавил его, но сохранил и старую, так как думаю, что .matchее немного легче читать.
Марсель Джекверт
7

ActiveRecord::Type::Boolean.new.type_cast_from_userделает это в соответствии с внутренними сопоставлениями Rails ConnectionAdapters::Column::TRUE_VALUESи ConnectionAdapters::Column::FALSE_VALUES:

[3] pry(main)> ActiveRecord::Type::Boolean.new.type_cast_from_user("true")
=> true
[4] pry(main)> ActiveRecord::Type::Boolean.new.type_cast_from_user("false")
=> false
[5] pry(main)> ActiveRecord::Type::Boolean.new.type_cast_from_user("T")
=> true
[6] pry(main)> ActiveRecord::Type::Boolean.new.type_cast_from_user("F")
=> false
[7] pry(main)> ActiveRecord::Type::Boolean.new.type_cast_from_user("yes")
DEPRECATION WARNING: You attempted to assign a value which is not explicitly `true` or `false` ("yes") to a boolean column. Currently this value casts to `false`. This will change to match Ruby's semantics, and will cast to `true` in Rails 5. If you would like to maintain the current behavior, you should explicitly handle the values you would like cast to `false`. (called from <main> at (pry):7)
=> false
[8] pry(main)> ActiveRecord::Type::Boolean.new.type_cast_from_user("no")
DEPRECATION WARNING: You attempted to assign a value which is not explicitly `true` or `false` ("no") to a boolean column. Currently this value casts to `false`. This will change to match Ruby's semantics, and will cast to `true` in Rails 5. If you would like to maintain the current behavior, you should explicitly handle the values you would like cast to `false`. (called from <main> at (pry):8)
=> false

Таким образом, вы можете создать свой собственный метод to_b(или to_boolили to_boolean) в инициализаторе, например:

class String
  def to_b
    ActiveRecord::Type::Boolean.new.type_cast_from_user(self)
  end
end
AlexChaffee
источник
2
Это ActiveRecord :: Type :: Boolean.new.cast (value) в Rails 5 (см. CWitty ниже)
Дэйв Берт,
6

В Rails 5 вы можете использовать ActiveRecord::Type::Boolean.new.cast(value)его для преобразования в логическое значение.

CWitty
источник
1
имейте в виду, ActiveRecord :: Type :: Boolean.new.cast ("42") возвращает истину
divByZero
3

Я не думаю, что в Ruby встроено что-либо подобное. Вы можете повторно открыть класс String и добавить туда метод to_bool:

class String
    def to_bool
        return true if self=="true"
        return false if self=="false"
        return nil
    end
end

Затем вы можете использовать его в любом месте вашего проекта, например: params[:internal].to_bool

socha23
источник
2
Я определенно не хотел бы, чтобы to_boolфункция возвращалась nil; это кажется неправильным. Другие функции преобразования этого не делают: "a".to_iвозвращает 0, а не возвращаетnil
Krease 08
3

Возможно str.to_s.downcase == 'true'для полноты. Тогда ничего не может вылететь, даже если strэто ноль или 0.

user1839842
источник
2

Глядя на исходный код Virtus , я, возможно, сделал бы что-то вроде этого:

def to_boolean(s)
  map = Hash[%w[true yes 1].product([true]) + %w[false no 0].product([false])]
  map[s.to_s.downcase]
end
d11wtq
источник
1

Вы можете рассмотреть возможность добавления internalк своему URL-адресу только в том случае, если он истинен, тогда, если флажок не установлен, и вы не добавляете его, он params[:internal]будет иметь nilзначение false в Ruby.

Я не так хорошо знаком с конкретным jQuery, который вы используете, но есть ли более чистый способ вызвать то, что вы хотите, чем создание строки URL-адреса вручную? Вы видели $getи $ajax?

Рассел
источник
1

Вы можете добавить к классу String метод to_boolean. Тогда вы можете сделать 'true'. To_boolean или '1'.to_boolean

class String
  def to_boolean
    self == 'true' || self == '1'
  end
end
Джош Кэвин
источник
-5

Я удивлен, что никто не опубликовал это простое решение. Это если ваши строки будут "истинными" или "ложными".

def to_boolean(str)
    eval(str)
end
повес
источник
4
Это потому, что это решение - Desaster безопасности. : D
davidb
1
Проблема с этим решением заключается в вводе пользователем - если кто-то напечатает to_boolean("ActiveRecord::Base.connection.execute('DROP TABLE *')"), он уничтожит вашу базу данных (и вернет истину!). Удачи: D
Бен Обин
Хорошие моменты. Я не думал о контексте. Я думал о наименьшем количестве персонажей для реализации. :)
povess 02
Простое исправление указанной уязвимости: bool = nil; bool = eval(str) if ["true", "false"].include?(str)просто подумал, что стоит добавить для пояснений.
Фернандо Кордейро,