Cadena "verdadero" y "falso" a booleano

85

Tengo una aplicación Rails y estoy usando jQuery para consultar mi vista de búsqueda en segundo plano. Hay campos q(término de búsqueda) start_date, end_datey internal. El internalcampo es una casilla de verificación y estoy usando el is(:checked)método para construir la URL que se consulta:

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

Ahora mi problema es params[:internal]porque hay una cadena que contiene "verdadero" o "falso" y necesito convertirlo en booleano. Por supuesto que puedo hacerlo así:

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

¡Pero creo que debe haber una forma más rubí de lidiar con este problema! ¿No hay ...?

Davidb
fuente

Respuestas:

133

Hasta donde yo sé, no hay una forma integrada de convertir cadenas a valores booleanos, pero si sus cadenas solo consisten en 'true'y 'false'podría acortar su método a lo siguiente:

def to_boolean(str)
  str == 'true'
end
pastor
fuente
8
solo una pequeña modificación str == 'true' || str = '1'
AMTourky
30
quizás str.downcase == 'true' para completar
JEMaddux
@AMTourky no debería ser str == 'true' || str == '1' con dos "=="?
Pascal
@Lowryder ¡Sí! si usa check_box predeterminado.
7urkm3n
49

ActiveRecord proporciona una forma limpia de hacer esto.

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

ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES tiene todas las representaciones obvias de valores verdaderos como cadenas.

Satya Kalluri
fuente
16
Aún más simple, solo use ActiveRecord::ConnectionAdapters::Column.value_to_boolean(string)(fuente) apidock.com/rails/v3.0.9/ActiveRecord/ConnectionAdapters/Column/…
Mike Atlas
¡Sí, en las últimas versiones!
Satya Kalluri
6
ActiveRecord::Type::Boolean.new.type_cast_from_user("true")=> verdadero ActiveRecord::Type::Boolean.new.type_cast_from_user("T")=> verdadero
AlexChaffee
La lista de valores falsos se ha movido a ActiveModel :: Type :: Boolean en Rails 5
divideByZero
ActiveModel::Type::Booleanparece un camino mucho más adecuado - aunque ActiveRecord::ConnectionAdapters::Column::TRUE_VALUEScontiene valores de "veracidad", se podría argumentar que es solo incidentalmente el caso, y que los valores que deberían considerarse veraces para ese caso de uso específico pero no otros podrían incluirse. Por otro lado, ActiveModel::Type::Booleanaparentemente está diseñado para ser utilizado de forma genérica.
Lyndsy Simon
24

Aviso de seguridad

Tenga en cuenta que esta respuesta en su forma básica solo es apropiada para el otro caso de uso que se enumera a continuación en lugar del de la pregunta. Si bien en su mayoría se corrigió, ha habido numerosas vulnerabilidades de seguridad relacionadas con YAML que fueron causadas por cargar la entrada del usuario como YAML.


Un truco que utilizo para convertir cadenas en bools es YAML.load, por ejemplo:

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

YAML bool acepta muchas cadenas de verdad / falsedad:

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

Otro caso de uso

Suponga que tiene un fragmento de código de configuración como este:

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

Y en la línea de comandos:

$ export ETC_SOMETHING=false

Ahora, dado que las ENVvars son cadenas una vez dentro del código, config.etc.somethingel valor de la cadena sería la cadena "false"y se evaluaría incorrectamente true. Pero si te gusta esto:

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

todo estaría bien. Esto también es compatible con la carga de configuraciones desde archivos .yml.

Halil Özgür
fuente
1
Eso es bueno si la cadena pasada está bajo su control. En el caso de esta pregunta, los valores proporcionados provienen del navegador del usuario y, como tales, deben considerarse inseguros. YAML le permite serializar / deserializar cualquier objeto Ruby y esto es potencialmente peligroso. Ha habido numerosos incidentes: google.com/webhp?q=rails+yaml+vulnerability
Teoulas
1
@Teoulas, estoy completamente de acuerdo contigo. De hecho, estoy agregando un aviso para que la gente no use esto de manera insegura.
Halil Özgür
16

