Chaîne «true» et «false» en booléen


85

J'ai une application Rails et j'utilise jQuery pour interroger ma vue de recherche en arrière-plan. Il existe des champs q(terme de recherche) start_date, end_dateet internal. Le internalchamp est une case à cocher et j'utilise la is(:checked)méthode pour créer l'url qui est interrogée:

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

Maintenant, mon problème est params[:internal]parce qu'il y a une chaîne contenant "true" ou "false" et je dois la convertir en booléen. Bien sûr, je peux le faire comme ceci:

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

Mais je pense qu'il doit y avoir une manière plus rubis de traiter ce problème! N'y a-t-il pas ...?

Réponses:


133

Autant que je sache, il n'y a pas de méthode intégrée pour convertir des chaînes en booléens, mais si vos chaînes ne sont constituées que de 'true'et 'false'vous pouvez raccourcir votre méthode comme suit:

def to_boolean(str)
  str == 'true'
end

8
juste une petite modification str == 'true' || str = '1'
AMTourky

30
peut-être str.downcase == 'true' pour l'exhaustivité
JEMaddux

@AMTourky ne devrait pas être str == 'true' || str == '1' avec deux "=="?
Pascal

@Lowryder Oui! si vous utilisez la case à cocher par défaut.
7urkm3n

49

ActiveRecord fournit une manière propre de faire ceci.

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

ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES a toutes les représentations évidentes des valeurs True sous forme de chaînes.


16
Encore plus simple, il suffit d'utiliser ActiveRecord::ConnectionAdapters::Column.value_to_boolean(string)(source) apidock.com/rails/v3.0.9/ActiveRecord/ConnectionAdapters/Column
Mike Atlas

Oui, dans les dernières versions!
Satya Kalluri

6
ActiveRecord::Type::Boolean.new.type_cast_from_user("true")=> true ActiveRecord::Type::Boolean.new.type_cast_from_user("T")=> true
AlexChaffee

La liste des fausses valeurs a été déplacée vers ActiveModel :: Type :: Boolean in Rails 5
divideByZero

ActiveModel::Type::Booleansemble être un chemin beaucoup plus approprié - bien que ActiveRecord::ConnectionAdapters::Column::TRUE_VALUEScontenant des valeurs de «vérité», on pourrait soutenir que ce n'est qu'incidemment le cas, et que des valeurs qui devraient être considérées comme véridiques pour ce cas d'utilisation spécifique mais pas pour d'autres pourraient être incluses. D'autre part, ActiveModel::Type::Booleanest apparemment conçu pour être utilisé de manière générique.
Lyndsy Simon

24

Avis de sécurité

Notez que cette réponse sous sa forme simple n'est appropriée que pour l'autre cas d'utilisation répertorié ci-dessous plutôt que pour celui de la question. Bien que pour la plupart corrigées, de nombreuses vulnérabilités de sécurité liées à YAML ont été causées par le chargement de l'entrée utilisateur en tant que YAML.


Une astuce que j'utilise pour convertir des chaînes en booléens est YAML.load, par exemple:

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

YAML bool accepte beaucoup de chaînes véridiques / fausses:

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

Un autre cas d'utilisation

Supposons que vous ayez un morceau de code de configuration comme celui-ci:

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

Et en ligne de commande:

$ export ETC_SOMETHING=false

Maintenant, puisque les ENVvars sont des chaînes une fois dans le code, config.etc.somethingla valeur de s serait la chaîne "false"et elle serait incorrectement évaluée true. Mais si vous aimez ça:

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

tout irait bien. Ceci est également compatible avec le chargement de configurations à partir de fichiers .yml.


1
C'est bien si la chaîne passée est sous votre contrôle. Dans le cas de cette question, les valeurs fournies proviennent du navigateur de l'utilisateur et en tant que telles, elles doivent être considérées comme dangereuses. YAML vous permet de sérialiser / désérialiser n'importe quel objet Ruby, ce qui est potentiellement dangereux. Il y a eu de nombreux incidents: google.com/webhp?q=rails+yaml+vulnerability
Teoulas

