Рядок "true" і "false" до логічної


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]тому, що є рядок, що містить або "true", або "false", і мені потрібно передати його в логічну форму. Звичайно, я можу зробити це так:

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

8
лише невелика модифікація str == 'true' || str = '1'
AMTourky

30
можливо str.downcase == 'true' для повноти
JEMaddux

@AMTourky не повинно бути str == 'true' || str == '1' з двома "=="?
Паскаль

@Lowryder Так! якщо використовується check_box за замовчуванням.
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/…
Mike Atlas

Так, в останніх версіях!
Сатья Каллурі

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 in Rails 5
divideByZero

ActiveModel::Type::Booleanздається набагато більш підходящим шляхом - хоча ActiveRecord::ConnectionAdapters::Column::TRUE_VALUESмістяться "неправдиві" значення, можна стверджувати, що це лише випадково, і що значення, які слід вважати неправдивими для конкретного випадку використання, але не можуть бути включені. З іншого боку, ActiveModel::Type::Booleanочевидно, розроблений для загального використання.
Ліндсі Саймон

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
Теулас,

1
@Teoulas, я повністю з вами згоден. Насправді я додаю повідомлення, щоб люди не використовували це ненадійним способом.
Halil Özgür

16

Немає жодного вбудованого способу впоратись із цим (хоча у actionpack для цього може бути помічник). Я б порадив щось подібне

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 та non-0 замість false / true літералів:

def to_boolean(s)
  !s.to_i.zero?
end

3
вам не потрібен захист "s і ...", якщо ви використовуєте "!! (s = ~ / regex_here /)", оскільки "nil = ~ / anything /" повертає 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

2
Це ActiveRecord :: Тип :: Boolean.new.cast (значення) у Rails 5 (див. CWitty нижче)
Дейв Берт,


6

У Rails 5 ви можете використовувати ActiveRecord::Type::Boolean.new.cast(value)його, щоб передати його в логічну форму.


1
пам’ятайте, ActiveRecord :: Type :: Boolean.new.cast ("42") повертає true
divideByZero

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


2
Я точно не хотів би мати to_boolфункцію return nil; це здається неправильним. Інші функції перетворення цього не роблять: "a".to_iповертається 0, ніnil
Krease


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

1

Ви можете розглянути можливість додавання internalдо вашої URL-адреси, якщо це правда, тоді, якщо прапорець не встановлений, і ви не додаєте, це params[:internal]буде nil, що в Ruby має значення false.

Я не настільки знайомий з конкретним 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 *')"), він знищить вашу базу даних (і поверне true!) Веселіться: D
Бен Обен

Хороші бали. Я не думав про контекст. Я думав про найменшу кількість символів для реалізації. :)
povess 02

Просте виправлення зазначеної вразливості: bool = nil; bool = eval(str) if ["true", "false"].include?(str)просто подумав, що я повинен додати заради уточнень.
Фернандо Кордейру
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.