No hay ninguna forma incorporada de manejar esto (aunque actionpack podría tener una ayuda para eso). Aconsejaría algo como esto

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

Lo que también funciona es usar 0 y no 0 en lugar de literales falsos / verdaderos:

def to_boolean(s)
  !s.to_i.zero?
end
Marcel Jackwerth
fuente
3
no necesita la protección "sy ..." si usa "!! (s = ~ / regex_here /)" porque "nil = ~ / anything /" devuelve nil.
Pavling
Ah, de hecho. Lo agregué, pero también conservé el antiguo, ya que creo que .matches un poco más fácil de leer.
Marcel Jackwerth
7

ActiveRecord::Type::Boolean.new.type_cast_from_userhace esto de acuerdo con las asignaciones internas de Rails ConnectionAdapters::Column::TRUE_VALUESy 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

Entonces podría crear su propio to_b( to_boolo to_boolean) método en un inicializador como este:

class String
  def to_b
    ActiveRecord::Type::Boolean.new.type_cast_from_user(self)
  end
end
AlexChaffee
fuente
2
Es ActiveRecord :: Type :: Boolean.new.cast (valor) en Rails 5 (ver CWitty a continuación)
Dave Burt
6

En Rails 5 puedes usarlo ActiveRecord::Type::Boolean.new.cast(value)para convertirlo en un booleano.

CWitty
fuente
1
Sin embargo, tenga en cuenta que ActiveRecord :: Type :: Boolean.new.cast ("42") devuelve verdadero
divideByZero
3

No creo que nada de eso esté integrado en Ruby. Puede volver a abrir la clase String y agregar el método to_bool allí:

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

Entonces puedes usarlo en cualquier lugar de tu proyecto, así: params[:internal].to_bool

socha23
fuente
2
Definitivamente no me gustaría tener una to_boolfunción de retorno nil; eso parece incorrecto. Otras funciones de conversión no hacen esto: "a".to_idevuelve 0, nonil
Krease
3

Quizás str.to_s.downcase == 'true'para completar. Entonces nada puede fallar incluso si stres nulo o 0.

usuario1839842
fuente
2

Mirando el código fuente de Virtus , tal vez haría algo como esto:

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
fuente
1

Podría considerar agregar solo internala su URL si es verdadera, luego, si la casilla de verificación no está marcada y no agrega, lo params[:internal]estaría nil, lo que se evalúa como falso en Ruby.

No estoy tan familiarizado con el jQuery específico que estás usando, pero ¿hay una forma más limpia de llamar a lo que quieras que construir manualmente una cadena de URL? ¿Has echado un vistazo a $gety $ajax?

Russell
fuente
1

Puede agregar a la clase String para tener el método to_boolean. Entonces podrías hacer 'true' .to_boolean o '1'.to_boolean

class String
  def to_boolean
    self == 'true' || self == '1'
  end
end
Josh Cavin
fuente
-5

Me sorprende que nadie haya publicado esta sencilla solución. Eso es si sus cadenas van a ser "verdaderas" o "falsas".

def to_boolean(str)
    eval(str)
end
povess
fuente
4
Eso es porque esta solución es un desastre de seguridad. : D
davidb
1
El problema con esta solución es la entrada del usuario: si alguien escribe to_boolean("ActiveRecord::Base.connection.execute('DROP TABLE *')"), destruirá su base de datos (¡y devolverá verdadero!). Diviértete: D
Ben Aubin
Buenos puntos. No estaba pensando en el contexto. Estaba pensando en la menor cantidad de caracteres para implementar. :)
povess
Una solución fácil para dicha vulnerabilidad: bool = nil; bool = eval(str) if ["true", "false"].include?(str)solo pensé que debería agregar por el bien de las aclaraciones.
Fernando Cordeiro