1
@Teoulas, je suis entièrement d'accord avec vous. En fait, j'ajoute un avis pour que les gens ne l'utilisent pas de manière non sécurisée.
Halil Özgür

16

Il n'y a aucun moyen intégré de gérer cela (bien que l'actionpack puisse avoir une aide pour cela). Je conseillerais quelque chose comme ça

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

Ce qui fonctionne également, c'est d'utiliser 0 et non-0 au lieu de littéraux faux / vrai:

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

3
vous n'avez pas besoin de la garde "s et ..." si vous utilisez "!! (s = ~ / regex_here /)" car "nil = ~ / n'importe quoi /" renvoie nil.
Pavling

Ah en effet. Je l'ai ajouté mais j'ai également conservé l'ancien, car je pense que .matchc'est un peu plus facile à lire.
Marcel Jackwerth

7

ActiveRecord::Type::Boolean.new.type_cast_from_userfait cela selon les mappages internes de Rails ConnectionAdapters::Column::TRUE_VALUESet 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

Vous pouvez donc créer votre propre méthode to_b(ou to_boolou to_boolean) dans un initialiseur comme celui-ci:

class String
  def to_b
    ActiveRecord::Type::Boolean.new.type_cast_from_user(self)
  end
end

2
Il s'agit de ActiveRecord :: Type :: Boolean.new.cast (valeur) dans Rails 5 (voir CWitty ci-dessous)
Dave Burt


6

Dans Rails 5, vous pouvez utiliser ActiveRecord::Type::Boolean.new.cast(value)pour le convertir en booléen.


1
attention cependant, ActiveRecord :: Type :: Boolean.new.cast ("42") renvoie true
divideByZero

3

Je ne pense pas que quelque chose comme ça soit intégré à Ruby. Vous pouvez rouvrir la classe String et y ajouter la méthode to_bool:

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

Ensuite, vous pouvez l'utiliser n'importe où dans votre projet, comme ceci: params[:internal].to_bool


2
Je ne voudrais certainement pas avoir un to_boolretour de fonction nil; cela semble faux. Les autres fonctions de conversion ne font pas cela: "a".to_irenvoie 0, pasnil
Krease

3

Peut-être str.to_s.downcase == 'true'par souci d'exhaustivité. Alors rien ne peut planter même si strnul ou 0.


2

En regardant le code source de Virtus , je ferais peut-être quelque chose comme ceci:

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

Vous pouvez envisager d'ajouter internalà votre URL uniquement si c'est vrai, alors si la case n'est pas cochée et que vous ne l'ajoutez pas, ce params[:internal]seraitnil , ce qui donne la valeur false dans Ruby.

Je ne suis pas très familier avec le jQuery spécifique que vous utilisez, mais existe-t-il un moyen plus propre d'appeler ce que vous voulez que de créer manuellement une chaîne d'URL? Avez-vous jeté un œil à $getet $ajax?


1

Vous pouvez ajouter à la classe String pour avoir la méthode to_boolean. Ensuite, vous pouvez faire 'true'.to_boolean ou' 1'.to_boolean

class String
  def to_boolean
    self == 'true' || self == '1'
  end
end

-5

Je suis surpris que personne n'ait publié cette solution simple. C'est si vos chaînes vont être "true" ou "false".

def to_boolean(str)
    eval(str)
end

4
C'est parce que cette solution est un désastre de sécurité. : D
davidb

1
Le problème avec cette solution est l'entrée de l'utilisateur - si quelqu'un tape to_boolean("ActiveRecord::Base.connection.execute('DROP TABLE *')"), cela détruira votre base de données (et retournera vrai!). Amusez-vous bien: D
Ben Aubin

Bons points. Je ne pensais pas au contexte. Je pensais au moins de caractères à implémenter. :)
povess

Une solution facile pour ladite vulnérabilité: bool = nil; bool = eval(str) if ["true", "false"].include?(str)Je pensais juste que je devrais ajouter pour des clarifications.
Fernando Cordeiro le
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.