String “true” e “false” para booleano


86

Eu tenho um aplicativo Rails e estou usando jQuery para consultar minha visualização de pesquisa em segundo plano. Existem campos q(termo de pesquisa) start_date, end_datee internal. O internalcampo é uma caixa de seleção e estou usando o is(:checked)método para construir o url que é consultado:

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

Agora meu problema é params[:internal]porque há uma string contendo "true" ou "false" e eu preciso convertê-la em booleano. Claro que posso fazer assim:

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

Mas eu acho que deve haver uma maneira mais Ruby de lidar com esse problema! Não há ...?

Respostas:


134

Pelo que eu sei, não existe uma maneira embutida de converter strings para booleanos, mas se suas strings consistirem apenas em 'true'e 'false'você puder encurtar seu método para o seguinte:

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

8
apenas uma pequena modificação str == 'true' || str = '1'
AMTourky

30
talvez str.downcase == 'true' para integridade
JEMaddux

@AMTourky não deveria ser str == 'true' || str == '1' com dois "=="?
Pascal

@Lowryder Sim! se estiver usando a caixa de seleção padrão.
7urkm3n

49

ActiveRecord fornece uma maneira limpa de fazer isso.

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

ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES tem todas as representações óbvias de valores True como strings.


16
Ainda mais simples, basta usar ActiveRecord::ConnectionAdapters::Column.value_to_boolean(string)(fonte) apidock.com/rails/v3.0.9/ActiveRecord/ConnectionAdapters/Column/…
Mike Atlas

Sim, nas últimas versões!
Satya Kalluri

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

A lista de valores falsos foi movida para ActiveModel :: Type :: Boolean no Rails 5
divideByZero

ActiveModel::Type::Booleanparece um caminho muito mais adequado - embora ActiveRecord::ConnectionAdapters::Column::TRUE_VALUEScontenha valores "verdadeiros", pode-se argumentar que esse é o caso apenas incidentalmente e que valores que devem ser considerados verdadeiros para aquele caso de uso específico, mas não outros, podem ser incluídos. Por outro lado, ActiveModel::Type::Booleanaparentemente foi projetado para ser usado de forma genérica.
Lyndsy Simon de

24

Aviso de Segurança

Observe que esta resposta em sua forma simples é apropriada apenas para o outro caso de uso listado abaixo, e não para aquele na pergunta. Embora a maioria tenha sido corrigida, houve inúmeras vulnerabilidades de segurança relacionadas ao YAML, causadas pelo carregamento da entrada do usuário como YAML.


Um truque que uso para converter strings em bools é YAML.load, por exemplo:

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

O bool YAML aceita muitas strings verdadeiras / falsas:

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

Outro caso de uso

Suponha que você tenha um código de configuração como este:

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

E na linha de comando:

$ export ETC_SOMETHING=false

Agora, como ENVvars são strings uma vez dentro do código, config.etc.somethingo valor de seria a string "false"e seria avaliado incorretamente como true. Mas se você gosta disso:

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

tudo ficaria bem. Isso é compatível com o carregamento de configurações de arquivos .yml também.


1
Isso é bom se a string passada estiver sob seu controle. No caso desta pergunta, os valores fornecidos vêm do navegador do usuário e, como tal, devem ser considerados inseguros. YAML permite serializar / desserializar qualquer objeto Ruby e isso é potencialmente perigoso. Houve vários incidentes: google.com/webhp?q=rails+yaml+vulnerability
Teoulas

1
@Teoulas, concordo totalmente com você. Na verdade, estou adicionando um aviso para que as pessoas não usem isso de maneira insegura.
Halil Özgür

16

Não há nenhuma maneira embutida de lidar com isso (embora o actionpack possa ter um auxiliar para isso). Eu aconselharia algo assim

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

O que funciona bem é usar 0 e não-0 em vez de literais falsos / verdadeiros:

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

3
você não precisa da proteção "se ..." se usar "!! (s = ~ / regex_here /)" porque "nil = ~ / qualquer coisa /" retorna nulo.
Pavimentação de

Ah, sim. Eu adicionei, mas mantive o antigo também, pois acho que .matché um pouco mais fácil de ler.
Marcel Jackwerth

7

ActiveRecord::Type::Boolean.new.type_cast_from_userfaz isso de acordo com os mapeamentos internos do Rails ConnectionAdapters::Column::TRUE_VALUESe 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

Portanto, você pode fazer seu próprio método to_b(ou to_boolou to_boolean) em um inicializador como este:

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

2
É ActiveRecord :: Type :: Boolean.new.cast (value) no Rails 5 (veja CWitty abaixo)
Dave Burt


6

No Rails 5 você pode usar ActiveRecord::Type::Boolean.new.cast(value)para convertê-lo em um booleano.


1
esteja ciente, porém, ActiveRecord :: Type :: Boolean.new.cast ("42") retorna true
divideByZero

3

Não acho que algo assim esteja embutido no Ruby. Você pode reabrir a classe String e adicionar o método to_bool lá:

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

Então você pode usá-lo em qualquer lugar em seu projeto, como este: params[:internal].to_bool


2
Eu definitivamente não gostaria de ter um to_boolretorno de função nil; isso parece errado. Outras funções de conversão não fazem isso: "a".to_iretorna 0, nãonil
Krease

3

Talvez str.to_s.downcase == 'true'para ser completo. Então, nada pode travar, mesmo se strfor nulo ou 0.


2

Olhando para o código-fonte do Virtus , talvez eu faça algo assim:

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

Você pode considerar apenas anexar internalao seu url se for verdadeiro; então, se a caixa de seleção não estiver marcada e você não anexar, params[:internal]seria nil, o que resulta em falso em Ruby.

Não estou familiarizado com o jQuery específico que você está usando, mas existe uma maneira mais limpa de chamar o que você deseja do que construir manualmente uma string de URL? Você já deu uma olhada em $gete $ajax?


1

Você poderia adicionar à classe String para ter o método to_boolean. Então você poderia fazer 'true'.to_boolean ou' 1'.to_boolean

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

-5

Estou surpreso que ninguém postou esta solução simples. Isso se suas strings forem "verdadeiras" ou "falsas".

def to_boolean(str)
    eval(str)
end

4
Isso porque esta solução é um Desaster de segurança. : D
davidb

1
O problema com esta solução é a entrada do usuário - se alguém digitar to_boolean("ActiveRecord::Base.connection.execute('DROP TABLE *')"), isso destruirá seu banco de dados (e retornará verdadeiro!). Divirta-se: D
Ben Aubin

Bons pontos. Eu não estava pensando no contexto. Eu estava pensando na menor quantidade de caracteres para implementar. :)
povess

Uma solução fácil para essa vulnerabilidade: bool = nil; bool = eval(str) if ["true", "false"].include?(str)apenas pensei que deveria acrescentar para esclarecimentos.
Fernando Cordeiro
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.