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 ...?

Davidb
la source

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
cvshepherd
la source
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.

Satya Kalluri
la source
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.

Halil Özgür
la source
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
Marcel Jackwerth
la source
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
AlexChaffee
la source
2
Il s'agit de ActiveRecord :: Type :: Boolean.new.cast (valeur) dans Rails 5 (voir CWitty ci-dessous)
Dave Burt
6

Vous pouvez utiliser wannabe_bool gem. https://github.com/prodis/wannabe_bool

Ce gem implémente une #to_bméthode pour les classes String, Integer, Symbol et NilClass.

params[:internal].to_b
Prodis
la source
6

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

CWitty
la source
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

socha23
la source
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.

user1839842
la source
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
d11wtq
la source
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?

Russell
la source
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
Josh Cavin
la source
-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
pov
la source
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