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.